Android4.4.2源碼分析之WiFi模組(二)

來源:互聯網
上載者:User

標籤:

接著上一篇繼續對WiFi源碼的分析

Android4.4.2源碼分析之WiFi模組(一)

onResume方法中

6>,首先是調用WiFiEnabler的resume方法對switch進行管理

接下來註冊廣播

getActivity().registerReceiver(mReceiver, mFilter);

廣播監聽的action如下

       //wifi狀態改變的action        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);              //WiFi掃描到附近可用WiFi時的廣播        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);             //        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);

查看WiFiManager發現各action定義如下,以及在監聽到對應廣播後各處理如下

i>

 /**     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,     * enabling, disabling, or unknown. One extra provides this state as an int.     * Another extra provides the previous state, if available.     *     * @see #EXTRA_WIFI_STATE     * @see #EXTRA_PREVIOUS_WIFI_STATE     */    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)    public static final String WIFI_STATE_CHANGED_ACTION =        "android.net.wifi.WIFI_STATE_CHANGED";

WIFI_STATE_CHANGED_ACTION:當WiFi被開啟、關閉、正在開啟、正在關閉或者位置狀態即wifi狀態發生改變時系統會自動發送該廣播,該廣播會附帶有兩個值,一個是int型表示改變後的state,可通過欄位EXTRA_WIFI_STATE擷取,還有一個是int型的改變前的state(如果有的話)可通過欄位EXTRA_PREVIOUS_WIFI_STATE擷取

當監聽到該廣播後會進行如下處理:更新WiFi狀態(在WiFiEnbabler中也監聽了該廣播,用於當WiFi狀態改變時對switch進行更新)

 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {            updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,                    WifiManager.WIFI_STATE_UNKNOWN));

updateWifiState方法如下

