Android-WiFi開發(fā)之 WiFi廣播監(jiān)聽(格式化版)

引子

  • 安卓中關于系統(tǒng)開發(fā)的文章比較少, 而且較為不全面.
  • 對于剛剛接觸做系統(tǒng)應用的開發(fā)的開發(fā)者而言, 在自己動手開發(fā)前, 需要借助 Android 源碼查詢, 參考的方式來實現(xiàn).
  • 本文為本人在開發(fā)中 WIFI 模塊功能過程中總結(jié)出來的廣播部分, 希望為大伙指點迷津.

過程

  • 最近做的研發(fā)項目, 需要做一個設置功能, 替換原生設置的apk, 內(nèi)部牽涉到的功能元有: 網(wǎng)絡, 藍牙, 存儲等. 前期被分配到涉及 WIFI 相關的功能開發(fā). 其中重要功能列表包含: 開啟/關閉 WIFI, 掃描/刷新WIFI列表, 添加隱藏網(wǎng)絡, 連接網(wǎng)絡, 編輯/保存/修改網(wǎng)絡, 忘記網(wǎng)絡等.
  • 剛開始是無從下手的, 對于監(jiān)聽系統(tǒng)的廣播及需要處理的數(shù)據(jù)也是摸不著頭腦, 更對Android本身的WIFI體系, 驅(qū)動識別, 數(shù)據(jù)存儲, 數(shù)據(jù)結(jié)構(gòu)更新等一無所知, 經(jīng)過一系列的學習和摸索, 參考原生 Settings 應用的各種處理手段, 才一步步破解了腦中的一團, 現(xiàn)抽空做了一點總結(jié), 本文為 WIFI 開發(fā)廣播篇, 具體內(nèi)容如下.

關于網(wǎng)絡開發(fā), 你可能會用到的廣播

  • 原生 Settings 中的代碼原型:
    IntentFilter filter = new IntentFilter();
    filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
    filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
    filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
    filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
  • 如上代碼塊是原生 Settings 中, 關于 WLAN 頁面中的整體功能的一個 IntentFilter 注冊. 其主要使用了 WifiManager 類中的 action 作為匹配. 對于不大了解上述 action 的開發(fā)者, 可以根據(jù)每個 ACTION 的常量的名字來猜一猜大概含義. Android 在廣播方面做的比較好的, 就有 ACTION 的定義這一條, 讓初學者, 可以通過ACTION的常量名, 就能大概猜出來, 這條廣播是干嘛的了. 下面, 我會圍繞上述的幾條廣播來做一個簡單的闡述.

ConnectivityManager 的一些說明:

  • 大多數(shù)做互聯(lián)網(wǎng)應用的開發(fā)者, 會在請求網(wǎng)絡 / 刷新內(nèi)容時, 對網(wǎng)路聯(lián)通做一個初步處理, 即: 網(wǎng)路存在, 進行網(wǎng)絡請求, 網(wǎng)絡不存在, 提示用戶當前沒網(wǎng)絡聯(lián)通, 設置網(wǎng)絡 / 顯示加載失敗. 那么首先, 如果判斷當前是否正常聯(lián)通網(wǎng)絡了呢? 這就用到了 Android 提供給廣大開發(fā)者的一個關于網(wǎng)絡的管理類: ConnectivityManager 了;

  • ConnectivityManager 中封裝了關于連接方式、連接類型、是否當前連接可用...豐富的供外部調(diào)用的函數(shù); 可以通過當前的 Context 以獲取系統(tǒng)服務的方式來獲取, 具體代碼如下:

    // 獲取 ConnectivityManager 對象. 
    public static ConnectivityManager getConnectivityManager(Context context) {
        return context == null ? null : (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    }
  • 利用 ConnectivityManager 獲取一些關于連接的東西了, 需要在 AndroidManifest 中配置android.permission.ACCESS_NETWORK_STATE的權(quán)限:
    // 獲取默認的連接方式: 
    public static NetworkInfo getDefaultNetwork(ConnectivityManager manager) {
        return manager == null ? null : manager.getActiveNetworkInfo();
    }
    
    // 默認網(wǎng)絡是否連接: 
    public static boolean isDefaultNetworkConnected(NetworkInfo info) {
        return info != null && info.isConnected() && info.isAvailable();
    }
    
    // 默認網(wǎng)絡是有線網(wǎng): 
    public static boolean isDefaultNetworkIsEthernet(NetworkInfo info) {
        return info != null && info.getType() == ConnectivityManager.TYPE_ETHERNET;
    }
    
    // 默認網(wǎng)絡是無線網(wǎng): 
    public static boolean isDefaultNetworkIsWifi(NetworkInfo info) {
        return info != null && info.getType() == ConnectivityManager.TYPE_WIFI;
    }
    
    // 有線網(wǎng)是否連接: 
    public static boolean isEthernetConnected(ConnectivityManager manager) {
        return manager != null && manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET) != null && manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET).isConnected;
    }
    
    // 無線網(wǎng)是否連接: 
    public static boolean isWifiConnected(ConnectivityManager manager) {
        return manager != null && manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI) != null && manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected;
    }
  • 動態(tài)監(jiān)聽網(wǎng)絡連接狀態(tài)的變化: ConnectivityManager.CONNECTIVITY_ACTION, 互聯(lián)網(wǎng)應用大多會根據(jù)網(wǎng)絡連接成功, 而主動刷出來內(nèi)容給用戶, 其監(jiān)聽的網(wǎng)絡連接變化的ACTION, 便是這一條.

