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 = 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);
}
}
最后附图一张,以便于大家理解。