private void updateWifiState(int state) {        Activity activity = getActivity();        if (activity != null) {        //重新載入菜單  ,該方法會調用activity中的onCreateOptionsMenu載入actionbar         activity.invalidateOptionsMenu();        }        switch (state) {            case WifiManager.WIFI_STATE_ENABLED://開啟WiFi              mScanner.resume();//從下面的方法中可以看到,該方法是用於開啟WiFi的掃描,並記錄掃描次數                return; // not break, to avoid the call to pause() below            case WifiManager.WIFI_STATE_ENABLING://正在開啟WiFi                addMessagePreference(R.string.wifi_starting);                break;            case WifiManager.WIFI_STATE_DISABLED://關閉WiFi              //使用者可以在wlan-->進階選項中去設定時是否隨時都可以掃描(關閉WiFi後也可以掃描),根據使用者的選擇,               //設定在關閉WLAN後顯示介面上的文本              setOffMessage();                break;        }        mLastInfo = null;        mLastState = null;        mScanner.pause();//移除message通知    }

因為更新的方法中涉及到了Scanner,這裡的Scanner是自訂的內部類,繼承自handler,代碼如下

private class Scanner extends Handler {        private int mRetry = 0;        void resume() {            if (!hasMessages(0)) {                sendEmptyMessage(0);            }        }        void forceScan() {            removeMessages(0);            sendEmptyMessage(0);        }        void pause() {            mRetry = 0;            removeMessages(0);        }        @Override        public void handleMessage(Message message) {            if (mWifiManager.startScan()) {                mRetry = 0;            } else if (++mRetry >= 3) {                mRetry = 0;                Activity activity = getActivity();                if (activity != null) {                    Toast.makeText(activity, R.string.wifi_fail_to_scan,                            Toast.LENGTH_LONG).show();                }                return;            }            sendEmptyMessageDelayed(0, 10*1000);//10s後再次發送message        }    }

可以看到,掃描附近可用WiFi的方法為mWifiManager.startScan()該方法對使用者可見,可直接調用

ii>

/**     * An access point scan has completed, and results are available from the supplicant.     * Call {@link #getScanResults()} to obtain the results.     */    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)    public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
SCAN_RESULT_AVAILABLE_ACTION:WiFi掃描結束時系統會發送該廣播,使用者可以監聽該廣播通過調用WifiManager的getScanResults方法來擷取到掃描結果

else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||                WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {                updateAccessPoints();

當使用者掃描到WiFi或者WiFi資訊發生改變時都需要去更新WiFi列表,更新WiFi列表的方法為updateAccessPoints(),掃描載入列表這塊感覺相當複雜,慢慢分析

首先在掃描結束後可以通過getScanResult()擷取到掃描後的WiFi列表,返回值為List<ScanResult>,所返回的每個WiFi會攜帶如下資訊



各欄位分別代表什麼含義呢?

BSSID:The address of the access point,存取點地址(String值)

SSID:The network name,WiFi名字(String值)

capabilities:Describes the authentication,key management,and encryption schemes supported by the access point,描述存取點的身分識別驗證,密鑰管理和加密方案(String值)

wifiSsid:ASCII encode SSID,This will replace SSID when we deprecate it ,SSID的ASCII碼編碼,當不支援它時我們可以用它來代替ssid(WifiSsid值)

timestamp:timestamp in microseconds (since boot)when this result was last seen,距離上一次的更改的微秒數

level:對於level的定義從源碼中可以看到,表示訊號的強度,屬於int型數值,

 /**     * The detected signal level in dBm, also known as the RSSI.     *     * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into     * an absolute signal level which can be displayed to a user.     */    public int level;

對於訊號強度的顯示可以通過如下代碼

mWifiLevel.setImageLevel(WifiManager.calculateSignalLevel(mList.get(position).level,4));

frequency:The primary 20 MHz frequency (in MHz) of the channel over which the client is communicating with the access point.用戶端與存取點通訊的頻率

我們一般顯示WiFi列表用到的是SSID、level和capabilities


Android源碼中掃描到WiFi後就需要去載入列表,在接收到SCAN_RESULT_AVAILABLE_ACTION廣播後調用updateAccessPoints方法進行更新列表,在該方法中會根據WiFi的開關狀態來對UI進行更新,只有在WiFi開啟時 才會去更新列表,這裡不再對其他情況進行贅述,在WiFi開啟時會通過如下代碼載入

 private void updateAccessPoints() {        // Safeguard from some delayed event handling        if (getActivity() == null) return;        if (isRestrictedAndNotPinProtected()) {            addMessagePreference(R.string.wifi_empty_list_user_restricted);            return;        }        final int wifiState = mWifiManager.getWifiState();        switch (wifiState) {            case WifiManager.WIFI_STATE_ENABLED:                // AccessPoints are automatically sorted with TreeSet.                //擷取到存取點列表               final Collection<AccessPoint> accessPoints = constructAccessPoints();                if (!getResources().getBoolean(R.bool.set_wifi_priority)) {                    getPreferenceScreen().removeAll();                }                if(accessPoints.size() == 0) {                    addMessagePreference(R.string.wifi_empty_list_wifi_on);                }                if (!getResources().getBoolean(R.bool.set_wifi_priority)) {                   for (AccessPoint accessPoint : accessPoints) {                       <pre name="code" class="java">                        //WiFisettings的xml檔案的根節點為preferencescreen,所以通過如下方法添加      preferencegetPreferenceScreen().addPreference(accessPoint); } } if (accessPoints.isEmpty()){ addMessagePreference(R.string.wifi_empty_list_wifi_on); } break; case WifiManager.WIFI_STATE_ENABLING://如果WiFi處於正在開啟的狀態,則清除列表 。。。。。。。。。 } }


那麼存取點列表的擷取是如何進行的呢?

 private List<AccessPoint> constructAccessPoints() {        ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();        /** Lookup table to more quickly update AccessPoints by only considering objects with the         * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */          //key為ssid,value為存取點ScanResult      Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();        if (getResources().getBoolean(R.bool.set_wifi_priority)) {            emptyCategory();        }      //getConfiguredNetwors可以返回一個配置列表,擷取到配置好的網路連接,該列表存放了關於已經串連過的存取點WiFi的資訊,          //返回的列表中包括如下欄位,當WiFi 關閉時會返回null           <pre name="code" class="java">         /** <ul>          * <li>networkId</li>          * <li>SSID</li>          * <li>BSSID</li>          * <li>priority</li>          * <li>allowedProtocols</li>          * <li>allowedKeyManagement</li>          * <li>allowedAuthAlgorithms</li>          * <li>allowedPairwiseCiphers</li>          * <li>allowedGroupCiphers</li>          * </ul>          */final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();        if (configs != null) {            for (WifiConfiguration config : configs) {                if (config.SSID != null) {                    AccessPoint accessPoint = new AccessPoint(getActivity(), config);                    accessPoint.update(mLastInfo, mLastState);                    accessPoints.add(accessPoint);                    apMap.put(accessPoint.ssid, accessPoint);                    if (getResources().getBoolean(R.bool.set_wifi_priority)) {                        SetAPCategory(accessPoint, mConfigedAP);                    }                }            }            if (getResources().getBoolean(R.bool.set_wifi_priority)) {                if (mConfigedAP != null && mConfigedAP.getPreferenceCount() == 0) {                    getPreferenceScreen().removePreference(mConfigedAP);                }            }        }         //擷取到WiFi掃描結果,返回附近可用WiFi,包括已經串連的或者已經儲存的WiFi        final List<ScanResult> results = mWifiManager.getScanResults();        if (results != null) {            for (ScanResult result : results) {                // Ignore hidden and ad-hoc networks.                if (result.SSID == null || result.SSID.length() == 0 ||                        result.capabilities.contains("[IBSS]")) {                    continue;                }                boolean found = false;                for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {                    if (accessPoint.update(result))                        found = true;                }                if (!found) {                    AccessPoint accessPoint = new AccessPoint(getActivity(), result);                    accessPoints.add(accessPoint);                    apMap.put(accessPoint.ssid, accessPoint);                    if (getResources().getBoolean(R.bool.set_wifi_priority)) {                        SetAPCategory(accessPoint, mUnKnownAP);                    }                }            }            if (getResources().getBoolean(R.bool.set_wifi_priority)) {                if(mUnKnownAP !=null && mUnKnownAP.getPreferenceCount() == 0){                    getPreferenceScreen().removePreference(mUnKnownAP);                }            }        }        // Pre-sort accessPoints to speed preference insertion        Collections.sort(accessPoints);        return accessPoints;    }


調用mWifiManager.getConfigureNetworks()方法擷取到的是已經配置過串連過的WiFi列表,列表包含下列值




對於掃描到的WiFi的保護方式通過判斷scanresult的capabilities欄位是否包含對應的string來判斷屬於何種保護方式

 boolean wpa = result.capabilities.contains("WPA-PSK");        boolean wpa2 = result.capabilities.contains("WPA2-PSK")



iii>

/**     * The network IDs of the configured networks could have changed.     */    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)    public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
NETWORK_IDS_CHANGED:所配置的網路的網路ID可能已經改變


iv>

  /**     * Broadcast intent action indicating that the state of establishing a connection to     * an access point has changed.One extra provides the new     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and     * is not generally the most useful thing to look at if you are just interested in     * the overall state of connectivity.     * @see #EXTRA_NEW_STATE     * @see #EXTRA_SUPPLICANT_ERROR     */    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)    public static final String SUPPLICANT_STATE_CHANGED_ACTION =        "android.net.wifi.supplicant.STATE_CHANGE";


SUPPLICANT_STATE_CHANGED_ACTION:正在建立的串連狀態已經改變,該廣播會攜帶兩個值


v>

 /**     * Broadcast intent action indicating that the configured networks changed.     * This can be as a result of adding/updating/deleting a network. If     * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set to true the new configuration     * can be retreived with the {@link #EXTRA_WIFI_CONFIGURATION} extra. If multiple     * Wi-Fi configurations changed, {@link #EXTRA_WIFI_CONFIGURATION} will not be present.     * @hide     */    public static final String CONFIGURED_NETWORKS_CHANGED_ACTION =        "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";

CONFIGURED_NETWORKS_CHANGED_ACTION:當WiFi列表中的網路添加、更新或者刪除時系統會發送該廣播,但是該廣播對使用者隱藏,無法調用


vi>

/**     * Broadcast intent action indicating that the state of Wi-Fi connectivity     * has changed. One extra provides the new state     * in the form of a {@link android.net.NetworkInfo} object. If the new     * state is CONNECTED, additional extras may provide the BSSID and WifiInfo of     * the access point.     * as a {@code String}.     * @see #EXTRA_NETWORK_INFO     * @see #EXTRA_BSSID     * @see #EXTRA_WIFI_INFO     */    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)    public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";


NETWORK_STATE_CHANGED_ACTION:WiFi串連發生改變時系統會發送該廣播,通過欄位EXTRA_NETWORK_INFO可以擷取到WiFi串連的狀態,如果是已串連的狀態,則會有額外的兩個欄位,欄位EXTRA_BSSID可以擷取到所串連的WiFi的bssid,欄位EXTRA_WIFI_INFO可以擷取到所串連的WiFi的資訊擷取到wifiinfo執行個體


vii>

 /**     * The RSSI (signal strength) has changed.     * @see #EXTRA_NEW_RSSI     */    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)    public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";

WIFI_RSSI_CHANGED:當WiFi訊號強度發生改變時系統會發送該廣播,通過欄位EXTRA_NEW_RSSI可以擷取到改變後的wifi訊號強度,當然也需要去更新WiFi列表



Android4.4.2源碼分析之WiFi模組(二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.