要在 local 網路註冊 service
首先得創建一個 NsdServiceInfo 物件
這物件提供給 local 網路內的其他裝置一些資訊
讓它們決定是否與你的裝置連接
NsdManager mNsdManager;
public void registerService(int port) {
// 創建 NsdServiceInfo 物件並填充它
NsdServiceInfo serviceInfo = new NsdServiceInfo();
// 若在同一網路中其他裝置也有相同服務名稱,Android會自動把其中一台的 ServiceName 改變避免衝突
// 例如 "NsdChat" 可能會被改名成 "NsdChat(1)" 這樣的名稱
serviceInfo.setServiceName("NsdChat");
// ServiceType 的語法為 "_<protocol>._<transportlayer>"
// 所以 uses HTTP protocol running over TCP 寫成 "_http._tcp."
serviceInfo.setServiceType("_http._tcp.");
serviceInfo.setPort(port);
mNsdManager = Context.getSystemService(Context.NSD_SERVICE);
//註冊 service
mNsdManager.registerService(
serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}為了避免你的 service 使用的 port 發生衝突,我們要取得一個可用 port
如下例是設定 port 為 0 讓 socket 可以使用任意可用的 port
public void initializeServerSocket() {
// 初始化 server socket 於下個可用 port
mServerSocket = new ServerSocket(0);
// 儲存被選到的 port
mLocalPort = mServerSocket.getLocalPort();
...
}在 registerService 前我們必須實作 RegistrationListener 以接收註冊 service 成功與否的訊息
因為 registerService 是非同步的,所以 service 建立後才要做的事情必須放在 onServiceRegistered 裡
public void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
@Override
public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
// 儲存 service name. 因為名稱有可能與原本設定的不同所以這裡要更新為實際名稱
mServiceName = NsdServiceInfo.getServiceName();
}
@Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// 註冊失敗! 依錯誤碼進行處理
}
@Override
public void onServiceUnregistered(NsdServiceInfo arg0) {
// Service 反註冊。在我們呼叫 NsdManager.unregisterService() 後才可能被觸發
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Service 反註冊失敗。依錯誤碼進行處理
}
};
}* Discover Services on the Network
Service discovery 跟 service registration 一樣有兩個步驟:
建立 discovery 的 listener 跟 callback 然後呼叫非同步執行的 discoverServices()
public void initializeDiscoveryListener() {
// new 一個 DiscoveryListener 實體
mDiscoveryListener = new NsdManager.DiscoveryListener() {
// Discovery 開始時就會被呼叫
@Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
}
@Override
public void onServiceFound(NsdServiceInfo service) {
// 發現 Service
Log.d(TAG, "Service discovery success" + service);
if (!service.getServiceType().equals(SERVICE_TYPE)) {
// 還記得嗎?Service type 是包含 protocol 跟 transport layer 的字串
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (service.getServiceName().equals(mServiceName)) {
// service 名稱
Log.d(TAG, "Same machine: " + mServiceName);
} else if (service.getServiceName().contains("NsdChat")){
mNsdManager.resolveService(service, mResolveListener);
}
}
@Override
public void onServiceLost(NsdServiceInfo service) {
// 當網路失效
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 透過 DiscoveryListener 去通知你 discovery 的狀態注意上例在找到 service 時進行了一些檢查:
1. 找到的 service name 必須與 local 的 service name 比對以確定裝置是否接收到自己的 broadcast
2. service type 會被檢查以驗證你的應用程式可以連接
3. service name 會被檢查以確定連接到正確的應用程式
然而檢查 service name 並不是必要的,例如應用程式可能只想要連接到其他裝置上相同應用程式的實體
(像網路印表機就只需要檢查service type 是 "_ipp._tcp")
設定好 DiscoveryListener 後就可以呼叫 discoverServices()
mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
* Connect to Services on the Network
當找到可以連接的網路服務,首先要用 resolveService() 判讀連線資訊
我們必須實作 NsdManager.ResolveListener 接收結果並取得包含連線資訊的 NsdServiceInfo
public void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
// 解析失敗。使用錯誤碼進行除錯
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();
}
};
}* Unregister Your Service on Application Close
在應用程式的生命週期裡適當的啟用和停用 NSD 功能是很重要的
當應用程式關閉時將 NSD 反註冊有助於防止其他應用程式認為它仍在活躍中並嘗試連接它
而 service discovery 是很耗效能的操作,所以應該在 activity pause 的時候停止、在 resume 時重新啟用
//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);
}相關資料:
Connecting Devices Wirelessly
YouTube-DevBytes: Network Service Discovery
Using Network Service Discovery
沒有留言:
張貼留言