7.3.1 WifiP2pSettings工作流程

Android平台中,P2P操作特别简单,用户只需执行如下三个步骤。

1)进入WifiP2pSettings界面。

2)搜索周围的P2P设备。搜索到的设备将显示在WifiP2pSettings中。

3)用户选择其中的某个设备以发起连接。

下面将根据上面的使用步骤来分析WifiP2pSettings。首先来看WifiP2pSettings的onActivityCreate函数。

1.WifiP2pSettings创建

WifiP2pSettings的onActivityCreated函数代码如下所示。

[—>WifiP2pSettings.java::onActivityCreated]

  1. public void onActivityCreated(Bundle savedInstanceState) {
  2. addPreferencesFromResource(R.xml.wifi_p2p_settings);// 加载界面元素
  3. /*
  4. 和第5章介绍的WifiSettings类似,WifiP2pSettings也是通过监听广播的方式来了解系统中
  5. Wi-Fi P2P相关的信息及变化情况。下面这几个广播属于P2P特有的,其作用如下。
  6. WIFI_P2P_STATE_CHANGED_ACTION:用于通知系统中P2P功能的启用情况,如该功能是enable还是disable。
  7. WIFI_P2P_PEERS_CHANGED_ACTION:系统内部将保存搜索到的其他P2P设备信息,如果这些信息有变化,
  8. 则系统将发送该广播。接收者需要通过WifiP2pManager的requestPeers函数重新获取这些P2P设备的信息。
  9. WIFI_P2P_CONNECTION_CHANGED_ACTION:用于通知P2P连接情况,该广播可携带WifiP2pInfo
  10. 和NetworkInfo两个对象。相关信息可从这两个对象中获取。
  11. WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:用于通知本机P2P设备信息发生了变化。
  12. WIFI_P2P_DISCOVERY_CHANGED_ACTION:用于通知P2P Device Discovery的工作状态,如启动或停止。
  13. WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:用于通知之persistent group信息发生了变化。
  14. */
  15. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
  16. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
  17. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
  18. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
  19. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
  20. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
  21.  
  22. final Activity activity = getActivity();
  23. // 创建WifiP2pManager对象,它将和WifiP2pService交互
  24. mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
  25. if (mWifiP2pManager != null) {
  26. // 初始化WifiManager并建立和WifiService的联系
  27. mChannel = mWifiP2pManager.initialize(activity, getActivity().getMainLooper(),null);
  28. }
  29. ......
  30. ......// 创建UI中按钮对应的onClickListener
  31. mRenameListener = new OnClickListener() {......};
  32. ......
  33. super.onActivityCreated(savedInstanceState);
  34. }

WifiP2pSettings将在onResume中注册一个广播接收对象以监听上面代码中介绍的广播事件。这部分代码很简单,请读者自行阅读。

2.WifiP2pSettings工作流程

(1)WIFI_P2P_STATE_CHANGED_ACTION处理流程

打开WifiP2pSettings后,首先要等待WIFI_P2P_STATE_CHANGED_ACTION广播以判断P2P功能是否正常启动。相应的处理函数如下所示。

[—>WifiP2pSettings.java::onReceive]

  1. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  2. public void onReceive(Context context, Intent intent) {
  3. String action = intent.getAction();
  4. if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
  5. // 从WIFI_P2P_STATE_CHANGED_ACTION广播中获取相关状态信息以判断P2P功能是否打开
  6. mWifiP2pEnabled = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
  7. WifiP2pManager.WIFI_P2P_STATE_DISABLED) ==
  8. WifiP2pManager.WIFI_P2P_STATE_ENABLED;
  9. handleP2pStateChanged();
  10. }
  11. ......
  12. }

来看handleP2pStateChange函数,代码如下所示。

[—>WifiP2pSettings.java::handleP2pStateChanged]

  1. private void handleP2pStateChanged() {
  2. updateSearchMenu(false);// 该函数将触发WifiP2pSettings的onCreateOptionsMenu被调用
  3. if (mWifiP2pEnabled) {
  4. ......
  5. /*
  6. 获取系统当前已经搜索到或者之前保存的P2P Device信息列表。Android为此定义了一个名为
  7. WifiP2pDeviceList的数据类型用来存储这些P2P Device信息。
  8. 请读者注意requestPeers函数调用的第二个参数,该参数的类型为PeerListener,它是一个
  9. 接口类,而WifiP2pSettings实现了它。WifiP2pDeviceList信息将通过这个接口类的
  10. onPeersAvailable函数返回给requestPeers的调用者。
  11. 后文将分析onPeersAvailable函数,此处先略过。
  12. */
  13. mWifiP2pManager.requestPeers(mChannel, WifiP2pSettings.this);
  14. }
  15. }

