Android WiFi 权限、广播、连接、踩坑相关记录

emmm…最近项目首页重构,UI重新弄,逻辑拆分重新写,变成我来写了…写完了,踩了好几个坑,好几个都忘记了,赶紧记一下防止都忘记了…

1.权限请求…

既然是WiFi连接,当然首先考虑到的是打开WiFi啊什么的,现在基本都是targetSdkVersion >=23了吧,这些就会有涉及到权限请求的问题.

主要是分为2个部分的权限:

1. 打开WIFI开关
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

这些权限仅仅是普通权限,直接写在清单文件里面即可…

2. 扫描WIFI信号
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

这个权限是隶属于 LOCATION 权限组,需要手动申请该权限.

权限组权限明细
https://developer.android.com/guide/topics/permissions/requesting.html

至于权限请求处理的第三方库,工具类大家自个找吧,多得是…
我这边因为除了常规的打开关闭WiFi,扫描WiFi信号还涉及到实际的业务逻辑,需要检测当前连接的是否为硬件对应的WiFi信号,同时页面和逻辑作不同的处理.撇开业务逻辑,说说常规流程和遇到的坑吧.


1.

工欲善其事必先利其器,先封装一个WifiManager对应的工具类吧,否则操作起来实在麻烦.

2.

判断WiFi是否开启(刚刚进入Activity/Fragment进行判断…)

mWifiManager.isWifiEnabled();

3.

WiFi关闭,点击申请打开WiFi,请求Manifest.permission.ACCESS_FINE_LOCATION权限.
该权限涉及到扫描WiFi列表.

4.

打开WiFi

if (!mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(true);
}

重点来了!!!

代码在执行到这一步的时候,小米/华为手机会弹窗出来(没有三丧,蓝绿厂的测试机,估计也差不多),是否允许打开WiFi开关,WTF…

这和权限有毛线关系,你们吃多了吧…
小米弹窗只需10秒钟,10秒钟没点击就会自动选择拒绝
华为更狠,15秒…

代码逻辑直接就在这里
Android WiFi 权限、广播、连接、踩坑相关记录_第1张图片

试了下MOTO X的 类似于原生Google的ROM…好好地,说开就开.

所以一切逻辑请放在这个后面执行,同时以WiFi开关是否打开作为判断用户点击弹窗的结果,顺带加个延时(考虑到有的手机配置低,4系U,甚至还有”良心”诺基亚的2系U),我是延时2000毫秒.发现红米够用,能够在2000毫秒内成功开启WiFi.

在实际过程中,掺杂着业务逻辑,需要一定时间的连接动画,请务必在动画执行完以后再次判断WiFi当前状态,毕竟在连接动画中,一个手势下拉状态栏,WiFi关掉就1,2秒就搞定,否则执行动画结束后,发现逻辑继续执行,但是WiFi是关闭的就尴尬了.

5.获得WiFi列表

 mWifiManager.startScan();
 // 得到扫描结果
 mWifiList = mWifiManager.getScanResults();
 // 得到配置好的网络连接
 mWifiConfiguration = mWifiManager.getConfiguredNetworks();

遍历去重,建立制定WiFi名称规则的list进行展示和连接.
连不上就直接跳转设置手动连接吧

startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));

以上仅仅是普通流程,主要问题还是在请求权限和打开WiFi,国内ROM设置的弹窗问题…
实际掺杂了业务逻辑后要麻烦的多,得考虑不同WiFi状态页面的显示情况和连接动画执行情况.


2.广播监听…

part.1主要涉及的是WiFi连接状态和业务逻辑.
这部分主要是对于切换WiFi开关后,通过监听对应广播后进行处理的一些情况和坑.

1.

注册广播和注销广播

/**
     * 注册wifi广播监听
     */
    private void sendReceiver() {
        IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        mWiFiReceiver = new WifiStateReceiver();
        mContext.registerReceiver(mWiFiReceiver, filter);
    }

    /**
     * 注销wifi广播监听
     */
    private void stopReceiver() {
        if (mWiFiReceiver != null) {
            mContext.unregisterReceiver(mWiFiReceiver);
            mWiFiReceiver = null;
        }
    }

