Wifi原始碼學習(Android5.1)之wifi開關
阿新 • • 發佈:2019-01-10
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
如上圖所示,自定義控制元件互動事件的書寫一般就是這樣。像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);
}
}
最後附圖一張,以便於大家理解。