Android静态ip设置的坑

这篇具有很好参考价值的文章主要介绍了Android静态ip设置的坑。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Android静态ip设置的坑

Android静态ip设置,对于这个功能,如果没有接触过,会给人感觉是个特别简单的功能,直接调用系统的接口即可,其实这个功能还是有许多坑的,因为谷歌在Android SDK中对相关的API进行非系统层的隐藏,打上了@hide标签,使得上层无法直接调用现有的接口,其中隐藏的API包含:EthernetManager,IpConfiguration,StaticIpConfiguration等接口,其中有的API虽然提供,但是关键的接口却被隐藏了。导致本来简单功能,变得无比繁琐,因此本文就对静态ip设置的坑做个总结,为相关开发者提供一个简易的参考。
解决Android静态ip设置的问题,可以从两个方面着手:

一方面有系统层接口的支持

系统接口也就是直接由系统层提供接口,一般是framework jar包上层直接调用接口传参,但是需要调整Gradle中的SDK的加载顺序,保证framework包正确替换AndroidSDK的jar包,否则编译会报错。

另一方面就是App层自己使用java的动态机制

动态机制即java的反射,通过反射调用系统隐藏的接口,但是这种方式发现在Android6以下的版本是可以的,Android6以上的版本虽然设置成功,设置里面参数也改了,但是重启你会发现,已经设置的静态模式会自动切换为动态模式,即系统不做数据存储保存,只是零时保存在内存中,所以你重启以后就被系统还原了,参考GitHub分析是高版本谷歌拦截了反射机制,不让非系统层修改数据,而且这个拦截是在C++层进行的,特别恶心,你绕都绕不过去,不过如果你的应用是Launcher应用的话,可以在App层做取巧,就是记录用户重启前是否做静态ip的保存,如果用户操作了静态ip保存,那么App启动的时候在Application的onCreate方法中下发静态ip配置,保证重启前静态ip状态和重启后的状态一致就能解决这个问题。以下就静态ip设置从系统层,App层两个方面做简要的代码实现分析:

1.系统层的实现:就是系统提供的接口直接调用,思路是先创建EthernetManager对象,接着创建StaticIpConfiguration对象,再使用StaticIpConfiguration对象创建IpConfiguration对象,最后将IpConfiguration设置给EthernetManager中即可,代码如下:
/**
 * 项目名称:demo
 * 描述
 * 静态ip的配置,通过framework.jar直调隐藏的系统接口
 */
public class FEthernetUtil {
    private static EthernetManager mEthManager;
    private static StaticIpConfiguration mStaticIpConfiguration;
    private static IpConfiguration mIpConfiguration;
    /**
     * 描述
     * @param context
     * @param iFace //网口名称
     * @param ip 
     * @param gateway
     * @param netmask
     * @param dns1
     * @param dns2
     * @return
     */
    @SuppressLint("WrongConstant")
    public static int setStaticIp(Context context, String iFace, String ip, String gateway, String netmask, String dns1, String dns2) {
	//获取EthernetManager对象
        mEthManager = (EthernetManager) context.getSystemService("ethernet");
        if (setStaticIpConfiguration(ip, gateway, netmask, dns1, dns2)) {
            mEthManager.setConfiguration(iFace, mIpConfiguration);
            return 0;
        }
        return 1;
    }
   /**

     * 描述 初始mIpConfiguration
     * @param ip 
     * @param gateway
     * @param netmask
     * @param dns1
     * @param dns2
     **/
    private static boolean setStaticIpConfiguration(String ip, String gateway, String netmask, String dns1, String dns2) {

        Inet4Address inetAddr = (Inet4Address) InetAddresses.parseNumericAddress(ip);
        int prefixLength = maskStr2InetMask(netmask);
        InetAddress gatewayAddr = InetAddresses.parseNumericAddress(gateway);
        InetAddress dnsAddr = InetAddresses.parseNumericAddress(dns1);

        if (null == inetAddr || inetAddr.getAddress().toString().isEmpty() || prefixLength == 0 || gatewayAddr.toString().isEmpty() || dnsAddr.toString().isEmpty()) {
            return false;
        }

        String dnsStr2 = dns2;
        ArrayList<InetAddress> dnsAddrs = new ArrayList<InetAddress>();
        dnsAddrs.add(dnsAddr);
        if (!dnsStr2.isEmpty()) {
            dnsAddrs.add(InetAddresses.parseNumericAddress(dns2));
        }
        mStaticIpConfiguration = new StaticIpConfiguration.Builder().setIpAddress(new LinkAddress(inetAddr, prefixLength)).setGateway(gatewayAddr).setDnsServers(dnsAddrs).build();

        mIpConfiguration = new IpConfiguration();
        mIpConfiguration.setIpAssignment(IpConfiguration.IpAssignment.STATIC);
        mIpConfiguration.setProxySettings(IpConfiguration.ProxySettings.NONE);
        mIpConfiguration.setStaticIpConfiguration(mStaticIpConfiguration);
        return true;
    }

