Service Discovery 简介

在Android WifiDirect学习(一 )中,简单介绍了如何使用WifiDirect进行搜索——连接——传输。

这样会有一个问题,那就是你会搜索到到附近所有处于WifiDirect搜索状态的网络设备,而这些设备中不一定都是你想进行连接的。

Android WifiDirect Api提供了一个仅搜索特定网络设备的搜索方式,叫做Service Discovery,它是Wi-Fi Direct API在Android 4.1中被增强以支持在WifiP2pManager中的预先关联服务发现。这允许在连接之前使用Wi-Fi Direct通过服务(Service)发现和筛选周围的设备。

常见Service:

Android已经提供了两种Service,一种是 Bonjour Service,一种是Upnp Service,开发者也可以自己设计一套Service,一般情况下,这两种Service已经足够用了。

Service基本使用方法

为了广播你的应用作为一个Wi-Fi上的服务,以便于其他设备能够发现并连接你的应用,需要调用addLocalService()方法并传输一个WifiP2pServiceInfo对象。这个对象描述了你的应用服务。

为了通过Wi-Fi开始发现附近的设备,首先你应当决定使用Bonjour Service还是Upnp Service实现通信。如果使用Bonjour,首先要用setDnsSdResponseListeners()设置好一些回调监听器。这个方法需要WifiP2pManager.DnsSdServiceResponseListenerDnsSdTxtRecordListener两个作参数。如果使用Upnp,则要调用setUpnpServiceResponseListener()。这个方法需要UpnpServiceResponseListener作为参数。

在你开始发现本地设备上的服务之前,你也需要调用addServiceRequest()方法。当你传递给这个方法的接口ActionListener收到一个成功的回调,接着你可以通过调用discoverServices()开始探索在本地设备上的服务。

当本地服务被发现,你会收到一个回调,来自WifiP2pManager.DnsSdServiceResponseListener或者WifiP2pManager.UpnpServiceResponseListener,这个取决于你注册使用的是Bonjour还是Upnp。收到的回调都会包含一个WifiP2pDevice对象,代表了对应的设备。

Service使用案例(以Bonjour Service为例)

设置Manifest

为了使用Wi-Fi P2P,要在你的androidmanifest中添加CHANGE_WIFI_STATEACCESS_WIFI_STATE、和INTERNET权限。即使Wi-FiP2P不要求互联网连接,但它要使用标准的Java socket,并且Android在使用这些套接字时要求申请这些权限,因此要在清单中申请INTERNET权限。

 <manifestxmlns: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"/>
...

添加Local Service

如果你要提供一个Local Service,那么就需要把这个服务注册为可发现的。本地服务被注册后,框架就会自动的响应来自对等点的服务发现请求。

以下是创建本地服务的步骤:

1. 创建一个WifiP2pServiceInfo对象;

2. 填入你的服务相关的信息;

3. 调用addLocalService()方法来注册本地服务,让其可发现。

 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");
// Serviceinformation. 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 thelocal service, sending the service info, network channel,
// andlistener that will be used to indicate success or failure of
// therequest.
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对象来监听传入的记录。这个记录可以是其他设备的任意广播。当一个记录进入时,你可以把设备地址和其他你想要的其他相关信息复制到当前方法外部的数据结构中,以便后续可以访问它。下面的例子假设记录中包含一个“buddyname”字段,它带有用户的标识。

 finalHashMap<String,String> buddies =newHashMap<String,String>();
...
private void discoverService() {
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
@Override
/* Callbackincludes:
* fullDomain: full domainname: e.g "printer._ipp._tcp.local."
* record: TXT record dta as amap of key/value pairs.
* device: The device runningthe advertised service.
*/
public voidonDnsSdTxtRecordAvailable(
String fullDomain, Map record, WifiP2pDevice device) {
Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
buddies.put(device.deviceAddress, record.get("buddyname"));
}
};
...
}

