由对Androidsetting的源码分析之WiFi模块的界面fragment为WiFisettings.java,关于setting模块的源码分析可以参考
Android系统源码剖析(一)---Settings
已经写了几篇关于Android源码的,源码代码量太大,所以如果想分析某个模块可能不知如何下手,说一下思路
1,分析源码英文阅读能力要够,想要分析某个模块一般找模块对应的英文,就是模块
2,找到之后首先查看清单配置文件Androidmani.fest,找到程序主界面activity
3,通过查看配置文件中的权限可以知道应用都有什么用
大致通过以上三步就可以进入源码进行分析
针对WiFi,我们通过清单文件可以知道,需要以下几个权限,其含义如下注释
<!-- 允许程序获取WiFi状态 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!--允许程序改变WiFi状态--> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <!--允许程序获取手机网络状态--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <!--允许程序改变网络状态--> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <!--允许程序访问网络--> <uses-permission android:name="android.permission.INTERNET"/>
如果使用as做开发工具的话在使用WiFi时如果不加权限会有警告来提示你,感觉还是很人性化的,如果把as的所有警告都解决了那么程序应该会很少出意料之外的bug,而且as的单步调试可以很好的去发现bug,界面美观,忍不住强烈推荐。
因为是首次接触WiFi源码,我会按代码顺序分析,当然这样可能会有些乱,完成全部分析后会再按照功能点分析,不过得等到下一篇博客喽。好了,闲话少叙,正式进入对WiFi源码的分析,
1,WifiSettings属于fragment,实现了对话框的点击事件接口,代码如下
public class WifiSettings extends RestrictedSettingsFragment implements DialogInterface.OnClickListener
在onCreateView方法中首先判断应用是否是第一次运行,根据是否是第一次运行来设置不同的布局
1>,if(mSetupWizardMode) 则加载启动布局
View view = inflater.inflate(R.layout.setup_preference, container, false);
否则加载正常布局
View v = inflater.inflate(R.layout.add_preference_list_fragment,null);
接着onActivityCreated方法中
2>
mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
这句代码用来判断是否支持WiFi直连:if the device supports Wi-Fi Direct networking or not
WiFi直连的含义是说:设备可以直接通过WiFi共享文件,图片等,类似蓝牙传输,实现点对点的连接,通信(要求两设备必须位于同一网段)。可以看到判断设备是否支持WiFi直连是通过PackageManager的hasSystemFeature
目录为/android/external/robolectric/src/main/java/com/xtremelabs/robolectric/res/
public boolean hasSystemFeature(String name) { return systemFeatureList.containsKey(name) ? systemFeatureList.get(name) : false; }
由源码可知该方法会遍历systemFeaturelist列表,判断是否包含所传入的feature,通过PackageManagerService中的readPermissionsFromXml(File f)传入xml文件进行解析获取到设备所支持的permission
3>,接下来获取到WiFi的管理类对象mWifiManager,用于开启/关闭WiFi,扫描WiFi,连接WiFi等等
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
4>,通过获取到set_wifi_priority的值来对preference列表进行 不同显示
<pre name="code" class="java">if (getResources().getBoolean(R.bool.set_wifi_priority)) { addPreferencesFromResource(R.xml.wifi_sort_settings); mDefaultTrustAP = (PreferenceCategory)findPreference("default_trust_access_points"); mConfigedAP = (PreferenceCategory)findPreference("configed_access_points"); mUnKnownAP = (PreferenceCategory)findPreference("unknown_access_points"); } else { addPreferencesFromResource(R.xml.wifi_settings); }
对于boolean值“set_wifi_priority”的值可以查看Z:\L7-A1\android\packages\apps\Settings\res\values\bools.xml文件该值表示whether
to show hotspot via the ap‘s classification接入点优先级设定即是否按照所搜索到的WiFi优先级来显示Wifi列表,可以看到等级有三种:默认的信任接入点,信任接入点,未知接入点因为在这里boolean值被设置为false,所以不按优先级排序5>,接下来就是添加WiFi开关,至于通过代码在导航栏actionbar添加switch的代码类似蓝牙,在此不再赘述,可以看到,蓝牙开关switch传入了WifiEnabler,所以对于Wifi的开关的管理位于WiFiEnablerswitch中
mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
进入WiFiEnabler可以看到,该类就做了两件事,第一注册广播监听WiFi的变化并随之改变switch的状态,第二为switch添加点击监听事件
广播所监听的事件有
//当WiFi状态发生改变时会发送该广播 mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); // The order matters! We really should not depend on this. :( mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); //当设备网络状态发生改变时会发送该广播 mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
action WifiManager.SUPPLICANT_STATE_CHANGD_ACTION定义如下,表示正在创建连接的状态发生改变,又有了新的连接可用,可用获取到WiFi的具体的连接状态,如果你只是对连接的整体状态感兴趣则该广播无用
/** * Broadcast intent action indicating that the state of establishing a connection to * an access point has changed.One extra provides the new * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and * is not generally the most useful thing to look at if you are just interested in * the overall state of connectivity. * @see #EXTRA_NEW_STATE * @see #EXTRA_SUPPLICANT_ERROR */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
接下来看广播的处理
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)) { //WiFi状态生改变时去更新switch的状态,WiFi的状态存在于WifiManager.EXTRA_WIFI_STATE handleWifiStateChanged(intent.getIntExtra( WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)); //needPrompt方法用于判断是否是飞行模式以及飞行模式是否无效 if (WifiSettings.needPrompt(context)) { setSwitchChecked(false); } } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { //连接发生改变时的更新,WifiManager.EXTRA_NEW_STATE存放改变后的状态 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()); } } };
当WiFi开关状态发生改变时更新放啊如下:
private void handleWifiStateChanged(int state) { switch (state) { case WifiManager.WIFI_STATE_ENABLING://正在打开WiFi mSwitch.setEnabled(false); break; case WifiManager.WIFI_STATE_ENABLED://WiFi已经打开 setSwitchChecked(true); mSwitch.setEnabled(true); break; case WifiManager.WIFI_STATE_DISABLING://正在关闭WiFi mSwitch.setEnabled(false); break; case WifiManager.WIFI_STATE_DISABLED://WiFi已经关闭 setSwitchChecked(false); mSwitch.setEnabled(true); break; default: setSwitchChecked(false); mSwitch.setEnabled(true); break; } }
对于网络状态发生改变时的更新方法,可以看到方法已经被注释掉,因为不需要去更新preference的副标题
private void handleStateChanged(@SuppressWarnings("unused") NetworkInfo.DetailedState state) { // After the refactoring from a CheckBoxPreference to a Switch, this method is useless since // there is nowhere to display a summary. // This code is kept in case a future change re-introduces an associated text. /* // WifiInfo is valid if and only if Wi-Fi is enabled. // Here we use the state of the switch as an optimization. if (state != null && mSwitch.isChecked()) { WifiInfo info = mWifiManager.getConnectionInfo(); if (info != null) { //setSummary(Summary.get(mContext, info.getSSID(), state)); } } */ }
对于switch的点击事件的监听代码如下
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { //Do nothing if called as a result of a state machine event if (mStateMachineEvent) { return; } if (mContext.getResources().getBoolean(R.bool.wifi_to_cell)) { ConnectivityManager mConnService = (ConnectivityManager) mContext. getSystemService(Context.CONNECTIVITY_SERVICE); if (mConnService != null) { NetworkInfo netInfo = (NetworkInfo) mConnService .getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (netInfo != null && netInfo.isConnected()) { Settings.System.putInt(mContext.getContentResolver(), WIFI_IS_CONNECTED, CONNECTED); } else { Settings.System.putInt(mContext.getContentResolver(), WIFI_IS_CONNECTED, DISCONNECTED); } } } // Show toast message if Wi-Fi is not allowed in airplane mode //判断是否在飞行模式中WiFi是不允许的 if (isChecked && (WifiSettings.needPrompt(mContext) || !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. buttonView.setChecked(false); return; } // Disable tethering if enabling Wifi int wifiApState = mWifiManager.getWifiApState(); if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { mWifiManager.setWifiApEnabled(null, false); } // shouldn't setWifiEnabled(true) in airplane mode. if (isChecked && WifiSettings.needPrompt(mContext)) { return; } else { //setWifiEnabled打开或者关闭WiFi的方法,会发送WiFi状态改变的广播:WIFI_STATE_CHANGED_ACTION if (mWifiManager.setWifiEnabled(isChecked)) { // Intent has been taken into account, disable until new state // is active mSwitch.setEnabled(false); } else { // Error Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show(); } } }<span style="font-size:14px;"> </span>
对于mStateMachineEvent的值是在对switch进行设置时赋值,起开关保护的作用,保证当点击switch的时候先将switch状态设置成功再进入点击事件方法
private void setSwitchChecked(boolean checked) { if (checked != mSwitch.isChecked()) { mStateMachineEvent = true; mSwitch.setChecked(checked); mStateMachineEvent = false; } }
打开或关闭WiFi的方法为,mWifiManager.setWifiEnabled(boolean enable),在打开时会发送广播WifiManager.WIFI_STATE_CHANGED_ACTION,可监听该广播来改变UI