注:本文翻譯自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使用這個接口的方法來通知你的應用何時開始搜索,何時失敗,或者何時服務發現或丟失了(丟失即無法獲取服務)。注意上述代碼樣例在一個服務搜索到了以后執行了一些檢查。
- 找到的服務的服務名字,會和本地服務的服務名字進行匹配,來確定設備是否接受到了它自己發出去的廣播。
- 檢查服務類型,來檢查你的應用是否能夠連接此類型的服務。
- 檢查服務名字來確認是否連接到了正確的應用。
檢查服務名字并不總是必須的,如果你希望連接到某一個確切的應用的話才會需要。例如,這個應用可能可能只希望連接到在其他設備上運行的其自身的實例。然而,如果應用希望連接到一個網絡打印機,那么吧服務類型設置為“ _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); }
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
