编写:naizhengtan - 原文:http://developer.android.com/training/connect-devices-wirelessly/nsd-wifi-direct.html
在本章第一节“使得网络服务可发现”中介绍了如何在局域网中发现并连接到其他设备的服务上。 然而,即使在不接入网络的环境中,Wi-Fi P2P的发现服务也可以使你的应用直接连接到附近的设备。 与此同时,你也可以向外公布自己设备上的服务。 Wi-Fi P2P发现服务的这种能力可以在没有局域网或者网络热点的情况下, 帮助不同设备上的应用进行通信。
虽然本节所述的API与第一节NSD(Network Service Discovery)的API相似, 但是具体的实现代码却截然不同。 本节将讲述如何通过Wi-Fi P2P技术发现附近可用设备中的服务。 假设读者已经对Wi-Fi P2P的API有一定了解。
使用Wi-Fi P2P技术,需要添加CHANGE_WIFI_STATE, ACCESS_WIFI_STATE以及 INTERNET三种权限到应用的manifest文件。 Wi-Fi P2P技术虽然不需要访问互联网,但是它会使用Java中的标准socket。 而使用socket需要具有INTERNET权限,这也是Wi-Fi P2P技术需要申请该权限的原因。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.nsdchat"
...
<uses-permission
android:required="true"
android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission
android:required="true"
android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission
android:required="true"
android:name="android.permission.INTERNET"/>
...
如果你想提供一个本地服务,就需要在服务发现框架中注册该服务。 当本地服务被成功注册,系统将自动回复所有来自附近的服务发现请求。
三步创建本地服务:
- 新建WifiP2pServiceInfo对象
- 加入想创立服务的详细信息
- 调用[addLocalService()](http://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html#addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener)注册该服务
private void startRegistration() {
// Create a string map containing information about your service.
Map record = new HashMap();
record.put("listenport", String.valueOf(SERVER_PORT));
record.put("buddyname", "John Doe" + (int) (Math.random() * 1000));
record.put("available", "visible");
// Service information. Pass it an instance name, service type
// _protocol._transportlayer , and the map containing
// information other devices will want once they connect to this one.
WifiP2pDnsSdServiceInfo serviceInfo =
WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record);
// Add the local service, sending the service info, network channel,
// and listener that will be used to indicate success or failure of
// the request.
mManager.addLocalService(channel, serviceInfo, new ActionListener() {
@Override
public void onSuccess() {
// Command successful! Code isn't necessarily needed here,
// Unless you want to update the UI or add logging statements.
}
@Override
public void onFailure(int arg0) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
}
});
}
Android使用回调函数通知应用程序附近可用的服务, 因此发现服务的第一步是设置这些回调函数。 新建一个WifiP2pManager.DnsSdTxtRecordListener实例侦听实时收到的记录(record)。 这些记录是来自其他设备的广播。 当收到记录时,将其中的设备地址和其他相关信息拷贝出,供之后使用。 下面的例子假设这条记录不仅包含设备的身份,还包含一个名为“buddyname”的域(field)。
final HashMap<String, String> buddies = new HashMap<String, String>();
...
private void discoverService() {
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
@Override
/* Callback includes:
* fullDomain: full domain name: e.g "printer._ipp._tcp.local."
* record: TXT record dta as a map of key/value pairs.
* device: The device running the advertised service.
*/
public void onDnsSdTxtRecordAvailable(
String fullDomain, Map record, WifiP2pDevice device) {
Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
buddies.put(device.deviceAddress, record.get("buddyname"));
}
};
...
}
接下来创建WifiP2pManager.DnsSdServiceResponseListener对象,用以获取目标服务的信息。 这个对象将接受服务的实际描述以及连接信息。 上一段代码构建了一个包含设备地址和“buddyname”键值对的Map对象。 WifiP2pManager.DnsSdServiceResponseListener对象使用这些配对信息将DNS记录和对应的服务信息对应起来。 当上述两个监听器构建完成了,调用[setDnsSdResponseListeners()](http://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html#setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener)将他们加入WifiP2pManager。
private void discoverService() {
...
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
@Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType,
WifiP2pDevice resourceType) {
// Update the device name with the human-friendly version from
// the DnsTxtRecord, assuming one arrived.
resourceType.deviceName = buddies
.containsKey(resourceType.deviceAddress) ? buddies
.get(resourceType.deviceAddress) : resourceType.deviceName;
// Add to the custom adapter defined specifically for showing
// wifi devices.
WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
.findFragmentById(R.id.frag_peerlist);
WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
.getListAdapter());
adapter.add(resourceType);
adapter.notifyDataSetChanged();
Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
}
};
mManager.setDnsSdResponseListeners(channel, servListener, txtListener);
...
}
然后创建服务请求,并调用[addServiceRequest()](http://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html#addServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener)方法。 这个方法也需要一个监听器(Listener)报告请求成功与失败。
serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
mManager.addServiceRequest(channel,
serviceRequest,
new ActionListener() {
@Override
public void onSuccess() {
// Success!
}
@Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
}
});
最后调用[discoverServices()](http://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html#discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener)。
mManager.discoverServices(channel, new ActionListener() {
@Override
public void onSuccess() {
// Success!
}
@Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
if (code == WifiP2pManager.P2P_UNSUPPORTED) {
Log.d(TAG, "P2P isn't supported on this device.");
else if(...)
...
}
});
如果所有部分都配置正确,你应该就能看到正确的结果了! 如果遇到了问题,你可以查看WifiP2pManager.ActionListener中的回调函数。 它们能够指示操作是否成功。 你可以将debug的代码放置在[onFailure()](http://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.ActionListener.html#onFailure(int)中来诊断问题。 其中的一些错误码(Error Code)也许能为你带来不小启发。 下面是一些常见的错误:
-
P2P_UNSUPPORTED
Wi-Fi P2P 不被现在的设备支持 -
BUSY
系统忙于处理请求 -
ERROR
内部错误