Android Network Service Discovery "timeout"?
Asked Answered
T

2

20

I'm using Network Service Discover (NSD) on Android to advertise a REST server on the device. My REST server is wrapped in an instance of android.app.Service.

In the onCreate, I successfully start the REST server and register my NSD service. I can see the NSD service name on other devices within my network (e.g. My Service).

In my onDestroy, I stop the REST Server and unregister the NSD service.

However, it seems that while developing, when I push a new instance of my app (via Eclipse), there's no guarantee that the service's onDestroy will be called. This means that My Service is still advertised, and the next time the service starts, I end up with My Service (1), My Service (2), etc.

I understand that NSD will amend the service name to create a unique instance. My questions are:

  1. What's the expected timeout for advertised services to be removed if they no longer exist?
  2. Is there a way to ensure my onDestroy is being called when I push a new instance of my app? Would this circumstance be similar to what a user might experience if they install an update of my app? i.e. is the onDestroy guaranteed to be called when a running app is shutdown so a new version of that app can be installed?
  3. Is there a way within my app to detect the old registrations and remove them?
Topsail answered 23/3, 2014 at 3:25 Comment(6)
If you want to ensure that onDestroy is being called, try using the stopService method.Birthstone
@Philip My current workaround is to have a button which I can press to stop the service prior to re-installing the app. However, if I can get Questions 1 and 3 answered, it would make me feel better when I release the app :). For that matter, is Question 2 (pushing a new instance of the app) relevant/similar to pushing an update via Google Play? i.e. is there a chance that the service's onDestroy won't be called if/when the user updates the version?Topsail
Hmm, it seems that questions 1 and 3 would not be issues if you met the condition for question 2.Birthstone
I am facing the same problem. Did you figure out a way to do this?Forswear
OnDestory may or may not be called, there is no way to ensure it. You could try to fiddle some wifh a shutdown hook. However, that's not 100% guaranteed either: developer.android.com/reference/java/lang/Runtime.htmlUlcerous
Runtime shutdown hooks are not triggered in Android. I've tested onDestroy and it works around 30% of the time. My other idea was to have a notification that tells the user to stop the service, and have the service run in the background even if the activity is closed.Curate
R
6

First of all, there is an open issue tracking this problem on the AOSP issue tracker. The person who filed the bug could repro in all Android 4.x versions but did not test with 5.x.

A brief explanation of terms:

  • NsdManager - the class that Android applications interface with to register their NSD service and discover other NSD services. NsdManager is really just a shell that establishes an asynchronous connection with NsdService.
  • NsdService(com.android.server.NsdService) - hidden Android system service that can have connections from multiple clients. Does all the heavy lifting and book-keeping for creating and managing NSD service and communicating events back to client applications.
  • NSD service - the service that NsdService creates on your behalf that advertises your app's presence over the network.

I just tested this issue with 5.1 and the issue is fixed(from the source it looks like the fix went into 5.0). I created a simple application that just registers an NsdService and logs when anything of note happens, including lifecycle events onCreate, onResume, onPause, and onDestroy. I got the following logs by rebooting, starting my application, and then reinstalling via Android Studio.

W/SimpleNsdActivity(14379): onCreate
D/NsdService( 3516): startMDnsDaemon
D/NsdService( 3516): New client listening to asynchronous messages
D/NsdService( 3516): New client, channel: com.android.internal.util.AsyncChannel
@3c114a11 messenger: android.os.Messenger@3213e776
D/NsdService( 3516): Register service
D/NsdService( 3516): registerService: 2 name: NsdTest, type: _http._tcp., host:
null, port: 1349
W/SimpleNsdActivity(14379): onResume
D/NsdService( 3516): Register 1 2
W/SimpleNsdActivity(14379): onServiceRegistered as NsdTest
D/NsdService( 3516): SERVICE_REGISTERED Raw: 606 2 "NsdTest"
D/NsdService( 3516): Client disconnected
D/NsdService( 3516): Terminating client-ID 1 global-ID 2 type 393225
D/NsdService( 3516): unregisterService: 2
D/NsdService( 3516): stopMDnsDaemon
W/SimpleNsdActivity(15729): onCreate
D/NsdService( 3516): startMDnsDaemon
D/NsdService( 3516): New client listening to asynchronous messages
D/NsdService( 3516): New client, channel: com.android.internal.util.AsyncChannel
@38bace07 messenger: android.os.Messenger@26d8cd5d
D/NsdService( 3516): Register service
D/NsdService( 3516): registerService: 3 name: NsdTest, type: _http._tcp., host:
null, port: 1349
D/NsdService( 3516): Register 1 3
W/SimpleNsdActivity(15729): onResume
D/NsdService( 3516): SERVICE_REGISTERED Raw: 606 3 "NsdTest"
W/SimpleNsdActivity(15729): onServiceRegistered as NsdTest

You can see that neither onPause() nor onDestroy() were called and thus my application did no explicit cleanup for the NSD service. NsdService is detecting when the AsyncChannel to the client is terminated and is cleaning it up properly. You can get a deeper understanding of the NsdService logs by looking at the source here.

Summing it all up, I'll respond to the questions from the prompt:

  1. For 4.x there is no timeout, it will stay up until reboot. For 5.x NsdService shuts down the NSD service when your app disconnects.
  2. There is no way to guarantee onDestroy() is called, a deeper explanation here. Installation via Android Studio killing the running application without it being called. Would be interesting to see if updates via the App Store behave the same way or more gracefully shut down the running app.
  3. Not a problem in 5.x since it kills them right away. In 4.x, there's no way to remove old registrations since you immediately lose the registration listener.
Rhinencephalon answered 11/8, 2015 at 5:9 Comment(1)
Exactly what I was looking for. I was wondering why I had to restart my device for NSD to work (in my testing). I'm glad 5.x solves this issue, but it's too bad there's really nothing to do except use onPause/onResume (as a last resort).Curate
L
0

Regarding the points 2) and 3), you can do the following:

  • In your service's onCreate() method, generate a random number or a timestamp and save it to a local field; this will be the service ID
  • Register a custom BroadcastReceiver in your Service, it will react to your custom ACTION
  • At each Service creation, send a broadcast with your custom ACTION and your current Service ID as extra param
  • For each active Service, the registered BroadcastReceiver will be triggered; now compare the received ID with the current service's ID
    • if they differ, invoke stopSelf()

In this way, only the last Service will stay alive.

Lineage answered 7/8, 2015 at 10:6 Comment(3)
You can't unregister a service once the activity finishes, because you need the service listener. I guess you could set the service id to a timestamp, and then connect to the service with the largest id.Curate
@DaveChen A service can stop itself whenever it wants (in this case, when it receives a broadcast). Activities don't matter.Lineage
@DaveChen I think there's confusion over the liberal use of the term "Service" in the prompt. My understanding is that the Android Service is always getting shut down when the application is updated, but that its onDestroy() method isn't always called. This causes the NSD Service to "leak".Rhinencephalon

© 2022 - 2024 — McMap. All rights reserved.