根据上文的介绍,用户下一步要做的事情就是主动搜索周围的P2P设备。Android原生代码中的WifiP2pSettings界面下方有两个按钮,分别是"SEARCH"和"RENAME"。

·"RENAME"用于更改本机的P2P设备名。

·"SEARCH"用于搜索周围的P2P Device。

当P2P功能正常启用后(即上述代码中的mWifiP2pEnabled为true时),这两个按钮将被使能。此后,用户就可单击"SEARCH"按钮以搜索周围的P2P设备。该按钮对应的函数是startSearch,马上来看它。

提示 一些手机厂商对WifiP2pSettings界面有所更改,但大体流程没有变化。

(2)startSearch函数

startSearch的代码如下所示。

[—>WifiP2pSettings.java::startSearch]

  1. private void startSearch() {
  2. if (mWifiP2pManager != null && !mWifiP2pSearching) {
  3. // discoverPeers将搜索周围的P2P设备
  4. mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
  5. ......});
  6. }
  7. }

上述代码中,WifiP2pSettings通过调用WifiManager的discoverPeers来搜索周围的设备。

当WPAS完成搜索后,WIFI_P2P_PEERS_CHANGED_ACTION广播将被发送。来看WifiP2pSettings中对该广播的处理。

(3)WIFI_P2P_PEERS_CHANGED_ACTION处理流程

广播事件在onReceive函数中被处理,此函数的代码如下所示。

[—>WifiP2pSettings.java::onReceive]

  1. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  2. public void onReceive(Context context, Intent intent) {
  3. String action = intent.getAction();
  4. ......
  5. // 如果搜索到新的P2P Device,则WIFI_P2P_PEERS_CHANGED_ACTION将被发送
  6. } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
  7. mWifiP2pManager.requestPeers(mChannel, WifiP2pSettings.this);
  8. }......
  9. else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
  10. int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,
  11. WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
  12. if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED)
  13. updateSearchMenu(true);// 更新“SEARCH”按钮显示的名称
  14. else updateSearchMenu(false);
  15. }......
  16. }
  17. };

注意,startSearch还将触发系统发送WIFI_P2P_DISCOVERY_CHANGED_ACTION广播,WifiP2pSettings将根据该广播携带的信息来更新"SEARCH"按钮的界面,其规则是:如果P2P Discovery启动成功(即状态变为WIFI_P2P_DISCOVERY_STARTED),则"SEARCH"按钮名显示为"Searching…",否则该按钮名显示为"Search For Devices"。

当系统搜索到新的P2P Device后,WIFI_P2P_PEERS_CHANGED_ACTION广播将被发送,而WifiP2pSettings对于该广播的处理就是调用WifiP2pManager的requestPeers来获取系统保存的P2P Device信息列表。

前文代码中曾介绍过requestPeers,系统中所有的P2P设备信息将通过PeerListener接口类的onPeersAvailable函数返回给WifiP2pSettings。直接来看该函数,代码如下所示。

[—>WifiP2pSettings.java::onPeersAvailable]

  1. public void onPeersAvailable(WifiP2pDeviceList peers) {
  2. // 系统中所有的P2P设备信息都保存在这个类型为WifiP2pDeviceList的peers对象中
  3. mPeersGroup.removeAll();// mPeersGroup类型为PerferenceGroup,属于UI相关的类
  4. mPeers = peers;
  5. mConnectedDevices = 0;
  6. for (WifiP2pDevice peer: peers.getDeviceList()) {
  7. // WifiP2pPeer是Perference的子类,它和UI相关
  8. mPeersGroup.addPreference(new WifiP2pPeer(getActivity(), peer));
  9. if (peer.status == WifiP2pDevice.CONNECTED) mConnectedDevices++;
  10. }
  11. }

在onPeersAvailable函数中,WifiP2pDeviceList中保存的每一个WifiP2pDevice信息将作为一个Preference项添加到mPeersGroup中并显示在UI界面上。

接下来,用户就可在界面中选择某个P2P Device并与之连接。这个步骤由onPreferenceTreeClick函数来完成。

提示 上述代码中提到的WifiP2pDevice等数据结构将放到下文再介绍。

(4)onPreferenceTreeClick函数

onPreferenceTreeClick的代码如下所示。

