天天看點

Wifi源碼學習(Android5.1)之wifi開關

wifi系列部落格位址:

Wifi源碼學習(Android5.1)之wifi開關

Wifi源碼學習(Android5.1)之wifi optionItem

Wifi源碼學習(Android5.1)之wifi清單

正文:

老方法,從界面入手:

Wifi源碼學習(Android5.1)之wifi開關
Wifi源碼學習(Android5.1)之wifi開關

現在我們看到的這兩個界面就是android5.1 的wifi 設定界面了,我們就從這兒入手。

我們可以看到這個界面大概分為三個部分

1、開關

2、option items

3、清單

一、開關:

這個開關是一個自定義控件,在源碼中這種重用自定義控件特别多,也值得我們去借鑒。

Settings/res/layout/switch_bar.xml

<com.android.settings.widget.ToggleSwitch android:id="@+id/switch_widget"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:background="@null"
        android:theme="@style/ThemeOverlay.SwitchBar" />
           

具體的細節,後續部落格再說,我們隻看 wifi開關 邏輯部分。

加載這個布局的地方是 SwitchBar

Settings\src\com\android\settings\widget\SwitchBar.java

Wifi源碼學習(Android5.1)之wifi開關

如上圖所示,自定義控件互動事件的書寫一般就是這樣。像button的單擊事件等都是按照這樣的方式實作的。

Settings\src\com\android\settings\wifi\WifiEnabler.java

這個方法就是接收 開關狀态回調的方法

@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
    //Do nothing if called as a result of a state machine event
// 如果是代碼中 修改開關狀态(也就是人沒有點選),則不走下邊的邏輯
    if (mStateMachineEvent) {
        return;
    }
    // Show toast message if Wi-Fi is not allowed in airplane mode
    // 寫的很清楚,如果在 飛行模式下,wifi 無法打開
    if (isChecked && !WirelessSettings.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {
        Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
        // Reset switch to off. No infinite check/listenenr loop.
        mSwitchBar.setChecked(false);
        return;
    }

    // Disable tethering if enabling Wifi
    int wifiApState = mWifiManager.getWifiApState();
// 界面顯示為打開狀态,并且 wifi 實際上是 正在打開或打開狀态,則将 wifi 共享關閉
    if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
            (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
        mWifiManager.setWifiApEnabled(null, false);
    }
      // 要開始改變 wifi 的狀态,因為是一個耗時操作,此時暫時将 開關置為不可點選的狀态,這種寫   
      //法我們在平時寫用戶端的時候也可以借鑒
    mSwitchBar.setEnabled(false);
    //判斷中的這句為真正的修改wifi的狀态,如果失敗,則彈框提示
    if (!mWifiManager.setWifiEnabled(isChecked)) {
        // Error
        mSwitchBar.setEnabled(true);
        Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
    }
 //這個地方需要注意,此時開關無法點選,而設定成功後也沒有在這兒恢複其點選狀态,其實這也是系統常用
 //的一種寫法,為了防止使用者不斷點選出現一些錯誤。我們繼續看
}
           

恢複點選狀态的方法其實是寫到了 wifi 狀态改變的回調中,所謂各司其職吧。

private void handleWifiStateChanged(int state) {
    switch (state) {
        case WifiManager.WIFI_STATE_ENABLING:
            mSwitchBar.setEnabled(false);
            break;
        case WifiManager.WIFI_STATE_ENABLED:
            setSwitchBarChecked(true);
            mSwitchBar.setEnabled(true);
            updateSearchIndex(true);
            break;
           

那系統是怎麼樣知道wifi狀态的變化呢?答案就是廣播

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            handleWifiStateChanged(intent.getIntExtra(
                    WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
            if (!mConnected.get()) {
                handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                        intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            mConnected.set(info.isConnected());
            handleStateChanged(info.getDetailedState());
        }
    }
};
           

都調用了 handleStateChanged() 方法

private void handleWifiStateChanged(int state) {
    switch (state) {
        case WifiManager.WIFI_STATE_ENABLING://正在打開
            mSwitchBar.setEnabled(false);
            break;
        case WifiManager.WIFI_STATE_ENABLED://已經打開
            setSwitchBarChecked(true);
            mSwitchBar.setEnabled(true);
            updateSearchIndex(true);
            break;
        case WifiManager.WIFI_STATE_DISABLING://正在關閉
            mSwitchBar.setEnabled(false);
            break;
        case WifiManager.WIFI_STATE_DISABLED://已經關閉
            setSwitchBarChecked(false);
            mSwitchBar.setEnabled(true);
            updateSearchIndex(false);
            break;
        default:
            setSwitchBarChecked(false);
            mSwitchBar.setEnabled(true);
            updateSearchIndex(false);
    }
}

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_UPDATE_INDEX:
                final boolean isWiFiOn = msg.getData().getBoolean(EVENT_DATA_IS_WIFI_ON);
                Index.getInstance(mContext).updateFromClassNameResource(
                        WifiSettings.class.getName(), true, isWiFiOn);
                break;
        }
    }
};
           