实现一个WifiP2pManager.DnsSdServiceResponseListener接口,来获取服务信息。这个接口会接收实际的描述和连接信息。上面的代码中使用了Map对象把设备地址和用户标识组成一对。服务响应监听器使用这个接口把DNS记录和对应的服务信息连接到一起。实现上述两个监听器后,使用setDnsSdResponseListener()方法把它们添加给WifiP2pManager对象。

 private void discoverService(){
...
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
@Override
public voidonDnsSdServiceAvailable(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()方法,这个方法也需要一个监听器来包括成功或失败。

 serviceRequest =WifiP2pDnsSdServiceRequest.newInstance();
mManager.addServiceRequest(channel,
serviceRequest,
new ActionListener() {
@Override
public void onSuccess() {
// Success!
}
@Override
public void onFailure(int code) {
// Command failed. Check forP2P_UNSUPPORTED, ERROR, or BUSY
}
});

最后,调用的discoverServices()方法。

 mManager.discoverServices(channel,newActionListener(){
@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'tsupported on this device.");
else if(...)
...
}
});

如果一切顺利,恭喜你大功告成。如果遇到问题,前面异步调用的参数WifiP2pManager.ActionListener参数,它提供了指示成功或失败的回调方法。把调试断点设置在onFailure()方法中来诊断问题。这个方法提供了错误代码,以下是可能发生的错误:

P2P_UNSUPPORTED

运行app的设备上不支持Wi-Fi P2P

BUSY

系统忙于处理请求

ERROR

由于内部错误导致操作失败

参考文章

Android 4.1 API中文详解-Android 4.1 APIs  http://wiki.eoeandroid.com/Android_4.1_APIs

http://developer.android.com/

Android WiFiDirect 学习(二)——Service Discovery的更多相关文章

  1. Android JNI学习(二)——实战JNI之“hello world”

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  2. Android WifiDirect 学习(三) 一些基础知识和问题

    P2P架构介绍 P2P架构中定义了三个组件,一个设备,两种角色.这三个组件分别是: P2P Device:它是P2P架构中角色的实体,读者可把它当做一个Wi-Fi设备. P2P Group Owner ...

  3. Android动画学习(二)——Tween Animation

    前两天写过一篇Android动画学习的概述,大致的划分了下Android Animation的主要分类,没有看过的同学请移步:Android动画学习(一)——Android动画系统框架简介.今天接着来 ...

  4. Android WifiDirect学习(一)

    WiFi Direct基本介绍 Wi-Fi Direct标准允许无线网络中的设备无需通过无线路由器即可相互连接.与蓝牙技术类似,这种标准允许无线设备以点对点形式互连,不过在传输速度与传输距离方面则比蓝 ...

  5. Android JNI 学习(二):JNI 设计机制

    本章我们重点说明以下JNI设计的问题,本章中提到的大多数设计问题都与native方法有关.至于调用相关的API的设计,我们会在后面进行介绍. 一.JNI接口函数和指针 native 代码通过调用JNI ...

  6. Android JNI学习(五)——Demo演示

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  7. Android JNI学习(四)——JNI的常用方法的中文API

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  8. Android JNI学习(三)——Java与Native相互调用

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  9. Android学习总结(二)——Service基本概念和生命周期

    好了,前面我们已经学习了Activity的知识,相信大家也有一定的理解,但是还是不能放松,Android四大组件,我们才学习了一个而已,接下我们继续学习Service.计划总结如下内容: 一.Serv ...

随机推荐

  1. 《Linux内核分析》 week2作业-时间片轮转

    一.基于时间片轮转调度代码的解读 代码结构主要由三个文件组成: 1.mypcb.h 2.myinterrupt.c 3.mymain.c 1.进程控制块(mypcb.h) /* CPU-specifi ...

  2. ContentProvider URI的组成

    ContentProvider URI由哪几部分组成 ContentProvider URI与HTTP URI类似,由以下4部分组成: 1.content://   相当于HTTP URI中的http ...

  3. jsp语法与标签

    语法: <% 多行java代码 %> 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本.HTML标记和其他JSP元素. 举例: <% int x = 10; ...

  4. 如何使用Reaver-PJ-Wi-Fi网络的WPA密码

    在正式开始之前,我还是要不厌其烦强调一下:知识就是力量,但是拥有力量不代表着可以为所欲为.触犯法律.同样,骑白马的不一定是王子,会开锁的也不一定是小偷.本文只是关于某些技术的实验与验证,只适用于学习. ...

  5. GPIO的8种模式详解

    和stm32的GPIO相关的寄存器有 (1)两个32位的配置寄存器(GPIOX_CRL,GPIOX_CRH) 每一个IO占用4位,16个IO占用64位就是两个32为寄存器. 其中CNF[1:0]是用来 ...

  6. strnclmp和strlen函数的用法

    一.strncmp 函数 函数原型: 1.函数原型:int strncmp (const char *s1, const char *s2, size_t  n) 2.头文件: <string. ...

  7. nodejs 记入

    1. vs2015 使用最新的 nodejs refer : http://josharepoint.com/2016/05/04/how-to-configure-visual-studio-201 ...

  8. 【转】s3c2440 按键驱动 — 字符设备

    原文网址:http://www.xuebuyuan.com/632893.html 主机:VM - redhat 9.0 开发板:FL2440,linux-2.6.12 arm-linux-gcc:3 ...

  9. SPOJ694 -- DISUBSTR 后缀树组求不相同的子串的个数

    DISUBSTR - Distinct Substrings   Given a string, we need to find the total number of its distinct su ...

  10. Java ConcurrentHashmap 解析

    总体描述: concurrentHashmap是为了高并发而实现,内部采用分离锁的设计,有效地避开了热点访问.而对于每个分段,ConcurrentHashmap采用final和内存可见修饰符Volat ...