Android 6.0 中的 Wifi 连接
2017-03-16 17:01
369 查看
Android 6.0 中的 Wifi 连接
这几天在写一个软件,结果被其中的 wifi 连接问题困扰了 3 天。先描述下需求:
usb 接口接了一根 usb2serial,通过这个接口接收命令
当接收到的命令为连接 wifi 时,从命令中读出要连接的 wifi 名称,用这个名称去进行连接
返回结果为是否能够找到这个 wifi,找到这个 wifi 是否能够连接
起先,我觉得这个问题是很容易的。它不就是:
构造出一个
WifiConfiguration实例;
将实例传递给
WifiManager对象的
addNetwork,此方法将会返回一个
networkId(这个
networkId其实就是说这个名称的 Wifi 是第几个添加的,它从 0 开始);
在获得了
networkId后,就可以使用
WifiManager对象的
enableNetwork方法启用此网络(这个方法的大概意思就是,看好了,我想要用这个网络开始连接了。一个事实是,使用这个方法,即使你之前没有 disconnect,wifi 也会中断);
然后使用
WifiManager对象的
reconnect重新进行网络连接。
看着这一切都十分的完美。但是,要是真是如此,那么绝对不会折磨我 3 天的时间了。
第一个坑:权限问题
其实说这个问题是第一个坑,完全是顺带提了一下,实际上,我完全没有在这个坑中呆过。这个坑是因为 Android 6 引入了一个动态权限问题。需要额外的一些代码处理。大家可以自己搜索下搞定或者是使用一些额外的库。第二个坑:addNetwork 方法的返回值为 -1
这个也是 android 6 之后才有的东西。具体返回值为-1的情况为指定的 SSID 由其他应用程序连接(包括系统的设置)。对于这一情况,其实一句话形容就是谁拉的屎谁去擦屁股(系统的 Setting 可以擦所有 app 的屁股)。所以,当 APP 本身已经连接 SSID 为 “ABCD” 的热点一次,第二次连接时,如果依旧以最开始的步骤进行处理,那么,依旧会得到和之前相同的
networkId,但是,假如系统或者是其他的 APP 已经连接过 “ABCD”,那么,你只能得到一个 -1。
这里,
-1还会引发另一个问题,就是当下一步想要使用
enableNetwork进行网络的一个预处理,将会发现,怎么阻塞在这个函数上了。
第三个坑:Android 将会自动连接其他的 Wifi
在看第二个坑的时候,有人一定很好奇,你为什么不使用getConfiguredNetworks来获得已经配置过的 Wifi 列表,这样就不需要再次配置了。理想很好,但是这样很不好。一个情况就是,假设现在已经存在“ABCD”和“1234”这2个热点,其中“1234”这个热点之前已经连接过,且没有删除记录。在这一情况下,连接“ABCD”失败后,系统会自动连接到"1234"上去。
为什么会这样子,我是很不解的。这里,看下 SDK 中的内容:
/** * Allow a previously configured network to be associated with. If * <code>disableOthers</code> is true, then all other configured * networks are disabled, and an attempt to connect to the selected * network is initiated. This may result in the asynchronous delivery * of state change events. * <p> * <b>Note:</b> If an application's target SDK version is * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or newer, network * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may * instead be sent through another network, such as cellular data, * Bluetooth tethering, or Ethernet. For example, traffic will never use a * Wi-Fi network that does not provide Internet access (e.g. a wireless * printer), if another network that does offer Internet access (e.g. * cellular data) is available. Applications that need to ensure that their * network traffic uses Wi-Fi should use APIs such as * {@link Network#bindSocket(java.net.Socket)}, * {@link Network#openConnection(java.net.URL)}, or * {@link ConnectivityManager#bindProcessToNetwork} to do so. * * @param netId the ID of the network in the list of configured networks * @param disableOthers if true, disable all other networks. The way to * select a particular network to connect to is specify {@code true} * for this parameter. * @return {@code true} if the operation succeeded */ public boolean enableNetwork(int netId, boolean disableOthers) { final boolean pin = disableOthers && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP; if (pin) { NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build(); NetworkPinner.pin(mContext, request); } boolean success; try { success = mService.enableNetwork(netId, disableOthers); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } if (pin && !success) { NetworkPinner.unpin(); } return success; }
在
enableNetwork函数的开始,是有这样的一段话的,其中说明这个函数需要两个参数,一个是需要连接的'networkID',也就是之前通过
addNetwork获得的返回值,另一个参数是说如果为真,则 disable 其他的网络,然后让系统老老实实的连接到指定的网络。这里 disable 算是一个很有意思的词,它到底是起到 disconnect 的作用呢,还是 block other 的作用呢。前文也说了,就算没有调用
disconnect函数,也会断开连接,从这一现象推测,它应该是会断开其他的连接。
最开始的时候我以为这个方法起到了 block other 的作用,但是看结果显然不是。
另外,也找了 SDK 中所有可能的方法,没有找到禁止在一个连接失败的情况下连接其它热点的方法。那么,最好的办法就是,在开始使用之前,系统不曾连接过一个热点,之后每次连接结束后由程序删除已经配置好的连接。因为手动删除了,所以
getConfiguredNetworks方法也就用不上了。
到底如何在 Android 6.0 以上的系统中连接指定名称的 Wifi
根据这几天的琢磨,以下的流程应该是比较的合适的:1根据得到的 SSID 信息,在已配置的 wifi 列表中进行搜索,后得到 networkid
2否则,根据 SSID ,在所有可用 WIFI 列表中进行搜索(非隐藏网络),如果找到,进行配置后 add,得到对应的 networkid
2如果是隐藏网络,则直接进行配置,然后通过 add 方法获得对应的 networkid
3使用得到的id进行添加网络,然后尝试进行连接。
在第三点中,我进行了额外的处理,即当返回值为false时,如果配置文件是 app 生成的,那么将会主动将其删除。否则的话,这个配置将会存储于已配置的热点列表中(考虑下,反正是连接不上,又是自己创建的,那就删除呗,指不定是手残 SSID 输入错误呢)。
通过以上的几个步骤,可以保证对于一个指定的 SSID,如果之前已经配置过那么就可以直接进行连接,否则自己新建一个配置进行连接。如果能够正常的连接,那么连接信息就存储于已配置列表中,下次连接就不用再创建配置,否则就手动将配置删除。以保证已配置的热点列表处于“干干净净”的状态。
晚些时候给出一个示例......
相关文章推荐
- 关于Android 5.0 、6.0 、 7.0代码设置WIFI连接方式为静态IP或DHCP的方法及注意事项
- Android Wifi连接控制、TCP、UDP通信,6.0以上适配
- android代码连接到指定wifi,适合5.0,6.0,7.0系统
- Android6.0通过WiFi名称密码连接WiFi的方案
- Android编程获取网络连接状态(3G/Wifi)及调用网络配置界面
- Ubuntu 11.04 下台式电脑通过已连接WIFI的Android手机上网
- 浅析android下如何通过jni监控wifi网络连接、dhcpcd执行和power电源控制
- Android编程获取网络连接状态(3G/Wifi)及调用网络配置界面
- Android 下使用wpa_cli 连接 wifi
- android 得到当前已连接的wifi的信号强度
- android socket wifi 连接PC实现简单的PPT控制器
- 浅析android下如何通过jni监控wifi网络连接、dhcpcd执行和power电源控制
- android 得到当前已连接的wifi的信号强度
- Android编程获取网络连接状态(3G/Wifi)及调用网络配置界面
- Android 中的WiFi学习笔记(转载)----WIFI启动 代码流程走读---网络连接流程
- 通过wifi 连接手机进行android程序调试
- 浅析android下如何通过jni监控wifi网络连接、dhcpcd执行和power电源控制
- android中wifi的上下层的连接、命令发送
- Android编程获取网络连接状态(3G/Wifi)及调用网络配置界面
- defy用WIFI连接PC上网(限Android2.2)