發現這兒并沒有進行wifi的搜尋操作什麼的(因為在界面中wifi開關打開會自動進行搜尋),但是在這兒沒有做搜尋處理也可以了解,各司其職嘛。

再找找看哪兒還有接收wifi狀态廣播的地方,果然,功夫不負有心人

Settings\src\com\android\settings\wifi\WifiSettings.java

public WifiSettings() {
    super(DISALLOW_CONFIG_WIFI);
    mFilter = new IntentFilter();
    mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    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);
// 在這兒我們跳過注冊廣播的代碼
    mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            handleEvent(intent);
        }
    };
    mScanner = new Scanner(this);
}

private void handleEvent(Intent intent) {
    String action = intent.getAction();
    //我們暫時隻看和wifi開關狀态相關的這個判斷語句(也就是第一個)
    if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
        updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                WifiManager.WIFI_STATE_UNKNOWN));
    } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
            WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
            WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
            updateAccessPoints();
    } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
        NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                WifiManager.EXTRA_NETWORK_INFO);
        mConnected.set(info.isConnected());
        changeNextButtonState(info.isConnected());
        updateAccessPoints();
        updateNetworkInfo(info);
    } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
        updateNetworkInfo(null);
    }
}

private void updateWifiState(int state) {
    Activity activity = getActivity();
    if (activity != null) {
        activity.invalidateOptionsMenu();
    }
    switch (state) {
        case WifiManager.WIFI_STATE_ENABLED:
            // wifi 被打開了
            mScanner.resume();
            return; // not break, to avoid the call to pause() below

        case WifiManager.WIFI_STATE_ENABLING:
            addMessagePreference(R.string.wifi_starting);
            break;

        case WifiManager.WIFI_STATE_DISABLED:
            setOffMessage();
            break;
    }
    mLastInfo = null;
    mLastNetworkInfo = null;
    mScanner.pause();
}

private static class Scanner extends Handler {
    private int mRetry = ;
    private WifiSettings mWifiSettings = null;
    Scanner(WifiSettings wifiSettings) {
        mWifiSettings = wifiSettings;
    }
    //我們一路調用到了這兒
    //原來是自己調用自己,停止搜尋和強制搜尋方法及邏輯都在這兒
    void resume() {
        if (!hasMessages()) {
            sendEmptyMessage();
        }
    }
    void forceScan() {
        removeMessages();
        sendEmptyMessage();
    }
    void pause() {
        mRetry = ;
        removeMessages();
    }
    @Override
    public void handleMessage(Message message) {
        // 如果掃描啟動成功則走到最後一句,十秒後繼續掃描
        // 如果開啟掃描失敗,則走 else ,連續三次失敗,就不再嘗試掃描,彈出Toast
        // 這段代碼應該是開關這一塊相對較難了解的一部分了,但是寫法值得我們去借鑒
        if (mWifiSettings.mWifiManager.startScan()) {
            mRetry = ;
        } else if (++mRetry >= ) {
            mRetry = ;
            Activity activity = mWifiSettings.getActivity();
            if (activity != null) {
                Toast.makeText(activity, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
            }
            return;
        }
        // WIFI_RESCAN_INTERVAL_MS = 10 * 1000 也就是十秒
        sendEmptyMessageDelayed(, WIFI_RESCAN_INTERVAL_MS);
    }
}
           

最後附圖一張,以便于大家了解。

Wifi源碼學習(Android5.1)之wifi開關