WifiP2P允许Android4.0以及以上的设备,通过wifi直接和其他设备相连,而不需要中继网络(Android的wifi p2p框架使用了Wifi Alliance的认证的程序)。使用这些API,你可以发现和连接其他使用Wifi P2pd的设备,,并且通过一个高速的连接进行通信,它的速度远远高于蓝牙连接。这对于要分享数据给其他应用的app来说十分有用,比如说多玩家游戏,或者分享照片的应用。
Wifi p2p的API中包含下面的 主要部分:
-让你可以发现、请求、连接其他设备的的方法。在WifiP2pManger类当中。
- 可以让你在WifiP2PManager中的方法被调用成功或者失败进行通知的的Listener。
- 在Wifi P2P框架发生变化的时候通知的特定的Intent,比如说意外断开连接或者一个新的连接发现的时候。
你会经常一起使用这API的三个主要组件。例如,你可以提供一个WifiP2pManager.ActionListener对象,这样在调用discoverPeers()的时候可以通过ActionListener.onSuccess()和ActionListener.onFailure()来被通知。如果discoverPeers()方法发现配对列表发生变化时候,也会发出WIFI_P2P_PEERS_CHANGED_ACTION 的intent广播。
Method | Description |
---|---|
initialize() | Registers the application with the Wi-Fi framework. This must be called before calling any other Wi-Fi P2P method. 使用wifi框架注册应用。这个方法应该在所有的方法之前调用。 |
connect() | Starts a peer-to-peer connection with a device with the specified configuration. 使用特定的配置,开始和一个设备进行点对点连接。 |
cancelConnect() | Cancels any ongoing peer-to-peer group negotiation. 取消任何进行中的 点对点组协商。 |
requestConnectInfo() | Requests a device's connection information. 获取设备的连接信息。 |
createGroup() | Creates a peer-to-peer group with the current device as the group owner. 使用当前的设备作为group owner创建点对点连接。 |
removeGroup() | Removes the current peer-to-peer group. 移除当前的点对点组。 |
requestGroupInfo() | Requests peer-to-peer group information. 获取当前点对点小组的信息。 |
discoverPeers() | Initiates peer discovery 初始化 配对发现。 |
requestPeers() | Requests the current list of discovered peers. 获取当前的的发现配对的列表。 |
WifiP2pManager 方法可以让你传入一个listener,这样Wifi P2P框架可以通过其中的方法调用来通知的你的activity状态。
可以使用的listener以及使用这些listener的WifiP2pManager的方法在下面的表中描述。
Intent | Description |
---|---|
WIFI_P2P_CONNECTION_CHANGED_ACTION | Broadcast when the state of the device's Wi-Fi connection changes. 当wifi连接变化的时候的广播。 |
WIFI_P2P_PEERS_CHANGED_ACTION | Broadcast when you call discoverPeers() . You usually want to callrequestPeers() to get an updated list of peers if you handle this intent in your application.当调用discoverPeers的时候发送的广播,你应该会在接受到广播时候调用requestPeers来更新配对的列表。 |
WIFI_P2P_STATE_CHANGED_ACTION | Broadcast when Wi-Fi P2P is enabled or disabled on the device. 当本设备的wifi P2P 启用或者禁用的时候的广播。 |
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION | Broadcast when a device's details have changed, such as the device's name. 当一个设备的细节发生变化的时候的广播,比如说设备的名变化。 |
广播接收器可以接受android系统当中的广播,这样你的app可以对他感兴趣的事情做出相应。
创建接受Wifi P2p的intent的广播接受器分为以下几个步骤:
1.创建一个BroadcastReceiver的子类。你需要在构造函数里面传入几个,一个是WifiP2pManager, 一个是WifiP2pManager.Channel,还有一个这个broadcast将会被注册到的activity。这样的话广播接收者就可以更新activity,并且可以根据需要访问wifi的硬件以及通信频道。
2.在receive方法中判断检查你关注的intent。基于接受到的不同的intent来发出不同的动作。例如,如果你接到了一个WIFI_P2P_PEERS_CHANGE_ACTION的intent ,你就可以调用requestPeers()方法,来获取现在发现的对等设备的列表。
下面的 代码想你展示了怎样创建一个典型的广播接受者。它接受一个WifiP2pManager对象以及一个activity作为参数,并且使用这两个类来发起接收广播以后的动作。
/** * A BroadcastReceiver that notifies of important Wi-Fi p2p events. */ public class WiFiDirectBroadcastReceiver extends BroadcastReceiver { private WifiP2pManager mManager; private Channel mChannel; private MyWiFiActivity mActivity; public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, MyWifiActivity activity) { super(); this.mManager = manager; this.mChannel = channel; this.mActivity = activity; } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // Check to see if Wi-Fi is enabled and notify appropriate activity 查看wifi是否可用,并通知合适的activity。 } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // Call WifiP2pManager.requestPeers() to get a list of current peers//调用WifiP2pManager.requestPeers(),来获取当前的对等列表 } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { // Respond to new connection or disconnections//响应临街和断开连接 } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { // Respond to this device's wifi state changing//响应wifi状态的变化。 } } }
在你使用WifiP2P API之前,你需要确保你的应用可以访问硬件,并且你的设备支持wifip2p协议。接下来实例化一个WifiP2pManager的对象,然后创建并注册广播接听者,然后开始使用wifi p2papi。
1.获取wifi硬件的访问权限,并且在manifest中声明正确的最小的sdk版本。
<uses-sdk android:minSdkVersion="14" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@Override public void onReceive(Context context, Intent intent) { ... String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { // Wifi P2P is enabled 可用 } else { // Wi-Fi P2P is not enabled 不可用 } } ... }
WifiP2pManager mManager; Channel mChannel; BroadcastReceiver mReceiver; ... @Override protected void onCreate(Bundle savedInstanceState){ ... mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); mChannel = mManager.initialize(this, getMainLooper(), null); mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this); ... }
4.创建一个intent filter并添加你的广播接受者要检查的广播。
IntentFilter mIntentFilter; ... @Override protected void onCreate(Bundle savedInstanceState){ ... mIntentFilter = new IntentFilter(); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); ... }
/* register the broadcast receiver with the intent values to be matched */ @Override protected void onResume() { super.onResume(); registerReceiver(mReceiver, mIntentFilter); } /* unregister the broadcast receiver */ @Override protected void onPause() { super.onPause(); unregisterReceiver(mReceiver); }
现在你已经构造了一个拥有wifi p2p功能的app,并调用WifiP2pManager中的方法。下一节中将讲述发现对的设备和连接设备等的一些典型操作。
可以调用diacoverPeers方法来探测范围内可以配对的设备。这个调用的方法是异步的,并且如果你创建了一个WifiP2pManager.ActionListener的对象,那么连接的成功与失败的结果将通过onSuccess()和onFailure方法通知你。onSuccess方法仅仅是通知你连接成功了,如果连接中有可能的设备的其他的信息,这个方法并不能提供这些信息。
mManager.discoverPeers(channel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { ... } @Override public void onFailure(int reasonCode) { ... } });如果发现过程成功探测到了设备,系统就会发出广播,WIFI_P2P_PEERS_CHANGED_ACTION intent,你可以监听他们来获取对等设备的列表。当你的app接受到了WIFI_P2P_PEERS_CHANGED_ACTION intent的时候,你就可以通过requestPeers方法来获得对等设备的列表。下面就是这个设置过程了例子:
PeerListListener myPeerListListener; ... if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // request available peers from the wifi p2p manager. This is an 从P2pManager里面获取可用的对等设备。这是一个异步的或者,调用的Activity将会通过PeerListListener.onPeersAvailabl方法被通知。 // asynchronous call and the calling activity is notified with a // callback on PeerListListener.onPeersAvailable() if (mManager != null) { mManager.requestPeers(mChannel, myPeerListListener); } }
requestPeers方法同样食欲不的,他通过调用WifiP2pManager中的PeerListListener接口中的onPeersAvailable方法来通知你的activity对等设备的列表。onPeersAvaivablable方法可以向你提供一个WifiP2pDeviceList对象,你可以通过遍历它来找到你要连接对等设备。
当你已经获得了可能连接设备的列表,并搞清楚要连接的设备的时候,你就可以通过调用connect方法来连接设备了。这个方法需要一个WifiP2pConfig对象,其中包含了要连接到的设备的信息。你可以通过调用WifiP2pManager.ActionListener中的方法来通知成功还是失败。下面代码展示了怎样与想要的设备创建一个连接。
//obtain a peer from the WifiP2pDeviceList WifiP2pDevice device; WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = device.deviceAddress; mManager.connect(mChannel, config, new ActionListener() { @Override public void onSuccess() { //success logic } @Override public void onFailure(int reason) { //failure logic } });
1.创建一个ServerSocket。这个socket将会在一个特定的端口等待客户端的的消息,并且一直阻塞直到通信发生,所以要在一个工作线程中工作。
2.创建一个客户端socket。这个socket通过ip地址和端口号和serversocket进行通信。
3.从客户端向服务器发送数据。当客户端成功和服务器端连接的时候,你可以使用字节流从客户端向服务器端发消息。
4.server socket等待一个客户端的连接(使用accept方法)。这个调用会阻塞直到客户端连接,所以在另一个线程中调用这个方法。当连接发生的时候,server 设备就可以接受从客户端设备发来的数据。可以对这些数据进行一些操作,比如说存入到文件或者想用户呈现。
下面是一个例子,修改自Wifi P2P Demo,展示了怎样创建一个CS连接,并且从客户端向服务器传输一个JPEG图片。对于一个完成的例子,编译并运行这个例子。
public static class FileServerAsyncTask extends AsyncTask { private Context context; private TextView statusText; public FileServerAsyncTask(Context context, View statusText) { this.context = context; this.statusText = (TextView) statusText; } @Override protected String doInBackground(Void... params) { try { /** * Create a server socket and wait for client connections. This //创建一个服务器server并等待连接,这个调用将会阻塞,直到接受到一个客户端连接。 * call blocks until a connection is accepted from a client */ ServerSocket serverSocket = new ServerSocket(8888); Socket client = serverSocket.accept(); /** * If this code is reached, a client has connected and transferred data//如果代码执行到了这里证明客户端已经连接并传递数据。这时从输入流中获取数据并存储为一个JPEG文件。 * Save the input stream from the client as a JPEG file */ final File f = new File(Environment.getExternalStorageDirectory() + "/" + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis() + ".jpg"); File dirs = new File(f.getParent()); if (!dirs.exists()) dirs.mkdirs(); f.createNewFile(); InputStream inputstream = client.getInputStream(); copyFile(inputstream, new FileOutputStream(f)); serverSocket.close(); return f.getAbsolutePath(); } catch (IOException e) { Log.e(WiFiDirectActivity.TAG, e.getMessage()); return null; } } /** * Start activity that can handle the JPEG image 开启一个activity,来处理这个JPEG文件。 */ @Override protected void onPostExecute(String result) { if (result != null) { statusText.setText("File copied - " + result); Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + result), "image/*"); context.startActivity(intent); } } }在客户端,使用客户端socket连接服务器并传输数据。这个例子传输了一个client的文件系统中的JPEG文件。
Context context = this.getApplicationContext(); String host; int port; int len; Socket socket = new Socket(); byte buf[] = new byte[1024]; ... try { /** * Create a client socket with the host,//创建一个客户端socket,设定ip主机,端口和超时信息。 * port, and timeout information. */ socket.bind(null); socket.connect((new InetSocketAddress(host, port)), 500); /** * Create a byte stream from a JPEG file and pipe it to the output stream//从JPEG文件创建一个字节流并输入到socket输出流中。这个数据将会被服务端设备获取。 * of the socket. This data will be retrieved by the server device. */ OutputStream outputStream = socket.getOutputStream(); ContentResolver cr = context.getContentResolver(); InputStream inputStream = null; inputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg")); while ((len = inputStream.read(buf)) != -1) { outputStream.write(buf, 0, len); } outputStream.close(); inputStream.close(); } catch (FileNotFoundException e) { //catch logic } catch (IOException e) { //catch logic } /** * Clean up any open sockets when done//当传输结束或者发生异常的时候都要关闭socket。 * transferring or if an exception occurred. */ finally { if (socket != null) { if (socket.isConnected()) { try { socket.close(); } catch (IOException e) { //catch logic } } } }