[—>WifiP2pSettings.java::onPreferenceTreeClick]

  1. public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
  2. if (preference instanceof WifiP2pPeer) {
  3. mSelectedWifiPeer = (WifiP2pPeer) preference;// 获取用户指定的那一个WifiP2pPeer项
  4. if (mSelectedWifiPeer.device.status == WifiP2pDevice.CONNECTED)
  5. showDialog(DIALOG_DISCONNECT);// 如果已经和该Device连接,则判断是否需要与之断开连接
  6. else if (mSelectedWifiPeer.device.status == WifiP2pDevice.INVITED)
  7. showDialog(DIALOG_CANCEL_CONNECT);
  8. else {// 向对端P2P设备发起连接
  9. WifiP2pConfig config = new WifiP2pConfig();
  10. config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;
  11. // 判断系统是否强制使用了某种WSC配置方法
  12. int forceWps = SystemProperties.getInt("wifidirect.wps", -1);
  13. if (forceWps != -1) config.wps.setup = forceWps;
  14. else {
  15. // 获取对端P2P Device支持的WSC配置方法,优先考虑PBC
  16. if (mSelectedWifiPeer.device.wpsPbcSupported())
  17. config.wps.setup = WpsInfo.PBC;
  18. else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {
  19. config.wps.setup = WpsInfo.KEYPAD;
  20. else config.wps.setup = WpsInfo.DISPLAY;
  21. }
  22. // 通过WifiP2pManager的connect函数向对端P2P Device发起连接。注意,目标设备
  23. // 信息保存在config对象中
  24. mWifiP2pManager.connect(mChannel, config,
  25. new WifiP2pManager.ActionListener(){......});
  26. }
  27. } ......
  28. }

(5)WIFI_P2P_CONNECTION_CHANGED_ACTION处理流程

当系统完成和指定P2P Device的连接后,WifiP2pSettings将收到WIFI_P2P_CONNECTION_CHANGED_ACTION广播,其对应的代码逻辑如下所示。

[—>WifiP2pSettings.java::onReceive]

  1. public void onReceive(Context context, Intent intent) {
  2. String action = intent.getAction();
  3. ......
  4. } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
  5. NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
  6. WifiP2pManager.EXTRA_NETWORK_INFO);
  7. if (mWifiP2pManager != null){
  8. // requestGroupInfo第二个参数的类型是GroupInfoListener,用于返回Group信息
  9. mWifiP2pManager.requestGroupInfo(mChannel, WifiP2pSettings.this);
  10. }
  11. if (networkInfo.isConnected()){
  12. if (DBG) Log.d(TAG, "Connected");
  13. } else startSearch(); // 如果没有加入某个P2P Group,则重新发起设备扫描
  14. }......
  15. }

当设备加入一个Group后,requestGroupInfo将通过其第二个参数设置的回调函数以返回此Group的信息,这个回调函数由GroupInfoListener接口类定义,WifiP2pSettings只需实现该接口类的onGroupInfoAvailable,相关代码如下所示。

[—>WifiP2pSettings.java::onGroupInfoAvailable]

  1. public void onGroupInfoAvailable(WifiP2pGroup group) {
  2. mConnectedGroup = group; // 保存当前所加入的Group的信息
  3. updateDevicePref(); // 更新WifiP2pPeer的界面
  4. }

以上是WifiP2pSettings的大体工作流程,读者只要把握几个重要广播的处理流程即可掌握Settings应用中和Wi-Fi P2P相关的知识。

3.WifiP2pSettings总结

上述代码中有两个比较重要的数据结构,WifiP2pDevice和WifiP2pGroup,成员比较简单,此处不赘述。它们的类图如图7-23所示。

7.3.1 WifiP2pSettings工作流程 - 图1

图7-23 WifiP2pDevice和WifiP2pGroup类图

通过上面几节的代码可知,WifiP2pSettings将借助WifiP2pManager和系统中的WifiP2pService交互。WifiP2pSettings主要使用WifiP2pManager的几个重要函数。

·initialize:初始化WifiP2pManager,其内部将和WifiP2pService通过AsyncChannel建立交互关系。

·discoverPeers:触发WifiP2pService发起P2P Device扫描工作。

·requestPeers:获取系统中保存的P2P Device信息。

·connect:和指定P2P Device发起连接,也就是相互协商以创建或加入一个P2P网络,即一个Group。

·requestGroupInfo:获取当前连接上的Group信息。

下面来看WifiP2pService,它是Android系统中P2P功能的Java层核心模块。