Wifi源码学习(Android5.1)之wifi开关

wifi系列博客地址:

Wifi源码学习(Android5.1)之wifi开关
Wifi源码学习(Android5.1)之wifi optionItem
Wifi源码学习(Android5.1)之wifi列表

正文:

老方法,从界面入手:
Wifi源码学习(Android5.1)之wifi开关_第1张图片

Wifi源码学习(Android5.1)之wifi开关_第2张图片

现在我们看到的这两个界面就是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开关_第3张图片

如上图所示,自定义控件交互事件的书写一般就是这样。像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 = 0;
    private WifiSettings mWifiSettings = null;
    Scanner(WifiSettings wifiSettings) {
        mWifiSettings = wifiSettings;
    }
    //我们一路调用到了这儿
    //原来是自己调用自己,停止搜索和强制搜索方法及逻辑都在这儿
    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) {
        // 如果扫描启动成功则走到最后一句,十秒后继续扫描
        // 如果开启扫描失败,则走 else ,连续三次失败,就不再尝试扫描,弹出Toast
        // 这段代码应该是开关这一块相对较难理解的一部分了,但是写法值得我们去借鉴
        if (mWifiSettings.mWifiManager.startScan()) {
            mRetry = 0;
        } else if (++mRetry >= 3) {
            mRetry = 0;
            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(0, WIFI_RESCAN_INTERVAL_MS);
    }
}

最后附图一张,以便于大家理解。

Wifi源码学习(Android5.1)之wifi开关_第4张图片

你可能感兴趣的:(android系统开发)