關于 action 的一些說明:

  1. 通過第一步, 已經(jīng)可以靜態(tài)獲取當前的網(wǎng)絡連接了, 而在實際開發(fā)過程中, 在很多情況下, 是需要動態(tài)刷新狀態(tài)的, 那么就依賴到 Android 的廣播機制了.

  2. 廣播機制是 Android 中重要的機制之一, 在跨進程方面的表現(xiàn)也是相當樂觀的, 不僅是安卓剛?cè)腴T還是精通安卓開發(fā)的高級工程師, 對此機制應該都是津津樂道的. 一方面是簡單易用, 另一方面, 對于進程通知, 調(diào)度, 數(shù)據(jù)傳遞都存在非常大的意義.

  3. 那么, 說回來了, 既然要動態(tài)監(jiān)聽, 那么我們應該怎么做呢, 當然是動態(tài)監(jiān)聽系統(tǒng)發(fā)出的廣播了;下面就把開發(fā)中你可能需要用到的廣播做一小說明:

  • 首先例舉出網(wǎng)絡開發(fā)中主體會使用到的action, 如下:
    ConnectivityManager.CONNECTIVITY_ACTION
    WifiManager.WIFI_STATE_CHANGED_ACTION
    WifiManager.SCAN_RESULTS_AVAILABLE_ACTION
    WifiManager.NETWORK_IDS_CHANGED_ACTION
    WifiManager.SUPPLICANT_STATE_CHANGED_ACTION
    WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION
    WifiManager.LINK_CONFIGURATION_CHANGED_ACTION
    WifiManager.NETWORK_STATE_CHANGED_ACTION
    WifiManager.RSSI_CHANGED_ACTION
  • 上述 action, 做如下說明, 使用起來也會更加清晰:

  • ConnectivityManager.CONNECTIVITY_ACTION: 網(wǎng)絡連接發(fā)生了變化的廣播, 通常是默認的連接類型已經(jīng)建立連接或者已經(jīng)失去連接會觸發(fā)的廣播; 監(jiān)聽到這個廣播之后, 可以從 intent 中獲取字段 ConnectivityManager.EXTRA_NO_CONNECTIVITY, 如果返回 true, 代表當前連接斷開. 否則, 連接成功. 同時可以從 intent 中取出字段 ConnectivityManager.EXTRA_NETWORK_INFO, 返回 NetWorkInfo, 通過此對象, 你會獲取當前連接的一些更為具體的信息. 部分代碼如下:

    // 是否無連接.
    public static boolean isNoConnectivity(Intent intent) {
        return intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
    }
    
    // 獲取當前網(wǎng)絡信息. 
    public static NetworkInfo getExtraNetworkInfo(Intent intent) {
        return intent.getPacelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
    }
    
    // 獲取當前網(wǎng)絡狀態(tài). 
    public static NetworkInfo.State getNetState(NetworkInfo info) {
        return info == null ? null : info.getState();
    }
    
    // 獲取當前網(wǎng)絡類型. 
    public static int getNetState(NetworkInfo info) {
        return info == null ? -1 : info.getType();
    }
  • WifiManager.WIFI_STATE_CHANGED_ACTION: WiFi模塊硬件狀態(tài)改變的廣播, 對于肉眼而言, 看到的直觀表征有, WiFi開啟, WiFi關閉; 而在實際的過程中, WIFI 從開啟到關閉, 或是從關閉到開啟, 需要經(jīng)歷三個狀態(tài), 以開啟WIFI為例, 其要經(jīng)過的狀態(tài)分別為: 已關閉, 開啟中, 已開啟. 關閉WIFI則相反, 分為為: 已開啟, 關閉中, 關閉. 接收到這個廣播后, 你可以從intent中取出當前WiFi硬件的變化狀態(tài), 可以使用 int 值來區(qū)別; 這個key是: EXTRA_WIFI_STATE, 可能得到的值為:0, 1, 2, 3, 4; 當然除了這種獲取方式, 也可以通過WiFiManager對象getWifiState() 獲取這個值. 也可以從 intent 中取出另外一個值, 表示之前WiFi模塊的狀態(tài), 是不是很爽? 那么, 對應的key, 就是: EXTRA_PREVIOUS_WIFI_STATE;
    // 通過 intent 獲取當前WIFI狀態(tài). 
    public static int getWifiStateByIntent(Intent intent) {
        return intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
    }
    
    // 通過 WifiManager 獲取當前WIFI狀態(tài). 
    public static int getWifiStateByWifiManager(WifiManager manager) {
        return manager == null ? WifiManager.WIFI_STATE_UNKNOWN : manager.getState();
    }
    
    // 獲取WIFI前一時刻狀態(tài). 
    public static int getWifiPreviousState(Intent intent) {
        return intent.getIntExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
    }
