(二)进阶练习____12、连接无线设备——使用Wi-Fi服务搜索
原文链接:http://developer.android.com/training/connect-devices-wirelessly/nsd-wifi-direct.html
负责人:liris
目录[隐藏] |
使用Wi-Fi Direct搜索服务 - Using Wi-Fi Direct for Service Discovery
这部分的第一课Using Network Service Discovery向你展示了如何找到连接本地网络的服务。而使用Wi-Fi Direct服务搜索,可以在不连接网络的情况下,直接找到附近的设备。 你也可以通知那些服务在你的机器上运行。这些功能帮助你在没有可用的本地网络或热点时,进行应用程序之间的通讯。
这组API和上节课讨论的网络服务搜索的API虽然功能上类似,但实现的代码却大相径庭。这节课向你介绍如何使用Wi-Fi Direct™从其他设备上搜索可用服务。本课假定你已经熟悉了Wi-Fi Direct 的API。
配置清单 - Set Up the Manifest
使用Wi-Fi Direct,需要向你的清单文件添加 CHANGE_WIFI_STATE, ACCESS_WIFI_STATE 和 INTERNET 权限。Wi-Fi Direct不需要因特网连接,但需要使用标准Java套接字,而在Android中使用套接字需要打开相应要求的权限。
<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"/> ...
添加一个本地服务 - Add a Local Service
如果你正提供一个本地服务,你需要注册服务搜索。当你注册了本地服务,框架会自动向对等点的服务搜索请求做出回应。
创建一个本地服务的步骤:
1. 创建一个 WifiP2pServiceInfo 对象。
2. 将你的服务信息填入该对象。
3. 调用 addLocalService() 来注册用于服务搜索的本地服务。
private void startRegistration() { //创建一个包含你服务信息的字符串映射 Map record = new HashMap(); record.put("listenport", String.valueOf(SERVER_PORT)); record.put("buddyname", "John Doe" + (int) (Math.random() * 1000)); record.put("available", "visible"); //服务信息。将一个实例名传给它,服务类型为_protocol._transportlayer //映射包含其他设备在连上它时希望获取的信息。 WifiP2pDnsSdServiceInfo serviceInfo = WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record); //添加本地服务,用来发送服务信息,网络频道, //以及将会用到的用来指示请求成败的监听器。 mManager.addLocalService(channel, serviceInfo, new ActionListener() { @Override public void onSuccess() { //命令成功!这里不一定需要代码, //除非你想刷新UI或者添加日志说明。 } @Override public void onFailure(int arg0) { //命令失败。检查P2P_UNSUPPORTED,ERROR或者BUSY } }); }
搜索附近的服务 - Discover Nearby Services
Android使用回调方法来通知你的应用程序有哪些可用的服务,因此首先要做的是将这些设置好。创建一个 WifiP2pManager.DnsSdTxtRecordListener 来监听进来的记录。这个记录也可以被其他设备广播。当有某个设备加入时,将设备地址和其他你需要的信息复制到当前方法以外的一个数据结构中,这样你就可以稍后再对它进行操作。下面的例子假定记录包含一个buddyname域,填入的是用户身份。
final HashMap<String, String> buddies = new HashMap<String, String>(); ... private void discoverService() { DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() { @Override /* 回调包含: * fullDomain: 完整的域名: 例如"printer._ipp._tcp.local." * record: 成对记录有关键字和值的数据的TXT. * device: 运行通知的服务的设备. */ 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 。它接收实际的描述和连接信息。前面的代码片段实现了一个 Map 对象,将设备地址和其伙伴名称进行配对。服务回应监听器使用这个对象来将DNS记录和对应的服务信息联系起来。当两个监听器都实现了,就用 setDnsSdResponseListeners() 方法把他们添加进 WifiP2pManager。
private void discoverService() { ... DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() { @Override public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) { //假定有一个设备加入了, //就使用DnsTxtRecord中的通俗易懂的信息更新设备名称 resourceType.deviceName = buddies.containsKey(resourceType.deviceAddress) ? buddies.get(resourceType.deviceAddress) : resourceType.deviceName; //添加到为显示Wi-Fi设备而自定义的适配器中 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()。这个方法也使用一个监听器来汇报成功或失败。
serviceRequest = WifiP2pDnsSdServiceRequest.newInstance(); mManager.addServiceRequest(channel, serviceRequest, new ActionListener() { @Override public void onSuccess() { //成功! } @Override public void onFailure(int code) { //命令失败。检查P2P_UNSUPPORTED, ERROR, 或者 BUSY } });
最后,呼叫 discoverServices()。
mManager.discoverServices(channel, new ActionListener() { @Override public void onSuccess() { //成功! } @Override public void onFailure(int code) { //命令失败。检查P2P_UNSUPPORTED, ERROR, 或者 BUSY if (code == WifiP2pManager.P2P_UNSUPPORTED) { Log.d(TAG, "P2P isn't supported on this device."); else if(...) ... } });
如果一切顺利的话,万岁,那你就搞定了!如果你遇到了问题,记得你的异步调用带一个 WifiP2pManager.ActionListener 参数,它为你提供了指明成功或失败的回调函数。如需诊断问题,将调试代码写在 onFailure() 里面。这个方法的错误代码将会暗示问题所在。下面是可能的错误值和意义。
- 运行程序的设备不支持Wi-Fi Direct
- 系统太忙,无法处理请求。
- 内部错误引起的操作失败。