注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/connect-devices-wirelessly/nsd.html


将网络服务搜索(NSD)添加至你的应用允许你的用户识别在本地网络的其它设备,这些设备提供了你的应用所需要的服务。这一特性对很多P2P类应用非常有用,如:文件共享,多人游戏等。Android的NSD API简化了你实现该功能特性所需要做的事情。

这节课将展示如何构建一个应用,它可以将其名字和连接信息广播至所在的本地网络,并搜索其它正在做同样事情的应用。最终,这节课将展示如何连接运行在另一设备上的应用。


一). 在网络上注册你的服务

Note:

这一步是可选的,如果你对于将你的应用服务广播至本地网络并不关心,你可以跳至下一节。

为了在本地网络注册你的服务,首先创建一个NsdServiceInfo对象。这个对象提供了网络上其它设备在决定是否要链接到你提供的服务时,所要使用到的信息。

public void registerService(int port) {
// Create the NsdServiceInfo object, and populate it.
NsdServiceInfo serviceInfo = new NsdServiceInfo(); // The name is subject to change based on conflicts
// with other services advertised on the same network.
serviceInfo.setServiceName("NsdChat");
serviceInfo.setServiceType("_http._tcp");
serviceInfo.setPort(port);
....
}

这段代码将服务名设置为“NsdChat”。这个名字对于任何在网络上,并正在使用NSD寻找本地服务的设备都是可见的。记住这个名字必须是在网络上是唯一的,不过Android会自动解决名字冲突的问题。如果两个网络上的设备都安装了NsdChat应用,他们中的一个会自动改变服务名称,如:“NsdChat(1)”。

第二个变量设置了服务类型,指定了传输层和应用层所使用的协议。语法的格式是:“_<应用层协议>._<传输层协议>”,在代码片段中,应用使用了运行于TCP之上的HTTP。如果一个应用提供了打印服务(例如,一个网络打印机)那么会将服务类型设置为“_ipp.tcp”。

Note:

国际号码分配机构(IANA)管理了一个集中、权威的服务类型列表,这一列表被服务搜索协议,如NSD和Bonjour。你可以下载这个列表。如果你希望使用一个新的服务类型,你需要填写IANA端口和服务注册申请表中维护它。

当配置了你的服务的端口,避免对它进行硬编码因为这可能会和其它应用产生冲突。例如,假设你的应用一直使用1337端口,那么将会有潜在的可能性与其它使用相同端口的已安装应用产生冲突。解决方案是使用设备的下一个可使用端口。因为这一信息会通过一个服务广播提供给其它应用,在编译期间,你的应用所使用的端口无需被其它应用知道。应用可以可以通过服务广播在连接到你的服务之前来得到这一信息。

如果你使用套接字链接,下面展示了你应该如何初始化一个套接字链接到任一可以获得的端口,方法是简单地设置它为0。

public void initializeServerSocket() {
// Initialize a server socket on the next available port.
mServerSocket = new ServerSocket(0); // Store the chosen port.
mLocalPort = mServerSocket.getLocalPort();
...
}

现在你已经定义了NsdServiceInfo对象,你需要实现RegistrationListener接口。这个接口包括了Android系统所使用的回调函数,以此告知你的应用服务的注册和注销是否成功。

public void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() { @Override
public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
// Save the service name. Android may have changed it in order to
// resolve a conflict, so update the name you initially requested
// with the name Android actually used.
mServiceName = NsdServiceInfo.getServiceName();
} @Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Registration failed! Put debugging code here to determine why.
} @Override
public void onServiceUnregistered(NsdServiceInfo arg0) {
// Service has been unregistered. This only happens when you call
// NsdManager.unregisterService() and pass in this listener.
} @Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Unregistration failed. Put debugging code here to determine why.
}
};
}

现在你具有了注册服务所需要的代码了。调用方法:registerService()

注意到这个方法是异步的,所以任何需要运行的代码,必须在服务注册后运行。这些代码写在onServiceRegistered()方法中。