其中:
 0 --> WiFiManager.WIFI_STATE_DISABLING, 表示 WiFi 正關閉的瞬間狀態(tài);
 1 --> WifiManager.WIFI_STATE_DISABLED, 表示 WiFi 模塊已經(jīng)完全關閉的狀態(tài); 
 2 --> WifiManager.WIFI_STATE_ENABLING, 表示 WiFi 模塊正在打開中瞬間的狀態(tài); 
 3 --> WiFiManager.WIFI_STATE_ENABLED, 表示 WiFi 模塊已經(jīng)完全開啟的狀態(tài);
 4 --> WiFiManager.WIFI_STATE_UNKNOWN, 表示 WiFi 處于一種未知狀態(tài); 通常是在開啟或關閉WiFi的過程中出現(xiàn)不可預知的錯誤, 通常是底層狀態(tài)機可能跑的出現(xiàn)故障了, 會到這種情況, 與底層控制相關; 
  • WifiManager.SCAN_RESULTS_AVAILABLE_ACTION: 掃描到一個熱點, 并且此熱點達可用狀態(tài) 會觸發(fā)此廣播; 此時, 你可以通過 wifiManager.getScanResult() 來取出當前所掃描到的 ScanResult; 同時, 你可以從intent中取出一個boolean值; 如果此值為true, 代表著掃描熱點已完全成功; 為false, 代表此次掃描不成功, ScanResult 距離上次掃描并未得到更新;
    // 獲取 ScanResult 列表: 
    public static List<ScanResult> getScanResultForWifi(WifiManager manager) {
        return manager == null ? null : manager.getScanResult();
    }

    // result 是否更新: 
    public static boolean isResultUpdated(Intent intent) {
        return intent != null && intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
    }
  • WifiManager.NETWORK_IDS_CHANGED_ACTION: 在網(wǎng)絡配置, 保存, 添加, 連接, 斷開, 忘記的操作過后, 均會對 WIFI 熱點配置形成影響, 在shell下, 如果有root權(quán)限, 可以在執(zhí)行上述動作前后, 分別瀏覽 /data/misc/wifi/wpa_supplicant.conf 應該是有本質(zhì)的變化, 此時會收到此廣播. 具體的執(zhí)行指令為:
