注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/connect-devices-wirelessly/wifi-direct.html


Wi-Fi的P2P API允许设备连接到附近的设备,而不需要连接到网络或热点(Android的Wi-Fi P2P框架使用Wi-Fi Direct™认证程序来编译)Wi-Fi P2P允许你的应用快速发现并连接到附近的设备,这一功能比起蓝牙来说更加强大。

这节课将向你展示如何使用Wi-Fi P2P来发现并连接附近的设备。


一). 设置应用权限声明

为了使用Wi-Fi P2P,需要添加CHANGE_WIFI_STATEACCESS_WIFI_STATEINTERNET权限声明到你的清单文件中。Wi-Fi P2P不需要一个网络连接,但它使用了标准的Java套接字,而这需要INTERNET权限。所以你需要下列权限来使用Wi-Fi P2P。

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.example.android.nsdchat"
  3. ...
  4.  
  5. <uses-permission
  6. android:required="true"
  7. android:name="android.permission.ACCESS_WIFI_STATE"/>
  8. <uses-permission
  9. android:required="true"
  10. android:name="android.permission.CHANGE_WIFI_STATE"/>
  11. <uses-permission
  12. android:required="true"
  13. android:name="android.permission.INTERNET"/>
  14. ...

二). 配置一个广播接收器和一个P2P管理器

要使用Wi-Fi P2P,你需要监听在某一事件发生时,用来告知你的应用的广播Intents。在你的应用中,实例化一个IntentFilter并设置它为监听下列事件:

WIFI_P2P_STATE_CHANGED_ACTION

指出Wi-Fi P2P已经启用

WIFI_P2P_PEERS_CHANGED_ACTION

指出可以获得的peer列表发生了变化

WIFI_P2P_CONNECTION_CHANGED_ACTION

指出Wi-Fi P2P连接的状态发生了变化

WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

指出设备的配置细节发生了改变

  1. private final IntentFilter intentFilter = new IntentFilter();
  2. ...
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main);
  7.  
  8. // Indicates a change in the Wi-Fi P2P status.
  9. intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
  10.  
  11. // Indicates a change in the list of available peers.
  12. intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
  13.  
  14. // Indicates the state of Wi-Fi P2P connectivity has changed.
  15. intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
  16.  
  17. // Indicates this device's details have changed.
  18. intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
  19.  
  20. ...
  21. }

onCreate()方法的最后,获取一个WifiP2pManager的实例,然后调用其initialize()方法。这一方法返回一个WifiP2pManager.Channel对象,在之后你将会用到它将你的应用连接到Wi-Fi P2P框架。

  1. @Override
  2.  
  3. Channel mChannel;
  4.  
  5. public void onCreate(Bundle savedInstanceState) {
  6. ....
  7. mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
  8. mChannel = mManager.initialize(this, getMainLooper(), null);
  9. }

现在创建一个新的BroadcastReceiver类,来监听系统的Wi-Fi P2P状态的改变。在onReceive()方法中,添加一个条件分支来处理每一个之前列举出来的P2P状态变化。

  1. @Override
  2. public void onReceive(Context context, Intent intent) {
  3. String action = intent.getAction();
  4. if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
  5. // Determine if Wifi P2P mode is enabled or not, alert
  6. // the Activity.
  7. int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
  8. if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
  9. activity.setIsWifiP2pEnabled(true);
  10. } else {
  11. activity.setIsWifiP2pEnabled(false);
  12. }
  13. } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
  14.  
  15. // The peer list has changed! We should probably do something about
  16. // that.
  17.  
  18. } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
  19.  
  20. // Connection state changed! We should probably do something about
  21. // that.
  22.  
  23. } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
  24. DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
  25. .findFragmentById(R.id.frag_list);
  26. fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
  27. WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
  28.  
  29. }
  30. }

最后,添加一些代码,在主activity处于活动状态时,注册intent过滤器和广播接收器,并在activity被暂停时注销它们。做这两件事情最好的位置是在onResume()onPause()方法中。

  1. /** register the BroadcastReceiver with the intent values to be matched */
  2. @Override
  3. public void onResume() {
  4. super.onResume();
  5. receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
  6. registerReceiver(receiver, intentFilter);
  7. }
  8.  
  9. @Override
  10. public void onPause() {
  11. super.onPause();
  12. unregisterReceiver(receiver);
  13. } 

