The Situation:
Note: This is a followup of a previous question that takes care of how to setup the handler/message communication between activity/service. See: link
Activity connects to a started Service using binding. Activity gets a local binder with a reference to a service Handler back. Activity and Service exchange Message objects through eachothers Handlers. When user is done with App, user signals Service to quit the started Service (shutdown the service). Within the service (=mainThread) another thread is running, the serviceThread. The serviceThread is capable of running more subthreads, fully controlled by the serviceThread. Communication is handled between activity and the serviceThread, not through the mainThread of the service!!!
The Problem:
How do I gracefully shutdown the Service when inside the Service several threads are running endlessly (until I signal a message saying: "pack your backs, go home!", aka: EXIT_SERVICE.
Solution candidates:
Scenario 1: from the activity side, send a EXIT_SERVICE message to the serviceThread that is running within the service (not the mainThread!). When all subthreads of serviceThread have been cleanup/stopped send a message back to the activity indicating that it is now safe to call the stopService(Intent) method which actually stops the service. The activity can now call finish() and the App is exited gracefully.
Scenario 2: from the activity side, send a EXIT_SERVICE message to the serviceThread which will cleanup all subthreads. After that is done the serviceThread sends a message to the activity that the service is to be shutted down completely and after that message is sent, sends a message to the mainThread handler of the service to actually shutdown the service. The service receives the shutdown-message and cleans up varialbles and calls stopSelf(int).
The service is stopped and the activity knows that it can stop too, WITHOUT calling stopService(Intent)! The App is exited gracefully.
Scenario 3: from the activity side, call the stopService(Intent) method, which will deliver the Intent to the service to stop the service. In the service this Intent is intercepted (I don't know if this is possible and how to do that yet... but assuming this can be done) before the actual service code that stops the service is executed. Before the service actually stops, other code is executed first which cleans up the threads by sending a EXIT_SERVICE message to the serviceThread. After the serviceThread has cleaned up, it sends a message back to the mainThread (the service itself) and the code continues to execute the normal code that was normally executed when the Intent to stop the service wasn't intercepted. The App is exited gracefully.
So, I have three options on how to gracefully stop the Service. The problem is which scenario is the "best" (less error prone, quickest in shutting down, easiest to implement). For example: what happens when the activity is destroyed because the user switched portrait/landscape mode right at the moment the "stop service" message or Intent was sent?
I think scenario 3 is a nice solution, because it doesn't need a lot of extra coding on the activiy side, only stopService(Intent) and finish(). The service can also be in the process of stopping while the GUI is already gone. Only thing is how to intercept the stop Intent and act upon that signal.
Please share your thoughts....
Related
I am a beginner in Android App Development. I am trying to implement IntentService to run a process in the background when the user taps on the 'Start Service' button in main activity. The process is small, and usually finishes off right after few seconds of tapping the button. But that small process has to be run periodically, even after the app closes. Here is the code:
protected void onHandleIntent(Intent intent)
{
Log.i(TAG, "The service has now started");
//getting the source code of a html link
Log.i(TAG,"Task has been completed");
}
If I keep the Main Activity opened during the IntentService process, I get both the messages in the Log Cat. But if I close the app right after pressing the 'Start Service' button in Main Activity, I get only the first message i.e. 'The service has now started'. Does this mean that the process doesn't finish if I close the app? Why is this happening? What should I do to overcome this problem?
According to IntentService docs, there is no link between your service and the application main thread.
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through startService(Intent) calls; the service is started as needed,
handles each Intent in turn using a worker thread, and stops itself
when it runs out of work.
This "work queue processor" pattern is commonly used to offload tasks
from an application's main thread. The IntentService class exists to
simplify this pattern and take care of the mechanics. To use it,
extend IntentService and implement onHandleIntent(Intent).
IntentService will receive the Intents, launch a worker thread, and
stop the service as appropriate.
I think you aren't getting the log in LogCat because application closes. But the service are logging without your application tag.
I'm working on this project where I need Location updates plus data uploads even when the main Application is not in the foreground. I'm thinking of placing all location and networking functions in a Service (XService, for argument's sake) and communicate between the Activity (Main) and XService using broadcasts within the same process (no access for other apps).
I have not yet written the code, but the work flow goes something like:
1. Main -- Start. In onCreate, start a new Thread, and on it
instantiate and bind XService. 2. Main -- Login screen. Get data,
package it in an Intent and broadcast to XService. 3. XService --
Receive broadcast, unpack intent. Setup socket and data streams. Setup
LocationManager for updates. Broadcast to Main that the network is
ready. Upload data every time LocationManager updates.
4. Main -- Upon receiving the network-ready broadcast, prepare Intent for initialization and broadcast. 5. XService -- Receive
initialization-request broadcast, unpack, perform network task,
broadcast response. 6. Repeat ad nauseam as per user
interaction and any timed updates. x. Main -- On logout,
broadcast to XService. x+1. XService -- Send logout info to
server, close sockets, broadcast to Main that it is exiting
gracefully, then stopSelf(). x+2 Main -- Complete logout
procedure.
My main question is -- is this the right way of going about it?
Another question is: What happens when XService broadcasts a Location update to Main, but Main is stopped (i.e. not running in foreground)? I assume that since the BroadcastReceiver on Main is not instantiated due to Main being in a stopped state, the broadcast essentially does nothing. How does this unreceived broadcast impact (or not, as the case may be) XService or the process / device in general?
That makes sense. My suggestion is starting the service on the main thread, then forming your new thread within the Service class. This would package the Service nicely so that you don't have to spawn a thread to use it, you can just call it.
If Main is stopped and the Broadcast sends a message, it's a lost message. You want to avoid those by registering the receiver at OnStart and unregistering at OnStop. You can save any pertinent information that the Activity may need to know in the Service itself.
An alternative to the Broadcast Receiver is the Messenger class. (http://developer.android.com/reference/android/os/Messenger.html)
There is some long processing that need to be completed, so I put it in a service. The activity must be able to connect to the service, show the user current results from the service. So I start the service with start Service and later call bind Service (with BIND_AUTO_CREATE) as in LocalService from http://developer.android.com/reference/android/app/Service.html#ServiceLifecycle. I want it to run until its job is done, and then self stop, even if client is still connected to it. (or determine the client to unbind) Is any way to do it with the sample LocalService?
I was considering passing a handler to the service so that it can send messages back to the activity, but I don't want the activity to get leaked. I am just getting used with services, so maybe I am misusing something.
EDIT: The workload consists of several threads, synchronized and run in parallel, so I guess is not a good candidate for intent service. Some data analysis is done in background service, and when the user restarts the activity that started the service, it should display some graphics according to current values computed by background service. All background processing is triggered at the beginning, and need only inspection later on, when activity connects to it. Android should not be able to stop the service. When the job is finished, the service should be able to terminate even if the activity is connected to it.
I just recorded a callback with the service. If the activity is not connected to service, it sets the callback to null. In this callback I call stopService() and then finish() on the activity. I am not sure that it is the best method, but it works fine for me.
If you want a service to be stopped when it is finished, I think what you are looking for is IntentService, they work as services, but run in another thread and when they are completed they dissappear.
Check this out
EDIT: NickT link is better, check that out! :)
I have a chat client activity which continously filtes incoming packets using a while(true) loop. On the basis of message contained in the packets, I need to start and stop a service. The service performs an intensive long running task. Since, the service starts in the same thread of the caller Activity, I cannot start the service and also continue to filter packets, otherwise it shows an ANR dialog.
How can I keep both the activity and the service doing their tasks and also the activity to be able to later inform/stop the service?
I started the service in a new thread but that still gave an ANR.
Edit: Using AsyncTask and BroadcastReceiver works only if packet-filtering is stopped at the time of calling the service. Simultaneously doing both gives ANR.
Use a Broadcast Receiver and register with a particular intent-filter. Broadcast an intent whenever you need to do any processing based on the content of your chat messages. Within the receiver you could take whatever action you need to.
I have an IntentSerivce that runs in the background sometimes and it can be quite a long running process in certain instances. I give an option for the user to quit the application which basically just stops and polling and ignores any GCM push notifications. But if a push notification came in and the IntentService is taking a while to finish doing what it has to (gets information from a server and sends a notification to the user if needed, like a new message arrived or something).
Here is the problem, if the user elects to "Quit" the app while the intentservice is still running they will still get the notification which I do not want. I know that there is the stopSelf() method for the service but I need to stop it in an activity when I know the user "Quit" the application via a menu button. Sending another intent to the service does not work since the intents get queued up and calling context.stopService(intent); in my activity does not work either so how else can I stop it?
Are you passing a new Intent into stopService(Intent) or the original one used in startService(Intent). Passing the original Intent should stop the service.
Failing that you could use a Handler to pass the service a Message. Have the IntentService implement the Handler.Callback interface and create a new Handler(Handler.Callback) in your Activity, passing your IntentService as callback. Then implement the onHandleMessage() in your IntentService to call stopSelf() and have your Activity pass a message to it when you want it to stop.
Below code is perfectly working fine for me to stop IntentService from Activity:
stopService(new Intent(getApplicationContext(), MyIntentService.class));