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内核分析》 week6作业-Linux内核fork()系统调用的创建过程

    一.进程控制块PCB-stack_struct 进程在操作系统中都有一个结构,用于表示这个进程.这就是进程控制块(PCB),在Linux中具体实现是task_struct数据结构,它主要记录了以下信息 ...

  2. 网易JS面试题与Javascript词法作用域说明

    调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量).函数参数及Arguments对象都在函数内的作用域中--这意味着它们隐藏了作用域链更上层的任何同名的属性. 2010年9月14日, ...

  3. JavaScript实现回车键切换输入框焦点

    用JavaScript实现回车键切换输入框焦点的功能,不是回车换行哦,在Textarea中,回车换行是默认功能,不过若要在textarea中使用 回车切换输入框焦点功能的话,回车换行就要失效了,不过i ...

  4. 三元运算和lambda表达式

      19.三目运算,三元运算:     if else 的简写: name = 'alex' if 1 == 1 else 'SB'   ==> 等价于      if 1 == 1:     ...

  5. Python3 如何优雅地使用正则表达式(详解二)

    使用正则表达式 现在我们开始来写一些简单的正则表达式吧.Python 通过 re 模块为正则表达式引擎提供一个接口,同时允许你将正则表达式编译成模式对象,并用它们来进行匹配. 小甲鱼解释:re 模块是 ...

  6. SparkStreaming 源码分析

    SparkStreaming 分析 (基于1.5版本源码) SparkStreaming 介绍 SparkStreaming是一个流式批处理框架,它的核心执行引擎是Spark,适合处理实时数据与历史数 ...

  7. SQL basic

    Function 从例子学习 drop function dbo.getWorkingDateCount; --getWorkingDateCount = functionName CREATE FU ...

  8. ANTLR

    http://dreamhead.blogbus.com/logs/10756716.html

  9. assets文件夹资源的访问

    访问assets文件夹中的文件,分为以下几个步骤:1.在Activity里面调用getAssets(),获取AssetManager引用2.调用AssetManager.open(String fil ...

  10. 【HDOJ】1520 Anniversary party

    第二道树形DP,先是MLE.后来仅需改小邻接矩阵的第二个维度到30就过了. #include <cstdio> #include <cstring> #include < ...