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层两个方面做简要的代码实现分析:文章来源:https://www.toymoban.com/news/detail-682180.html
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模板网!