三). 初始化Peer搜索

要使用Wi-Fi P2P来搜索附近的设备,调用discoverPeers()方法。这一方法接收如下参数:

  1. mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
  2.  
  3. @Override
  4. public void onSuccess() {
  5. // Code for when the discovery initiation is successful goes here.
  6. // No services have actually been discovered yet, so this method
  7. // can often be left blank. Code for peer discovery goes in the
  8. // onReceive method, detailed below.
  9. }
  10.  
  11. @Override
  12. public void onFailure(int reasonCode) {
  13. // Code for when the discovery initiation fails goes here.
  14. // Alert the user that something went wrong.
  15. }
  16. });

记住这仅仅是初始化了peer搜索。discoverPeers()方法启动搜索进程,然后迅速返回。系统会通知你搜索进程是否被监听器初始化成功。同时搜索会保持激活状态知道一个连接被初始化或者一个P2P组被构建完成。


四). 获取Peers列表

现在写下获取和处理Peers列表的代码。首先实现WifiP2pManager.PeerListListener接口,它提供了检测到的Wi-Fi P2P的peer信息。请看下面的代码:

  1. private List peers = new ArrayList();
  2. ...
  3.  
  4. private PeerListListener peerListListener = new PeerListListener() {
  5. @Override
  6. public void onPeersAvailable(WifiP2pDeviceList peerList) {
  7.  
  8. // Out with the old, in with the new.
  9. peers.clear();
  10. peers.addAll(peerList.getDeviceList());
  11.  
  12. // If an AdapterView is backed by this data, notify it
  13. // of the change. For instance, if you have a ListView of available
  14. // peers, trigger an update.
  15. ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
  16. if (peers.size() == 0) {
  17. Log.d(WiFiDirectActivity.TAG, "No devices found");
  18. return;
  19. }
  20. }
  21. }

现在修改你的广播接收器的onReceive()方法,当一个具有WIFI_P2P_PEERS_CHANGED_ACTION的intent被接收时,来调用requestPeers()方法。你需要通过某种方法将监听器传递给广播接收器。一种方法是将它作为一个参数传递给广播接收器的构造函数:

  1. public void onReceive(Context context, Intent intent) {
  2. ...
  3. else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
  4.  
  5. // Request available peers from the wifi p2p manager. This is an
  6. // asynchronous call and the calling activity is notified with a
  7. // callback on PeerListListener.onPeersAvailable()
  8. if (mManager != null) {
  9. mManager.requestPeers(mChannel, peerListListener);
  10. }
  11. Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
  12. }...
  13. }

现在,一个具有WIFI_P2P_PEERS_CHANGED_ACTION的intent将会激活一个更新Peer列表的请求。


五). 与一个Peer发起连接

为了和一个Peer发起连接,创建一个新的WifiP2pConfig对象,然后从代表你想要连接的设备的WifiP2pDevice中把数据拷贝到这个对象里面。然后调用connect()方法。

  1. @Override
  2. public void connect() {
  3. // Picking the first device found on the network.
  4. WifiP2pDevice device = peers.get(0);
  5.  
  6. WifiP2pConfig config = new WifiP2pConfig();
  7. config.deviceAddress = device.deviceAddress;
  8. config.wps.setup = WpsInfo.PBC;
  9.  
  10. mManager.connect(mChannel, config, new ActionListener() {
  11.  
  12. @Override
  13. public void onSuccess() {
  14. // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
  15. }
  16.  
  17. @Override
  18. public void onFailure(int reason) {
  19. Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
  20. Toast.LENGTH_SHORT).show();
  21. }
  22. });
  23. }

