1. 程式人生 > >Android WiFi 許可權、廣播、連線、踩坑相關記錄

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 許可權組,需要手動申請該許可權.

至於許可權請求處理的第三方庫,工具類大家自個找吧,多得是…
我這邊因為除了常規的開啟關閉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秒…

程式碼邏輯直接就在這裡
這裡寫圖片描述

試了下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……
還是多注意,多測試下各種情況吧.