public void registerService(int port) {
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName("NsdChat");
serviceInfo.setServiceType("_http._tcp.");
serviceInfo.setPort(port); mNsdManager = Context.getSystemService(Context.NSD_SERVICE); mNsdManager.registerService(
serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}

二). 在网络上发现服务

网络让生活变得丰富,从网络打印机到网络相机。让你的应用能够看见这一充满活力的生态系统的关键是服务搜索。你的应用需要再网络上监听服务来查询当前能够获得哪些服务,并且排除任何应用无法实现的服务。

服务搜索,和服务注册类似,有两步要做:为相关的回调函数配置一个搜索监听器,为discoverServices()执行一次异步调用。

首先,实例化一个匿名类,它实现了NsdManager.DiscoveryListener。下面的代码片段为一个例子:

public void initializeDiscoveryListener() {

    // Instantiate a new DiscoveryListener
mDiscoveryListener = new NsdManager.DiscoveryListener() { // Called as soon as service discovery begins.
@Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
} @Override
public void onServiceFound(NsdServiceInfo service) {
// A service was found! Do something with it.
Log.d(TAG, "Service discovery success" + service);
if (!service.getServiceType().equals(SERVICE_TYPE)) {
// Service type is the string containing the protocol and
// transport layer for this service.
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (service.getServiceName().equals(mServiceName)) {
// The name of the service tells the user what they'd be
// connecting to. It could be "Bob's Chat App".
Log.d(TAG, "Same machine: " + mServiceName);
} else if (service.getServiceName().contains("NsdChat")){
mNsdManager.resolveService(service, mResolveListener);
}
} @Override
public void onServiceLost(NsdServiceInfo service) {
// When the network service is no longer available.
// Internal bookkeeping code goes here.
Log.e(TAG, "service lost" + service);
} @Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
} @Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
} @Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
};
}

NSD的API使用这个接口的方法来通知你的应用何时开始搜索,何时失败,或者何时服务发现或丢失了(丢失即无法获取服务)。注意上述代码样例在一个服务搜索到了以后执行了一些检查。

  1. 找到的服务的服务名字,会和本地服务的服务名字进行匹配,来确定设备是否接受到了它自己发出去的广播。
  2. 检查服务类型,来检查你的应用是否能够连接此类型的服务。
  3. 检查服务名字来确认是否连接到了正确的应用。

检查服务名字并不总是必须的,如果你希望连接到某一个确切的应用的话才会需要。例如,这个应用可能可能只希望连接到在其他设备上运行的其自身的实例。然而,如果应用希望连接到一个网络打印机,那么吧服务类型设置为“_ipp._tcp”就够了。

在设置了监听器之后,调用discoverServices(),将你的应用要查找的服务类型,使用的搜索协议,和你刚创建过的监听器作为参数传入。

mNsdManager.discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

三). 连接网络上的服务

当你的应用发现了一个网络上要连接的服务,它必须首先使用resolveService()方法来确定该服务的连接信息。实现一个NsdManager.ResolveListener,并传递给该方法,并用它来获得一个包含连接信息的NsdServiceInfo

public void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener() { @Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Called when the resolve fails. Use the error code to debug.
Log.e(TAG, "Resolve failed" + errorCode);
} @Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.e(TAG, "Resolve Succeeded. " + serviceInfo); if (serviceInfo.getServiceName().equals(mServiceName)) {
Log.d(TAG, "Same IP.");
return;
}
mService = serviceInfo;
int port = mService.getPort();
InetAddress host = mService.getHost();
}
};
}

一旦服务解析完成了,你的应用会收到详细的服务信息,包括一个IP地址和一个端口号。这些就是用来和服务创建连接所需要的所有东西。


四). 应用关闭时注销你的服务

在应用的生命周期恰当地启用或禁用NSD功能是很重要的。在应用关闭时注销服务可以阻止其它应用误认为你的服务仍然是启用状态并尝试连接。同时,服务搜索是一个很消耗资源的操作,应该在父Activity被暂停时停止搜索,而在父Activity恢复时重新启用。覆写你的Activity生命周期函数,并插入代码来启动或停止服务广播,并在恰当地时机执行搜索。

