在你的应用程序中添加网络服务发现(NSD)允许你的用户识别在本地网络中的设备以便支持在你的应用程序中服务的请求。这将对于不同的点对点的应用程序很有用,比如文件分享和多人玩的游戏。android平台的NSD的API简化你实现这项功能所需要作出的努力。
这节课将会向你展示怎么样创建一个能够广播它的名字和连接信息给本地网络和浏览从其他应用程序做相同的工作的应用程序。最后,这节课将会向你展示怎么样连接在其他设备上的同一个应用程序。
在网上注册你的服务
这一步是可选的。如果你不关心在本地网络广播你的应用程序的服务,你可以跳到下一步,Discover Services on the Network.
在本地网络注册你的服务,首先创建一个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)"的名字。
第二个参数设置服务的类型,决定应用程序使用哪一个端口和传输层。语法是<protocol>._<transportlayer>。在代码段中,服务在TCP上使用HTTP协议。一个提供打印服务(例如,网络打印机)的应用程序把服务的类型设置为"_ipp._tcp"。
注意:国际地质分配机构集(IANA)中管理,授权服务发现协议的服务类型的清单,例如NSD和Bonjour。你可以从the IANA list of service names and port numbers下载清单。如果你打算使用一种新的服务类型,你应该通过填写IANA Ports and Service registration form储存它.
当为您的服务设置端口,避免硬编码它像这个冲突与其他应用程序。例如,假设您的应用程序总是使用端口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.
}
};
}
;
现在你有所有的片段注册你的服务。调用int, android.net.nsd.NsdManager.RegistrationListener) 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);
}
;
发现在网络中的服务
网络是充满生命,从残忍的网络打印机的温顺的网络摄像头,残酷的、激烈的战斗附近的井字的球员。让您的应用程序看到这充满活力的生态系统的功能的关键是服务的发现。您的应用程序需要监听在网络上广播的服务查看哪些服务是可用的,和过滤掉所有不能处理的应用程序。
服务的发现,就像服务的登记,有两个步骤:对有关的回调设置发现监听器,制作一个异步的API调用int, android.net.nsd.NsdManager.DiscoveryListener) 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”。
设置监听之后,调用int, android.net.nsd.NsdManager.DiscoveryListener) discoverServices()把你的应用程序应该寻找的类型传递给它。使用发现协议和你创建的监听器。
mNsdManager.discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
;
连接网络上的服务
当你的应用程序发现了网络中和的服务去连接的时候,必须先确定服务的连接信息。使用android.net.nsd.NsdManager.ResolveListener) 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覆盖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);
}
;