在这个代码中实现的WifiP2pManager.ActionListener仅在当初始化成功或失败时向你发起通知。要监听连接状态的变化,需要实现WifiP2pManager.ConnectionInfoListener接口。它的onConnectionInfoAvailable()回调函数将会在连接状态变化后向你发出通知。在一些情况下,许多设备会向一个设备发起连接(比如一个多人连接的游戏,或者一个聊天的应用),其中一个设备会被任命为一个“组所有者(group owner)”。

  1. @Override
  2. public void onConnectionInfoAvailable(final WifiP2pInfo info) {
  3.  
  4. // InetAddress from WifiP2pInfo struct.
  5. InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());
  6.  
  7. // After the group negotiation, we can determine the group owner.
  8. if (info.groupFormed && info.isGroupOwner) {
  9. // Do whatever tasks are specific to the group owner.
  10. // One common case is creating a server thread and accepting
  11. // incoming connections.
  12. } else if (info.groupFormed) {
  13. // The other device acts as the client. In this case,
  14. // you'll want to create a client thread that connects to the group
  15. // owner.
  16. }
  17. }

现在回到广播接收器的onReceive()方法中,修改监听WIFI_P2P_CONNECTION_CHANGED_ACTION的intent的部分。当这个intent接收到了以后,调用requestConnectionInfo()。这是一个异步的调用,所以结果会被之前你所提供的作为参数的连接信息监听器接收:

  1. ...
  2. } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
  3.  
  4. if (mManager == null) {
  5. return;
  6. }
  7.  
  8. NetworkInfo networkInfo = (NetworkInfo) intent
  9. .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
  10.  
  11. if (networkInfo.isConnected()) {
  12.  
  13. // We are connected with the other device, request connection
  14. // info to find group owner IP
  15.  
  16. mManager.requestConnectionInfo(mChannel, connectionListener);
  17. }
  18. ...

【Android Developers Training】 76. 用Wi-Fi创建P2P连接的更多相关文章

  1. 【Android Developers Training】 1. 创建一个Android项目工程

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. 【Android Developers Training】 106. 创建并检测地理围栏

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. 【Android Developers Training】 95. 创建一个同步适配器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  4. 【Android Developers Training】 94. 创建一个空内容提供器(Content Provider)

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. 【Android Developers Training】 93. 创建一个空验证器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  6. 【Android Developers Training】 18. 重新创建一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. 【Android Developers Training】 21. 创建一个可变动的UI

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 【Android Developers Training】 20. 创建一个Fragment

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. 【Android Developers Training】 74. 序言:通过无线连接设备

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

随机推荐

  1. Java集合框架类

    java集合框架类图 Collection接口(List.Set.Queue.Stack):

  2. OBS实现直播解决方案【html实现直播】

    项目的需要,要整一个视频直播,但又不想在其他平台那种直播室盗链展示,那我就直接用播放器来实现rtmp流媒体服务器推流吧!没废话,走起 1.你要有一个媒体服务器,暂时用[盘古云],这个还好,算是不错的平 ...

  3. CMT2300 收发一体 SUB 1G 支持灵活选频

    CMT2300A 是一款超低功耗,高性能,适用于各种140 至1020 MHz 无线应用的OOK,(G)FSK 射频收发器.它是CMOSTEK NextGenRFTM 射频产品线的一部分,这条产品线包 ...

  4. 几个常用的linux快捷键和shell知识

    1)   !$    !$是一个特殊的环境变量,它代表了上一个命令的最后一个字符串.如:你可能会这样:     $mkdir mydir     $mv mydir yourdir     $cd y ...

  5. Zepto源码分析-deferred模块

    源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT l ...

  6. 如何通过JS实现简单抖动效果

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  7. MySQL开放远程登录

    在服务器上部署MYSQL每次观看MYSQL记录或者修改的时候都需要登录服务器,又烦又占资源.所以使用另一种方法:对外开放接口. 注:如果某些服务器开启防火墙屏蔽了某些接口就有可能导致远程用户无法登录M ...

  8. 用NIO实现http协议

    先来看一下本篇博文的目录: 一:简介Nio 二:Nio的好处 三:关于http协议 四:代码实现 五:总结 一:简介Nio 我们都知道io流,那么NIO是什么呢?本篇博文将会带你一探NIO,NIO的全 ...

  9. js实现防盗图

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 手把手教你用Eclipse+TestNG搭建接口自动化测试框架

    转载于:http://qa.blog.163.com/blog/static/190147002201510275306185/ 把群博里关于接口自动化的文章都看了一遍,都是关于测试过程中遇到的问题及 ...