//In your application's Activity

    @Override
protected void onPause() {
if (mNsdHelper != null) {
mNsdHelper.tearDown();
}
super.onPause();
} @Override
protected void onResume() {
super.onResume();
if (mNsdHelper != null) {
mNsdHelper.registerService(mConnection.getLocalPort());
mNsdHelper.discoverServices();
}
} @Override
protected void onDestroy() {
mNsdHelper.tearDown();
mConnection.tearDown();
super.onDestroy();
} // NsdHelper's tearDown method
public void tearDown() {
mNsdManager.unregisterService(mRegistrationListener);
mNsdManager.stopServiceDiscovery(mDiscoveryListener);
}

【Android Developers Training】 75. 使用NSD的更多相关文章

  1. 【Android Developers Training】 74. 序言:通过无线连接设备

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. 【Android Developers Training】 12. 支持不同屏幕

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. 【Android Developers Training】 77. 使用Wi-Fi P2P进行服务搜索

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  4. 【Android Developers Training】 9. 覆盖于布局之上的Action Bar

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. 【Android Developers Training】 8. 定义Action Bar风格

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  6. 【Android Developers Training】 7. 添加Action Buttons

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. 【Android Developers Training】 6. 配置Action Bar

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 【Android Developers Training】 5. 序言:添加Action Bar

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. 【Android Developers Training】 4. 启动另一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

随机推荐

  1. JDBC事务详解

    在JDBC的数据库操作中,事务是由一个或者多个操作组成的一个不可分割的工作单元.JDBC的事务处理包含三个方面:事务的自动提交模式(Auto-commit mode).事务隔离级别(Transacti ...

  2. Jmeter 初学(一)

    Jmeter 目前属于比较流行的测试工具,即可做自动化测试也可以做性能测试,而且比较方便. 环境准备: Jmeter 运行环境需要跑在java环境,首先需要安装一下java的环境,由于我目前使用的Jm ...

  3. 每天一道Java题[7]

    题目 什么是REST原则,请解释RESTful架构,以及其设计思想? 解答 REST,全称为Representation State Transfer,是一种互联网软件的架构原则.凡是满足REST原则 ...

  4. Java泛型的应用——T extends Comparable<? super T>

    在观察Java源码的时候,发现了这么一个写法T extends Comparable<? super T>.不禁纳闷为什么要这么写呢?有什么好处吗,extends和super在这里的作用着 ...

  5. 浏览器如何生成URL

    点击页面中的链接,浏览器会根据源码中相对URL路径作不同的处理: (1)有协议名称,但没有域名信息 对于这种形式的URL,它的协议,路径,查询字符串和片段ID都以它自身为准,但域名信息的部分,以引用它 ...

  6. Promise (2) 基本方法

    "I'm Captain Jack Sparrow" 加勒比海盗5上映,为了表示对杰克船长的喜爱,昨天闪现了几次模仿船长的走路姿势(哈哈哈,简直妖娆). 为了周天能去看电影,要赶紧 ...

  7. No matching provisioning profiles found for "Applications/MyApp.app”问题解决

    新开发的一个app打包报错,度娘谷歌了好久,废了不少时间,发现错误提示已经很明显了,只是自己没读懂而已,先说下问题和解决方法,给同意遇到这个问题的你: Failed to locate or gene ...

  8. HTML面试题

    1.你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? 所谓的“浏览器内核”无非指的是一个浏览器最核心的部分-“Rendering Engine”,直译叫做“渲染引擎”,我们也常称为“排版引擎 ...

  9. ubuntu下配置Apache+mod_wsgi+Django项目(个人测试)

    经过了一个星期的摸索,查找资料以及实验,我搭建的环境基本能用(还有就是Django后台的静态文件加载的问题) 这里面只是介绍一下我的过程,因为对应Apache还不是很熟练,特别是配置文件.只能供大家参 ...

  10. javascript走马灯的效果(文档标题文字滚动)

    做一些网站的时候,文档标题会滚动,这个效果是走马灯的效果. <!DOCTYPE html> <html> <head> <meta charset=" ...