最近项目中需要用到Android WiFi相关功能,而API文档中对这方面语焉不详,所以决定来看一看Google工程师是怎么写的。
WiFi Setting App 在packages_app_settings中。从Github上下载下源码后,找到WifiSettings.java文件(这里推荐下listary这个功能强大的文件查找操作软件),这就是WIFI设置的所在了。
首先看下它的继承链,WifiSettings extends RestrictedSettingsFragment,RestrictedSettingsFragment extends SettingsPreferenceFragment。SettingsPreferenceFragment也被很多App用在自己的设置页面。
继续看它的构造函数:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } |
new了个BroadcastReceiver接收一系列广播。WIFI_STATE_CHANGED_ACTION事件当WIFI功能开启或关闭时会收到,SCAN_RESULTS_AVAILABLE_ACTION事件当手机扫描到有可用的WIFI连接时会收到,SUPPLICANT_STATE_CHANGED_ACTION事件当连接请求状态发生改变时会收到,NETWORK_STATE_CHANGED_ACTION事件当网络状态发生变化时会收到。Broadcast Receiver中使用handleEvent()来处理接收到的广播。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | private void handleEvent(Intent intent) { String action = intent.getAction(); if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)); //WIFI功能开启或关闭或正在开启/正在关闭/未知 //则更新WIFI状态 } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) { updateAccessPoints(); //ScanResults有结果了 或 添加/更新/删除了一个网络 或 连接的网络配置改变(?) //则更新APs } 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); //WIFI网络连接的变化 // } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { updateNetworkInfo(null); //正在连接的WIFI信号强度变化 } } |
里面接受广播后调用了相应的几个函数来更新相关状态,有updateWifiState(), updateAccessPoints(), updateNetworkInfo()。
先来看updateAccessPoints()
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | private void updateAccessPoints() { // Safeguard from some delayed event handling if (getActivity() == null) return;
if (isUiRestricted()) { //ezio: WTF? addMessagePreference(R.string.wifi_empty_list_user_restricted); return; } final int wifiState = mWifiManager.getWifiState();
//when we update the screen, check if verbose logging has been turned on or off mVerboseLogging = mWifiManager.getVerboseLoggingLevel();
switch (wifiState) { case WifiManager.WIFI_STATE_ENABLED: // AccessPoints are automatically sorted with TreeSet. final Collection<accesspoint> accessPoints = //ezio constructAccessPoints(getActivity(), mWifiManager, mLastInfo, mLastNetworkInfo); getPreferenceScreen().removeAll(); if (accessPoints.size() == 0) { //ezio:如果空,显示提示信息 addMessagePreference(R.string.wifi_empty_list_wifi_on); }
for (AccessPoint accessPoint : accessPoints) { // Ignore access points that are out of range. //ezio:如果accessPoints不为空,则弄一个PrefList装进去,忽略范围外的ap if (accessPoint.getLevel() != -1) { getPreferenceScreen().addPreference(accessPoint); } } break;
case WifiManager.WIFI_STATE_ENABLING: getPreferenceScreen().removeAll(); break;
case WifiManager.WIFI_STATE_DISABLING: addMessagePreference(R.string.wifi_stopping); break;
case WifiManager.WIFI_STATE_DISABLED: setOffMessage(); break; } }</accesspoint> |
updateAccessPoints()当广播是WIFI已开启的时候,会构造APs,APs是放满AP的Collection。
找到constructAccessPoints()的实现,上方的注释已经给了我们提示,它能返回排序好的APs
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /** Returns sorted list of access points */ private static List<accesspoint> constructAccessPoints(Context context, WifiManager wifiManager, WifiInfo lastInfo, NetworkInfo lastNetworkInfo) { ArrayList<accesspoint> accessPoints = new ArrayList<accesspoint>(); /** Lookup table to more quickly update AccessPoints by only considering objects with the * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */
//multimap是Guava: Google Core Libraries的api Multimap<string, accesspoint=""> apMap = new Multimap<string, accesspoint="">();
final List<wificonfiguration> configs = wifiManager.getConfiguredNetworks(); if (configs != null) { //如果有configuredNetwork // Update "Saved Networks" menu option. if (savedNetworksExist != (configs.size() > 0)) { //如果savedNetworksExist为false,则反转 savedNetworksExist = !savedNetworksExist; if (context instanceof Activity) {
((Activity) context).invalidateOptionsMenu(); } } for (WifiConfiguration config : configs) { if (config.selfAdded && config.numAssociation == 0) {
continue; }
AccessPoint accessPoint = new AccessPoint(context, config); if (lastInfo != null && lastNetworkInfo != null) { accessPoint.update(lastInfo, lastNetworkInfo); } accessPoints.add(accessPoint); apMap.put(accessPoint.ssid, accessPoint); } }
final List<scanresult> results = wifiManager.getScanResults(); if (results != null) { for (ScanResult result : results) { // Ignore hidden and ad-hoc networks. if (result.SSID == null || result.SSID.length() == 0 || result.capabilities.contains("[IBSS]")) { continue; //continue:如果ssid==null或者length==0,则忽略这一个直接进行下一次for循环 }
boolean found = false; for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { //ezio:apMap? if (accessPoint.update(result)) found = true; } if (!found) { AccessPoint accessPoint = new AccessPoint(context, result); if (lastInfo != null && lastNetworkInfo != null) { accessPoint.update(lastInfo, lastNetworkInfo); } accessPoints.add(accessPoint); apMap.put(accessPoint.ssid, accessPoint); } } }
// Pre-sort accessPoints to speed preference insertion Collections.sort(accessPoints); return accessPoints; }</scanresult></wificonfiguration></string,></string,></accesspoint></accesspoint></accesspoint> |
其中用到了另一个类AccessPoint.java
在包中找到AccessPoint.java类,它有三个构造函数:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | AccessPoint(Context context, WifiConfiguration config) { super(context); loadConfig(config); refresh(); }
AccessPoint(Context context, ScanResult result) { super(context); loadResult(result); refresh(); }
AccessPoint(Context context, Bundle savedState) { super(context);
mConfig = savedState.getParcelable(KEY_CONFIG); if (mConfig != null) { loadConfig(mConfig); } mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT); if (mScanResult != null) { loadResult(mScanResult); } mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO); if (savedState.containsKey(KEY_NETWORKINFO)) { mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO); } update(mInfo, mNetworkInfo); }
private void loadConfig(WifiConfiguration config) { //如果SSID为null则将其赋值为空字符串,不为空则。。 ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); bssid = config.BSSID; security = getSecurity(config); networkId = config.networkId; mConfig = config; }
private void loadResult(ScanResult result) { ssid = result.SSID; bssid = result.BSSID; security = getSecurity(result); wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS"); if (security == SECURITY_PSK) pskType = getPskType(result); mRssi = result.level; mScanResult = result; if (result.seen > mSeen) { mSeen = result.seen; } } |
AP类构造函数中调用loadConfig和loadResult两个函数来初始化成员变量,然后调用refresh来刷新。
接下来看用户点击WIFI会发生什么。在WifiSetting中找到
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @Override public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { if (preference instanceof AccessPoint) { mSelectedAccessPoint = (AccessPoint) preference; //bypass:绕过 /** Bypass dialog for unsecured, unsaved, and inactive networks */ if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE && mSelectedAccessPoint.networkId == INVALID_NETWORK_ID && !mSelectedAccessPoint.isActive()) { //生成一个不加密的WifiConfigration mSelectedAccessPoint.generateOpenNetworkConfig(); if (!savedNetworksExist) { //如果不存在 savedNetworksExist = true; //改为存在 getActivity().invalidateOptionsMenu(); //重新生成OptionsMenu } connect(mSelectedAccessPoint.getConfig()); } else { showDialog(mSelectedAccessPoint, false); } } else { return super.onPreferenceTreeClick(screen, preference); } return true; } |
再追入AP中的generateOpenNetworkConfig
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | /** * Generate and save a default wifiConfiguration with common values. * Can only be called for unsecured networks. * @hide */ protected void generateOpenNetworkConfig() { if (security != SECURITY_NONE) throw new IllegalStateException(); if (mConfig != null) return; mConfig = new WifiConfiguration(); mConfig.SSID = AccessPoint.convertToQuotedString(ssid); mConfig.allowedKeyManagement.set(KeyMgmt.NONE); } |
会生成一个开放网络的Config
接下来会调用connect连接选中的WIFI
[代码]java代码:
1 2 3 4 5 6 7 | protected void connect(final WifiConfiguration config) { mWifiManager.connect(config, mConnectListener); }
protected void connect(final int networkId) { mWifiManager.connect(networkId, mConnectListener); } |
找到WifiManager中的connect方法,喜闻乐见的是hide方法,API中没有
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | /** * Connect to a network with the given configuration. The network also * gets added to the supplicant configuration. * * For a new network, this function is used instead of a * sequence of addNetwork(), enableNetwork(), saveConfiguration() and * reconnect() * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * * @hide */ public void connect(WifiConfiguration config, ActionListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); validateChannel(); // Use INVALID_NETWORK_ID for arg1 when passing a config object // arg1 is used to pass network id when the network already exists sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID, putListener(listener), config); }
/** * Connect to a network with the given networkId. * * This function is used instead of a enableNetwork(), saveConfiguration() and * reconnect() * * @param networkId the network id identifiying the network in the * supplicant configuration list * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again * @hide */ public void connect(int networkId, ActionListener listener) { if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative"); validateChannel(); sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener)); } |
然后回到WifiSettings中的onPreferenceTreeClick,如果不是开放网络,会调用showDialog()
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 | private void showDialog(AccessPoint accessPoint, boolean edit) { if (mDialog != null) { removeDialog(WIFI_DIALOG_ID); mDialog = null; }
// Save the access point and edit mode mDlgAccessPoint = accessPoint; mDlgEdit = edit;
showDialog(WIFI_DIALOG_ID); } |
最后一句调用了爷爷类的showDialog()
[代码]java代码:
1 2 3 4 5 6 7 | protected void showDialog(int dialogId) { if (mDialogFragment != null) { Log.e(TAG, "Old dialog fragment not null!"); } mDialogFragment = new SettingsDialogFragment(this, dialogId); mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId)); } |
SettingsDialogFragment是SettingsPreferenceFragment中的一个静态内部类,继承自DialogFragment
下方还有个onCreateDialog()
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @Override public Dialog onCreateDialog(int dialogId) { switch (dialogId) { case WIFI_DIALOG_ID: AccessPoint ap = mDlgAccessPoint; // For manual launch if (ap == null) { // For re-launch from saved state if (mAccessPointSavedState != null) { ap = new AccessPoint(getActivity(), mAccessPointSavedState); // For repeated orientation changes mDlgAccessPoint = ap; // Reset the saved access point data mAccessPointSavedState = null; } } // If it's null, fine, it's for Add Network mSelectedAccessPoint = ap; mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit); return mDialog; case WPS_PBC_DIALOG_ID: return new WpsDialog(getActivity(), WpsInfo.PBC); case WPS_PIN_DIALOG_ID: return new WpsDialog(getActivity(), WpsInfo.DISPLAY); case WRITE_NFC_DIALOG_ID: if (mSelectedAccessPoint != null) { mWifiToNfcDialog = new WriteWifiConfigToNfcDialog( getActivity(), mSelectedAccessPoint, mWifiManager); return mWifiToNfcDialog; }
} return super.onCreateDialog(dialogId); } |
这回Google的攻城狮终于肯写点注释了,看起来顺了很多。想检查mDlgAccessPoint是否为null,如果null就从SavedState生成个新的ap,最后new一个WifiDialog,这个Dialog就是我们将要看到的Dialog。
在包中找到WifiDialog.java,它有两个构造函数(带默认则是三个)
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | public WifiDialog(Context context, DialogInterface.OnClickListener listener, AccessPoint accessPoint, boolean edit, boolean hideSubmitButton) { this(context, listener, accessPoint, edit); mHideSubmitButton = hideSubmitButton; }
public WifiDialog(Context context, DialogInterface.OnClickListener listener, AccessPoint accessPoint, boolean edit) { super(context); mEdit = edit; mListener = listener; mAccessPoint = accessPoint; mHideSubmitButton = false; } |
可以看到之前调用的是4参数的,第二个参数是 DialogInterface.OnClickListener,之前传入的this,那我们去WifiSettings中找到实现的接口的函数
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 | @Override public void onClick(DialogInterface dialogInterface, int button) { if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { forget(); } else if (button == WifiDialog.BUTTON_SUBMIT) { if (mDialog != null) { submit(mDialog.getController()); } } } |
很简单,判断了点击的是忘记网络还是连接WIFI。看下submit的代码
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* package */ void submit(WifiConfigController configController) {
final WifiConfiguration config = configController.getConfig();
if (config == null) { if (mSelectedAccessPoint != null && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { connect(mSelectedAccessPoint.networkId); } } else if (config.networkId != INVALID_NETWORK_ID) { if (mSelectedAccessPoint != null) { mWifiManager.save(config, mSaveListener); } } else { if (configController.isEdit()) { mWifiManager.save(config, mSaveListener); } else { connect(config); } }
if (mWifiManager.isWifiEnabled()) { mScanner.resume(); } updateAccessPoints(); } |
共同学习,写下你的评论
评论加载中...
作者其他优质文章