Android 6.0 中的 Wifi 连接

这几天在写一个软件,结果被其中的 wifi 连接问题困扰了 3 天。

先描述下需求:

  • usb 接口接了一根 usb2serial,通过这个接口接收命令
  • 当接收到的命令为连接 wifi 时,从命令中读出要连接的 wifi 名称,用这个名称去进行连接
  • 返回结果为是否能够找到这个 wifi,找到这个 wifi 是否能够连接

起先,我觉得这个问题是很容易的。它不就是:

  1. 构造出一个 WifiConfiguration实例;
  2. 将实例传递给WifiManager对象的addNetwork,此方法将会返回一个networkId(这个networkId其实就是说这个名称的 Wifi 是第几个添加的,它从 0 开始);
  3. 在获得了 networkId后,就可以使用 WifiManager 对象的 enableNetwork 方法启用此网络(这个方法的大概意思就是,看好了,我想要用这个网络开始连接了。一个事实是,使用这个方法,即使你之前没有 disconnect,wifi 也会中断);
  4. 然后使用 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 6.0 中的 Wifi 连接的更多相关文章

  1. 在Android 5.0中使用JobScheduler

    在Android 5.0中使用JobScheduler 原文链接 : using-the-jobscheduler-api-on-android-lollipop 译者 : Mr.Simple 校对者 ...

  2. Android 5.0中使用JobScheduler

    在这篇文章中,你会学习到在Android 5.0中怎样使用JobScheduler API. JobScheduler API同意开发人员在符合某些条件时创建运行在后台的任务. 介绍 在Android ...

  3. Android 7.0 中 ContentProvider 实现原理

    欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:汪毅雄 导语: 本文描述了ContentProvider发布者和调用者这两在Framework层是如何实现的. 作为Android的四大 ...

  4. 【树莓派】【转】树莓派3装Android 6.0,支持Wi-Fi和蓝牙

    树莓派3装Android 6.0,支持Wi-Fi和蓝牙 相信对于许多树莓派初学者(包括我)来说,Android系统的确是一个不错的选择.但国内这方面资源稀缺,经本人FQ苦寻,找到了老外的树莓派Andr ...

  5. 我的Android进阶之旅------>如何解决Android 5.0中出现的警告: Service Intent must be explicit:

    我的Android进阶之旅-->如何解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...

  6. 我的Android进阶之旅------&gt;怎样解决Android 5.0中出现的警告: Service Intent must be explicit:

    我的Android进阶之旅-->怎样解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...

  7. Android配置----小米手机通过wifi连接ADB调试Android应用

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...

  8. android 4.0 中出错 java.lang.UnsupportedOperationException

    在android4.0中  画图的时候使用: canvas.clipPath(path, Region.Op.XOR); 报错 java.lang.UnsupportedOperationExcept ...

  9. Android Studio3.0中dependencies依赖由compile变为implementation的区别

    前言 Android Studio版本更新至3.0了,更新后,连带着com.android.tools.build:gradle 工具也升级到了3.0.0,在3.0.0中使用了最新的Gralde 4. ...

随机推荐

  1. 在express中HMR(合并express和webpack-dev-server)

    在学习react的时候,经常用create-react-app来创建web应用,然而写到后面总有连自己服务器和数据库的需求,create-react-app创建的是一个webpack-dev-serv ...

  2. L2-006 树的遍历 (后序中序求层序)

    题目: 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(≤30),是二叉树中结点的个数.第二行给出其后序遍历序 ...

  3. MySQL->处理重复数据[20180517]

    限制数据重复的方式:表上增加主键(Primary Key)或增加唯一性索引(Unique)     主键对重复资料进行限制,这样资料在导入时就无法重复插入 create table primary_t ...

  4. 高性能MySQL--MySQL数据类型介绍和最优数据类型选择

    MySQL支持的数据类型很多,那么选择合适的数据类型对于获得高性能就至关重要.那么就先了解各种类型的优缺点! 一.类型介绍 1.整型类型 整型类型有: TINYINT,SMALLINT,MEDIUMI ...

  5. Java编码算法和摘要算法

    编码算法 编码算法是将一种形式转换成等价的另外一种形式.主要是为了方便某种特定场景的处理. 字母如何在计算机中表示呢? 用ASCII编码 那中文字符如何在计算机中表示呢? 用Unicode编码 如何同 ...

  6. 基于MATLAB的多项式数据拟合方法研究-毕业论文

    摘要:本论文先介绍了多项式数据拟合的相关背景,以及对整个课题做了一个完整的认识.接下来对拟合模型,多项式数学原理进行了详细的讲解,通过对文献的阅读以及自己的知识积累对原理有了一个系统的认识.介绍多项式 ...

  7. 20155214 2016-2017-2 《Java程序设计》第4周学习总结

    20155214 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 Chapter6 父类就是将多个类的相同代码提升(Pull Up) 子类继承父类会继承到pr ...

  8. Win10系统下VirtualBox虚拟机初体验

    在接触本次的VirtualBox之前,我在大一下学期参加李冬冬老师的选修课中学习过VMware,并使用VMware进行过一些计算机病毒之类的实验.但是,使用虚拟机模拟其他不同操作系统这次是第一次,因此 ...

  9. Cache-Aside模式

    Cache-Aside 该模式是从数据仓库中将数据加载到缓存中,从而提高访问速度的一种模式.该模式可以有效的提高性能,同时也能一定程度上保证缓存中的数据和数据仓库中的数据的一致性,和同步数据到数据仓库 ...

  10. bzoj1901&zoj2112&cogs257 Dynamic Rankings(动态排名系统)

    bzoj1901&zoj2112&cogs257 Dynamic Rankings(动态排名系统) cogs zoj bzoj-权限 题解 bzoj和zoj都是骗访问量的233,我没有 ...