From 73f1ba806fe5fa0dad2334b168deefaf6d8ca5c3 Mon Sep 17 00:00:00 2001 From: Dave Platt Date: Fri, 28 Mar 2014 13:54:12 -0700 Subject: [PATCH] Update sample NSD app to work reliably with current Android Change the management of NSD Listener objects to one which ensures that each Listener will be used for only one NSD service request at a time (as the APIs were intended to function). Listeners are now allocated in a "lazy" on-demand way, rather than having one Listener of each type allocated at application startup time. When the Register or Discover button is clicked, any existing Listener request of that type will be canceled and the Listener discarded (avoiding the need to wait until the Listener callback has been completed), and a new Listener will be allocated. Moves code around in the application-lifecycle callbacks to reflect the current Android lifecycle. Doing important things like unregistering NSD services should not be done in the onDestroy() callback, as there is no guarantee that the app won't be killed before this callback is invoked. This can lead to "zombie" NSD registrations in KitKat. Adds additional lifecycle logging. Pass 2 - fix typo, clean up trailing whitespace Pass 3 - fix onStart/onRestart mismatch Bug: 13512512 Change-Id: Ic164110759204b27d8a14376777b593ebe1865fa --- .../android/nsdchat/ChatConnection.java | 12 +++-- .../android/nsdchat/NsdChatActivity.java | 45 ++++++++++++++---- .../example/android/nsdchat/NsdHelper.java | 46 +++++++++++++------ 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java b/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java index 80aa9fd7c..534f218bd 100644 --- a/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java +++ b/samples/training/NsdChat/src/com/example/android/nsdchat/ChatConnection.java @@ -52,7 +52,9 @@ public class ChatConnection { public void tearDown() { mChatServer.tearDown(); - mChatClient.tearDown(); + if (mChatClient != null) { + mChatClient.tearDown(); + } } public void connectToServer(InetAddress address, int port) { @@ -64,15 +66,15 @@ public class ChatConnection { mChatClient.sendMessage(msg); } } - + public int getLocalPort() { return mPort; } - + public void setLocalPort(int port) { mPort = port; } - + public synchronized void updateMessages(String msg, boolean local) { Log.e(TAG, "Updating message: " + msg); @@ -142,7 +144,7 @@ public class ChatConnection { // used. Just grab an available one and advertise it via Nsd. mServerSocket = new ServerSocket(0); setLocalPort(mServerSocket.getLocalPort()); - + while (!Thread.currentThread().isInterrupted()) { Log.d(TAG, "ServerSocket Created, awaiting connection"); setSocket(mServerSocket.accept()); diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java index 47ee09893..5782634ff 100644 --- a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java +++ b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdChatActivity.java @@ -43,6 +43,7 @@ public class NsdChatActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Log.d(TAG, "Creating chat activity"); setContentView(R.layout.main); mStatusView = (TextView) findViewById(R.id.status); @@ -54,11 +55,6 @@ public class NsdChatActivity extends Activity { } }; - mConnection = new ChatConnection(mUpdateHandler); - - mNsdHelper = new NsdHelper(this); - mNsdHelper.initializeNsd(); - } public void clickAdvertise(View v) { @@ -100,26 +96,59 @@ public class NsdChatActivity extends Activity { mStatusView.append("\n" + line); } + @Override + protected void onStart() { + Log.d(TAG, "Starting."); + mConnection = new ChatConnection(mUpdateHandler); + + mNsdHelper = new NsdHelper(this); + mNsdHelper.initializeNsd(); + super.onStart(); + } + + @Override protected void onPause() { + Log.d(TAG, "Pausing."); if (mNsdHelper != null) { mNsdHelper.stopDiscovery(); } super.onPause(); } - + @Override protected void onResume() { + Log.d(TAG, "Resuming."); super.onResume(); if (mNsdHelper != null) { mNsdHelper.discoverServices(); } } - + + + // For KitKat and earlier releases, it is necessary to remove the + // service registration when the application is stopped. There's + // no guarantee that the onDestroy() method will be called (we're + // killable after onStop() returns) and the NSD service won't remove + // the registration for us if we're killed. + + // In L and later, NsdService will automatically unregister us when + // our connection goes away when we're killed, so this step is + // optional (but recommended). + @Override - protected void onDestroy() { + protected void onStop() { + Log.d(TAG, "Being stopped."); mNsdHelper.tearDown(); mConnection.tearDown(); + mNsdHelper = null; + mConnection = null; + super.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(TAG, "Being destroyed."); super.onDestroy(); } } diff --git a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java index 568a79bbd..5111318cf 100644 --- a/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java +++ b/samples/training/NsdChat/src/com/example/android/nsdchat/NsdHelper.java @@ -44,8 +44,6 @@ public class NsdHelper { public void initializeNsd() { initializeResolveListener(); - initializeDiscoveryListener(); - initializeRegistrationListener(); //mNsdManager.init(mContext.getMainLooper(), this); @@ -78,22 +76,20 @@ public class NsdHelper { mService = null; } } - + @Override public void onDiscoveryStopped(String serviceType) { - Log.i(TAG, "Discovery stopped: " + 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); } }; } @@ -125,48 +121,68 @@ public class NsdHelper { @Override public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) { mServiceName = NsdServiceInfo.getServiceName(); + Log.d(TAG, "Service registered: " + mServiceName); } - + @Override public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) { + Log.d(TAG, "Service registration failed: " + arg1); } @Override public void onServiceUnregistered(NsdServiceInfo arg0) { + Log.d(TAG, "Service unregistered: " + arg0.getServiceName()); } - + @Override public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + Log.d(TAG, "Service unregistration failed: " + errorCode); } - + }; } public void registerService(int port) { + tearDown(); // Cancel any previous registration request + initializeRegistrationListener(); NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setPort(port); serviceInfo.setServiceName(mServiceName); serviceInfo.setServiceType(SERVICE_TYPE); - + mNsdManager.registerService( serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); - + } public void discoverServices() { + stopDiscovery(); // Cancel any existing discovery request + initializeDiscoveryListener(); mNsdManager.discoverServices( SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); } - + public void stopDiscovery() { - mNsdManager.stopServiceDiscovery(mDiscoveryListener); + if (mDiscoveryListener != null) { + try { + mNsdManager.stopServiceDiscovery(mDiscoveryListener); + } finally { + } + mDiscoveryListener = null; + } } public NsdServiceInfo getChosenServiceInfo() { return mService; } - + public void tearDown() { - mNsdManager.unregisterService(mRegistrationListener); + if (mRegistrationListener != null) { + try { + mNsdManager.unregisterService(mRegistrationListener); + } finally { + } + mRegistrationListener = null; + } } }