这个没什么好说的,将注册和注销放在对应的生命周期方法内即可.

2.广播部分

首先是WiFi打开关闭的广播
WifiManager.WIFI_STATE_CHANGED_ACTION

//WIFI打开和关闭...
 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
    int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
    switch (wifiState) {
                        case WifiManager.WIFI_STATE_DISABLED:
                            //这里处理WiFi关闭以后的逻辑   
                            break;
                        case WifiManager.WIFI_STATE_ENABLED:

                            break;
                        default:
                            break;
                    }
                }

EXTRA_WIFI_STATE 对应的State有5种,分别是DISABLING、DISABLED、ENABLING、ENABLED、UNKNOWN.

我记得之前我是直接通过这个广播状态来处理整个WiFi变化的逻辑处理,但是后来发现有坑,具体什么坑,抱歉,我忘记了,2333.反正别用就对了,继续往下看.

后来对于EXTRA_WIFI_STATE,主要使用的就是 DISABLED来处理WiFi关闭的情况.至于WiFi开启则是通过 WifiManager.NETWORK_STATE_CHANGED_ACTION


当WifiManager.WIFI_STATE_ENABLED时,就能接收到NETWORK_STATE_CHANGED_ACTION.
它有6种情况,分别是CONNECTING、CONNECTED、SUSPENDED、DISCONNECTING、DISCONNECTED、UNKNOWN.
主要监听的是DISCONNECTED和CONNECTED.
它们代表的状态是 我WiFi开关是开着,但是我没有连接和我WiFi开关是开着,且我已经连接上.

需要注意的是

1.注意添加flag,防止在刚刚进入Activity/Fragment时,切换WiFi开关连接状态,同时执行广播逻辑

2.NetworkInfo.State.CONNECTED内逻辑添加延时处理(针对连接制定设备WiFi信号进行鉴权,认证逻辑处理).

在切换WiFi,且WiFi自动连接时,逻辑会先走DISCONNECTED然后才是CONNECTED,会出现当前已经获得到WIFI是对的,但是实际还没有连接上的这种情况,会导致DISCONNECTED和CONNECTED逻辑冲突,还是建议给CONNECTED逻辑进行延时处理.

3.广播接收去重的问题

每次切换WiFi时,都会重复发送,重复接收到WiFi,需要进行去重.

我这边的办法是通过判断时间来进行去重

private static final int WIFI_DISABLED = 0;
private static final int WIFI_DISCONNECTED = 1;
private static final int WIFI_CONNECTED = 2;

private boolean forOnceByStatus(int index) {
            long nowTime = TimeUtils.getCurrentTimeInLong();
            if ((nowTime - (index == 0 ? timeForDisabled : (index == 1 ? timeForDisconnect : timeForConnect))) > 800) {
                if (index == WIFI_DISABLED) {
                    timeForDisabled = nowTime;
                } else if (index == WIFI_DISCONNECTED) {
                    timeForDisconnect = nowTime;
                } else if (index == WIFI_CONNECTED) {
                    timeForConnect = nowTime;
                }
                return true;
            } else {
                return false;
            }
        }

分别在DISABLED,DISCONNECTED,CONNECTED进行调用,为true才允许执行相关逻辑.

当关闭WiFi:
WIFI关闭我进来了DISCONNECTED,时间是====1509342281409
WIFI关闭我进来了DISABLED,时间是====1509342281589

当开启WiFi:
WIFI开启我进来了DISCONNECTED,时间是====1509342288210
WIFI开启我进来了CONNECTED,时间是====1509342289212


总结

总体来说,WiFi开启/关闭,扫描,连接,广播监听.API还是挺简单的,当涉及掺杂实际的业务逻辑时,就会变得贼鸡儿麻烦.
需要考虑的情况特别多,什么手动给你关WiFi,权限拒绝,弹窗拒绝,连到一半关,连接的不是指定WiFi……
还是多注意,多测试下各种情况吧.

你可能感兴趣的:(Android)