    /**
     * 描述
     * 子网掩码解析
     * @param maskStr
     * @return int
     **/
     private static int maskStr2InetMask(String maskStr) {
        StringBuffer sb;
        String str;
        int inetmask = 0;
        int count = 0;
        /*
         * check the subMask format
         */
        Pattern pattern = Pattern.compile("(^((\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])$)|^(\\d|[1-2]\\d|3[0-2])$");
        if (pattern.matcher(maskStr).matches() == false) {
            return 0;
        }

        String[] ipSegment = maskStr.split("\\.");
        for (String s : ipSegment) {
            sb = new StringBuffer(Integer.toBinaryString(Integer.parseInt(s)));
            str = sb.reverse().toString();
            count = 0;
            for (int i = 0; i < str.length(); i++) {
                i = str.indexOf("1", i);
                if (i == -1) break;
                count++;
            }
            inetmask += count;
        }
        return inetmask;
    }
}

拿到系统层的jar包,跟使用其他jar一样进行gradle依赖配置,需要注意是gradle中的jar包加载顺序修改:文章来源地址https://www.toymoban.com/news/detail-682180.html

plugins {
    id 'com.android.library'
    id 'org.jetbrains.kotlin.android'
}
   ......
dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'
    api(files("libs/framework.jar"))//系统层配置静态ip的jar包
}

gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
        Set<File> fileSet = options.bootstrapClasspath.getFiles();
        List<File> newFileList = new ArrayList<>()
        newFileList.add(new File("libs/framework.jar"))//加载本地系统jar包
        newFileList.addAll(fileSet)
        options.bootstrapClasspath = files(newFileList.toArray())
    }
}
//调整jar包的顺序
preBuild {
    doLast {
        def imlFile = file(project.name + ".iml")
        println('Change ' + project.name + '.iml order')
        try {
            def parsedXml = (new XmlParser()).parse(imlFile)
            def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
            parsedXml.component[1].remove(jdkNode)
            def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
            new groovy.util.Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
            groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
        } catch (FileNotFoundException e) {

        }
    }
2.App层的实现:App层实现通过反射来操作,实现思路其实和系统层实现是一样的逻辑,首先需要先获取EthernetManager对象,接着创建StaticIpConfiguration对象,再根据StaticIpConfiguration对象反射创建,IpConfiguration对象,和系统层对比App层是使用反射来获取对象而已逻辑是一样的:以下是代码实现:
class EthernetUtil @Inject constructor(
    @ApplicationContext private val context: Context,
    private val ipConfigRepository: IPConfigRepository,
    private val guideRepository: GuideRepository
) {
    /**
     * 设置以太网静态IP地址
     *
     * @param address ip地址
     * @param mask    子网掩码
     * @param gate    网关
     * @param dns     dns
     */
    fun setEthernetStaticIp(
        address: String,
        mask: String,
        gate: String,
        dns: String,
        dns2: String
    ): Boolean {
        d(TAG, "setEthernetStaticIp --> ip: $address, mask: $mask, gate: $gate, dns: $dns, dns2: $dns2")
        return try {
            val ethernetManagerCls = Class.forName("android.net.EthernetManager")
            //获取EthernetManager实例
            @SuppressLint("WrongConstant") val ethManager = getApp().getSystemService("ethernet")
            val ipInfo = ipConfigRepository.getDefaultIp()
            val primaryDNS = if (dns.isNullOrEmpty() || dns.isNullOrBlank()) {
                ipInfo.IPAddress?.PrimaryDNS?.ipAddress
            } else {
                dns
            }
            val secondaryDNS = if (dns2.isNullOrEmpty() || dns2.isNullOrBlank()) {
                ipInfo.IPAddress?.SecondaryDNS?.ipAddress
            } else {
                dns2
            }
            //创建StaticIpConfiguration
            val staticIpConfiguration =
                newStaticIpConfiguration(address, gate, mask, primaryDNS ?: "", secondaryDNS ?: "")
            //创建IpConfiguration
            val ipConfiguration = newIpConfiguration(staticIpConfiguration)
            //获取EthernetManager的setConfiguration()
            val setConfigurationMethod = ethernetManagerCls.getDeclaredMethod(
                "setConfiguration",
                *arrayOf(NET_ETH_0.javaClass, ipConfiguration.javaClass)
            )
            //保存静态ip设置
            saveIpSettings(getApp(), address, mask, gate, dns, dns2)//保存和硬终端一致dns为空也配置
            //设置静态IP
            setConfigurationMethod.invoke(ethManager, NET_ETH_0, ipConfiguration)
            guideRepository.guideStatus = true
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }


    /**
     *
     * 创建StaticIpConfiguration对象
     */
    @Throws(Exception::class)
    private fun newStaticIpConfiguration(
        address: String,
        gate: String,
        mask: String,
        dns: String,
        dns2: String
    ): Any {
        val staticIpConfigurationCls = Class.forName("android.net.StaticIpConfiguration")
        //实例化StaticIpConfiguration
        val staticIpConfiguration = staticIpConfigurationCls.newInstance()
        val ipAddress = staticIpConfigurationCls.getField("ipAddress")
        val gateway = staticIpConfigurationCls.getField("gateway")
        val domains = staticIpConfigurationCls.getField("domains")
        val dnsServers = staticIpConfigurationCls.getField("dnsServers")
        //设置ipAddress
        ipAddress[staticIpConfiguration] = newLinkAddress(address, mask)
        //设置网关
        gateway[staticIpConfiguration] = InetAddress.getByName(gate)
        //设置掩码
        domains[staticIpConfiguration] = mask
        //设置dns1
        val dnsList = dnsServers[staticIpConfiguration] as ArrayList<InetAddress>
        dnsList.add(InetAddress.getByName(dns))
        //设置dns2
        dnsList.add(InetAddress.getByName(dns2))
        return staticIpConfiguration
    }

    /**
     * 获取LinkAddress实例
     */
    @Throws(Exception::class)
    private fun newLinkAddress(address: String, mask: String): Any? {
        val linkAddressCls = Class.forName("android.net.LinkAddress")
        val linkAddressConstructor = linkAddressCls.getDeclaredConstructor(
            InetAddress::class.java,
            Int::class.java
        )
        d(TAG, "子网掩码参数:ip: ${InetAddress.getByName(address)} ${maskStr2InetMask(mask)}")
        return linkAddressConstructor.newInstance(
            InetAddress.getByName(address),
            maskStr2InetMask(mask)
        )
    }

    /**
     * 获取IpConfiguration实例
     */
    @Throws(Exception::class)
    private fun newIpConfiguration(staticIpConfiguration: Any): Any {
        val ipConfigurationCls = Class.forName("android.net.IpConfiguration")
        val ipConfiguration = ipConfigurationCls.newInstance()
        //设置StaticIpConfiguration
        val staticIpConfigurationField = ipConfigurationCls.getField("staticIpConfiguration")
        staticIpConfigurationField[ipConfiguration] = staticIpConfiguration
        //获取ipAssignment、proxySettings的枚举值
        val ipConfigurationEnum = getIpConfigurationEnum(ipConfigurationCls)
        //设置ipAssignment
        val ipAssignment = ipConfigurationCls.getField("ipAssignment")
        ipAssignment[ipConfiguration] = ipConfigurationEnum["IpAssignment.STATIC"]
        //设置proxySettings
        val proxySettings = ipConfigurationCls.getField("proxySettings")
        proxySettings[ipConfiguration] = ipConfigurationEnum["ProxySettings.STATIC"]
        return ipConfiguration
    }

    /**
     * 获取IpConfiguration的枚举值
     */
    private fun getIpConfigurationEnum(ipConfigurationCls: Class<*>): Map<String, Any> {
        val enumMap: MutableMap<String, Any> = HashMap()
        val enumClass = ipConfigurationCls.declaredClasses
        for (enumC in enumClass) {
            val enumConstants = enumC.enumConstants ?: continue
            for (enu in enumConstants) {
                enumMap[enumC.simpleName + "." + enu.toString()] = enu
            }
        }
        return enumMap
    }

    /**
     * 保存静态ip设置
     */
    private fun saveIpSettings(
        context: Context,
        address: String,
        mask: String,
        gate: String,
        dns: String,
        dns2: String
    ) {
        val contentResolver = context.contentResolver
        Settings.Global.putString(contentResolver, "ethernet_static_ip", address)
        Settings.Global.putString(contentResolver, "ethernet_static_mask", mask)
        Settings.Global.putString(contentResolver, "ethernet_static_gateway", gate)
        Settings.Global.putString(contentResolver, "ethernet_static_dns1", dns)
        Settings.Global.putString(contentResolver, "ethernet_static_dns2", dns2)
    }

    /**
     * 获取子网掩码长度
     */
    private fun maskStr2InetMask(maskStr: String): Int {
        var sb: StringBuffer
        var str: String
        var inetmask = 0
        var count = 0

        /***
         * check mask format
         */
        val pattern: Pattern =
            Pattern.compile("(^((\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])$)|^(\\d|[1-2]\\d|3[0-2])$")
        if (pattern.matcher(maskStr).matches() === false) {
            d(TAG, "subMask is error")
            return 0
        }
        val ipSegment = maskStr.split("\\.".toRegex()).toTypedArray()
        for (n in ipSegment.indices) {
            sb = StringBuffer(Integer.toBinaryString(ipSegment[n].toInt()))
            str = sb.reverse().toString()
            count = 0
            var i = 0
            while (i < str.length) {
                i = str.indexOf("1", i)
                if (i == -1) break
                count++
                i++
            }
            inetmask += count
        }
        return inetmask
    }

到了这里,关于Android静态ip设置的坑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android P 9.0 增加以太网静态IP功能

    1、vendormediatekproprietarypackagesappsMtkSettingsresxmlnetwork_and_internet.xml 在 mobile_network_settings 和 tether_settings 之间增加如上代码, 对应的 icon 资源文件是我从 SystemUI 中拷贝过来的,稍微调整了下大小,也贴给你们吧 2、vendormediatekproprietarypackagesappsMtkSettingsresdrawableic_ethern

    2024年02月22日
    浏览(54)
  • linux设置静态ip与windows互相ping通、设置静态ip之后不能联网和网络服务重启失败的问题

    转载:https://www.codenong.com/cs105332412/ 按照提示查看网络服务的状态 看到这篇博文https://www.cyberithub.com/failed-to-start-lsb-bring-up-down-networking/ 首先可能是因为你MAC地址的问题,在 /etc/sysconfig/network-scripts/ifcfg-ens33 中提供正确的 MAC 地址并重新启动网络服务后应该可以解决此错误。

    2024年02月05日
    浏览(54)
  • VMware设置静态ip地址及不同网络模式讲解【Linux网络问题】

    此处的静态IP配置选用的是使用NAT方式连接网络【如果之前配置有错误,可以尝试暴力方法:将虚拟机网络配置重新恢复为默认,然后从头开始配置】 1.查看VMware给Linux虚拟机分配的ip所在网段: (1)右键点击VMware中的【编辑】菜单栏,打开【虚拟网络编辑器】,然后选中【

    2024年02月01日
    浏览(52)
  • 如何在 Linux 中将本地网络 IP 设置为静态的

    笔者的运行环境: CentOS 8 Red Hat Enterprise Linux 9   在 Linux 中将本地网络 IP 设置为静态的,只需要在那台 Linux 上设置即可。为了方便,这里笔者使用了 Xshell 来远程访问那台 Linux。关于如何在 Windows 主机上访问本地局域网中的 Linux 主机,可见笔者的另一篇博客:    如何在

    2024年02月07日
    浏览(41)
  • Android开发知识学习——TCP / IP 协议族

    一系列协议所组成的一个网络分层模型 为什么要分层? 因为网络的不稳定性 Application Layer 应用层:HTTP、FTP、DNS,提供了网络应用程序之间的通信机制 Transport Layer 传输层:TCP、UDP,提供可靠的端到端的数据传输服务的层次 Internet Layer 网络层:IP,网络中所有数据传输的入口

    2024年02月07日
    浏览(43)
  • VMware Linux Centos 配置网络并设置为静态ip

    在root用户下进行以下操作 1. 查看子网ip和网关 (1)进入虚拟网络编辑器 (2)进入NAT设置 (3)记录子网IP和子网掩码 2. 修改网络配置文件 (1)cd到网络配置文件路径下 (2)查看当前目录 (3)用vi编辑ifcfg-ens33文件 (4)按键盘【i 键】进入编辑模式 修改配置文件 (5)填写

    2024年02月14日
    浏览(43)
  • Ubuntu 22.04下对无线网络作静态ip设置

    内容如题所示,最近本人安全毕业,参加工作了,此处应有鲜花和掌声,哈哈哈。但新的生活总是有很多的小问题,坎坎坷坷,所以,我继续记录工作和学习生活中遇到的问题。 今天带我的云哥给了我一个ip,一个mask和一个gateway,让我将自己的笔记本设置静态ip,我想,这不

    2024年02月15日
    浏览(66)
  • Debian 系列 Linux 的静态 DNS 、gateway 、IP 设置和网络重启

    各个 Linux 发行版的网络设置有很大不同。根据最近对 Debian 系列(含 Debian 12、基于 Debian 12 的Proxmox 8.0-2 以及基于Debian 11 的 openmediavault 6 )的使用经验,就该系列发行版的静态 DNS 、gateway 、IP address 设置和网络重启做一下归纳总结。 DNS 配置文件 DNS 设置仅涉及DNS 配置文件

    2024年02月14日
    浏览(41)
  • Android 设备 设置adb自动监听tcp 5555端口,重启有效

    Android 设备调试有两种连线方式:有线和无线; 有线是通过USB导线连接android设备和电脑端,无线方式是通过连接WIFI,通过TCP的方式,连接设备和电脑端,一般用 5555端口; 有线的调节,只需要打开调试模式基本就可以了,无线连接方式比有线多一步操作,就是需要先通过usb导线连接后,ad

    2024年02月08日
    浏览(40)
  • Ubuntu 22.04网络配置指南:如何设置静态IP和自定义DNS服务器

    找到并打开Netplan配置文件 : 在Ubuntu终端中,输入以下命令来编辑Netplan的配置文件: 请确保文件名与您系统中实际的文件名匹配。 更新配置文件 : 使用以下内容替换文件中的内容(根据实际情况调整接口名称 enp0s3 、IP地址、网关和DNS服务器): 在这个配置中: enp0s3 是网

    2024年04月11日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包