如何创建一个使用Wi-Fi P2P的应用

创建使用Wi-Fi P2P功能的应用需要多个步骤,其中包括注册广播接受者,发现设备,连接设备,传输数据等步骤。下面是关于这些步骤的介绍。

初始化设置

在调用wifi-p2p的API前,你须要确保相应设备支持Wi-Fi P2P。如果该设备支持Wi-Fi P2P,那么你可以在你的应用中获取WifiP2pManager实例,并进行广播接受者的创建和注册,以及调用其它Wi-Fi P2P 的API。

1.在manifest文件当中声明相关权限,同时声明你应用的SDK 最小版本号(因为Android4.0才开始支持WiFi直连,所以版本号是14)。

2.检查是WiFi是否开启。一个比较好的检查方式是,当广播接受者收到WIFI_P2P_STATE_CHANGED_ACTION广播时,对其进行判断看WiFi是否开启。然后将WiFi的状态通知给Activity,让其对此作出相应的处理(例如提示用户或者开启WiFi)。

@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
}
}
...
}

3、在你的Activity的onCreate方法当中,获取WifiP2pManager的实例。然后调用initialize()方法来将你的应用注册到 Wi-Fi P2P framework当中,这个方法将会返回一个WifiP2pManager.Channel对象,它把你的应用与底层的 Wi-Fi P2P framework连接起来。

建议你同时创建一个广播接受者,并把WifiP2pManager和WifiP2pManager.Channel以及Activity引用传给它。这可以让你的broadcast receiver在接收到特定时间的广播时,能够很方便的通知Activity或者对Wi-Fi进行操作。

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,然后把下面这些intent加入其中,这样你的广播接收者就能接收到WiFi变化相应的广播。

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);
...
}
5、在Activity的onResume()方法当中注册广播接受者,在onPause方法当中注销它。
/* 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);
}
发现设备
调用discoverPeers()方法,就可以开始发现周边适合连接的设备。这个方法的调用是异步的,如果你设置了一个WifiP2pManager.ActionListener监听器,那么这个方法的调用结果会通过ActionListener回调返回。onSuccess()方法只告诉你该发现节点的任务执行成功,但不会提供所发现节点的任何信息。
mManager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
...
}
@Override
public void onFailure(int reasonCode) {
...
}
});
如果发现节点的任务执行成功,并且检测到了适合节点。系统会发送WIFI_P2P_PEERS_CHANGED_ACTION广播,当你收到这个广播的时候,你就可以调用requestPeers()方法,来获取发现了节点列表。
PeerListListener myPeerListListener;
...
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// request available peers from the wifi p2p manager. This is an
// asynchronous call and the calling activity is notified with a
// callback on PeerListListener.onPeersAvailable()
if (mManager != null) {
mManager.requestPeers(mChannel, myPeerListListener);
}
}
连接节点
当你找到自己想要连接的设备时,你可以通过调用connect()方法来连接这个设备,调用这个方法需要一个WifiP2pConfig对象作为参数。WifiP2pConfig对象包含了进行连接的一些配置信息,包括设备地址,认证方式,谁作为GroupOwner等。这个方法调用的结果会通过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
}
});

传输数据

一旦设备间的连接成功建立,你就可以通过socket来传输数据,基本的步骤如下。

1、创建ServerSocket,并调用accept()方法在指定端口等待客户端的连接,注意这个过程将会阻塞线程,请在后台线程完成这个它。

2、创建ClientSocket,然后使用服务器IP和端口去连接充当服务器的设备。

3、 客户端往服务器发送数据。当客户端成功连接上服务器端之后,你就可以以字节流的形式向服务器发送数据了。

4、服务器接收数据。当服务器的accept()接受一个客户端连接之后,服务器端口能够收到客户端发来的数据了。

注意在WiFi P2P连接中,Group Owner和Group Client都能够作为Server Socket,并且当一个Socket连接建立后,双方都可以收发数据。

下面是一个通过WiFi P2P从客户端往服务器端发送图片的例子,以下是服务器端部分代码。

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
* 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
* 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
*/
@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);
}
}
}
以下是客户端部分代码
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,
* 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
* 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
* transferring or if an exception occurred.
*/
finally {
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
} catch (IOException e) {
//catch logic
}
}
}
}