adb shell 
$ cat /data/misc/wifi/wpa_supplicant.conf
  • WifiManager.SUPPLICANT_STATE_CHANGED_ACTION: 建立連接的熱點正在發(fā)生變化. 象征變化的相關類為: SupplicantState, 你可以在接收到此廣播時, 觀察到已經(jīng)建立連接的熱點的整個連接過程, 包含可能會出現(xiàn)連接錯誤的錯誤碼. 相關代碼為:
    // 獲取當前網(wǎng)絡新狀態(tài). 
    public static SupplicantState getCurrentNetworkState(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
    }
    
    // 獲取當前網(wǎng)絡連接狀態(tài)碼. 
    public static int getCurrentNetworkCode(Intent intent) {
        return int netConnectErrorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0);
    }
    
    // 當前網(wǎng)絡是否連接失敗
    public static boolean isCurrentNetworkConnectFailed(intent intent) {
        return WifiManager.ERROR_AUTHENTICATING == getCurrentNetworkCode(intent);
    }
  • WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION: 官方的注釋是這么說的, 廣播已配置的網(wǎng)絡發(fā)生變化, 可由添加, 修改, 刪除網(wǎng)絡的觸發(fā). 當從 intent 中取出key值為 EXTRA_MULTIPLE_NETWORKS_CHANGED, 其值為 true 時, 那么字段 EXTRA_WIFI_CONFIGURATION 中取出來的配置已經(jīng)過時, 不是最新配置了, 具體的代碼為:
    // 是否多重網(wǎng)絡發(fā)生變化. 
    public static boolean isMultipleNetworkChanged(Intent intent) {
        return intent.getBooleanExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
    }
    
    // 獲取當前最新網(wǎng)絡配置: 
    public static WifiConfiguration getCurWifiConfig(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
    }
  • WifiManager.LINK_CONFIGURATION_CHANGED_ACTION: WIFI 連接配置發(fā)生改變的廣播. 此時, 網(wǎng)路連接功能封裝 LinkProperties 和 NetworkCapabilities 可能發(fā)生變化.
    // 獲取 LinkProperties 
    public static LinkProperties getLinkProperties(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES);
    }
    
    // 獲取 NetworkCapabilities
    public static NetworkCapabilities getNetworkCapabilities(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_CAPABILITIES);
    }
  • WifiManager.NETWORK_STATE_CHANGED_ACTION: WIFI 連接狀態(tài)發(fā)生改變的廣播. 可以從 intent 中取得 NetworkInfo, 此時 NetworkInfo 中提供了連接的新狀態(tài), 如果連接成功, 可以獲取當前連接網(wǎng)絡的 BSSID, 和 WifiInfo. 相關代碼:
    // 獲取當前網(wǎng)絡
    public static NetworkInfo getCurrentNetworkInfo(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
    }
    
    // 獲取當前網(wǎng)路狀態(tài). 
    public static NetworkInfo.State getCurrentNetworkState(NetworkInfo info) {
        return info != null ? info.getState() : null;
    }
    
    // 獲取當前網(wǎng)路BSSID.
    public static String getCurrentNetworkBssid(Intent intent) {
        return intent.getStringExtra(WifiManager.EXTRA_BSSID);
    }
    
    // 獲取當前網(wǎng)路的WifiInfo. wifi 連接成功有效.
    public static WifiInfo getCurrentWifiInfo(Intent intent) {
        return intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
    }
  • WifiManager.RSSI_CHANGED_ACTION: WIFI 熱點信號強度發(fā)生變化的廣播. 可以獲取當前變化熱點的最新的信號強度.
// 獲取當前熱點最新的信號強度
public static int getCurrentNetworkRssi(Intent intent) {
    return intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -1000);
}

小結(jié):

  1. 如上, 針對原生 Android Settings源代碼, 結(jié)合 SDK 中給出的官方 javadoc 整合出, 無線網(wǎng)絡開發(fā)中常用的廣播. WLAN 開放熱點, 是另外的功能, 有所區(qū)別, 在之后會單獨整合出來, 如上, 感謝~~~

  2. 如有疑問, 請簡信, 或郵箱告知. 亦可下方評論區(qū)留言.

  3. qq 郵箱: 1281641968@qq.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怨酝,一起剝皮案震驚了整個濱河市乖仇,隨后出現(xiàn)的幾起案子殉了,更是在濱河造成了極大的恐慌尖滚,老刑警劉巖鹿寨,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件档冬,死亡現(xiàn)場離奇詭異储狭,居然都是意外死亡,警方通過查閱死者的電腦和手機捣郊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門辽狈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呛牲,你說我怎么就攤上這事刮萌。” “怎么了娘扩?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵着茸,是天一觀的道長。 經(jīng)常有香客問我琐旁,道長涮阔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任灰殴,我火速辦了婚禮敬特,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己伟阔,他們只是感情好辣之,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著皱炉,像睡著了一般怀估。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上合搅,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天多搀,我揣著相機與錄音,去河邊找鬼灾部。 笑死康铭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的梳猪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蒸痹,長吁一口氣:“原來是場噩夢啊……” “哼春弥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起叠荠,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤匿沛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后榛鼎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逃呼,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年者娱,在試婚紗的時候發(fā)現(xiàn)自己被綠了抡笼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡黄鳍,死狀恐怖推姻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情框沟,我是刑警寧澤藏古,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站忍燥,受9級特大地震影響拧晕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜梅垄,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一厂捞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦蔫敲、人聲如沸饲嗽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽貌虾。三九已至,卻和暖如春裙犹,著一層夾襖步出監(jiān)牢的瞬間尽狠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工叶圃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留袄膏,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓掺冠,卻偏偏與公主長得像沉馆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子德崭,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容