wifi系列部落格位址:
Wifi源碼學習(Android5.1)之wifi開關
Wifi源碼學習(Android5.1)之wifi optionItem
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
如上圖所示,自定義控件互動事件的書寫一般就是這樣。像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);
}
}
最後附圖一張,以便于大家了解。