Stop background service only if no more work - android

I use a service to process intents with ContentResolver in the background.
Each new intent calls starts the service and the service stops itself once the processing is over.
This creates an issue where new intent starts a service run but the previous service run is still processing, resulting in killing the new run before its processing is over.
I thought about adding some kind of static 'nunInstances' in the service and stop it only of it's 1.
Another option is leaving the service running. I've tried looking for information about it's validity and side effects but came up empty.

You can use an IntentService, which acts as a work queue. You can send it work to do with startService() as many times as you like. The work is performed serially, one after another. When the last work unit has been performed the Service stops itself.
Or you use a regular Service and queue the work yourself in onStartCommand(). When you have no more work to do, you call stopSelf().
NOTE: Android will not create more than one instance of a Service. Your concern about having multiple instances of the Service running is not necessary. If something calls startService() and the Service is already running, Android does not create a new instance of the Service. It just called onStartCommand() on the running instance and passes the Intent as a parameter.

Related

Android - Service or IntentService?

I have an app that runs certain "long" process when it is opened or when the device is booted.
The process performs certain amount of downloads via the download manager, and when the process finishes it sends a broadcast event to the app, to capture some analytics.
As I understand, whenever the boot broadcast event is called, the Application class is intantiated (if not in memory already).
Application class is also initiated when the user opens the app.
Based on that, the code to run the proccess is placed inside onCreate method of the Application class. (the process starts a new thread from there)
However, it appears that the "long" process is taking more than the time-to-live of the boot broadcast receiver (about 10s?), so the app process is killed. Then a couple of seconds later, after the download is finished a second broadcast is sent and the app is started again calling Application.onCreate again, with some undesirable effects.
The process was expected to run in a single linear way and not calling again onCreate.
As this process happens only once, I was thinking on creating a Service to give more time to the app to stay alive. However the process depends on DownloadManager so:
1) Using IntentService won't work (as I understand) because the process is still killed after handleIntent returns. The process creates new threads so its tasks are async, and handleIntent method will return before the whole process finishes.
2) Should I create a Service which is simply 'alive' for some time and then is stopped after the process finishes?
3) As an additional question, if I have a running Service, will Application class be instantiated and kept in memory until the Service is stopped?
If you do this only once, you should just create your own Service. When your app starts, call startService(). In onStartCommand() of your Service, start a background Thread that does what you want and return START_STICKY (This keeps your Service running until you stop it). When the Thread completes, call stopSelf() on the Service.
Of course, you can use IntentService, but that class is just an extension of Service that provides another layer of convenience for you. IntentService manages a pool of background threads and takes care of starting and stopping the Service when it runs out of work. This is probably overkill for what you need, but go ahead and use it if you want. The tradeoffs here are negligible.
NOTE: <opinion>For some reason, lots of developers seem to like certain "fad" solutions, like AsyncTask, IntentService, and Event Bus. There are plenty of other ways to solve problems. There is no "one size fits all" in software development.</opinion>
You could still use an Intent Service, you just need to block while the background task is running. Implementation could work like this:
Put it in a service, an IntentService could work like so:
public class DownloadIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
//get url or whatever from intent
//kick off async code to start the download, something like eg:
DownloadTask downloadTask = new DownloadTask(url);
downloadTask.setListener(new Listener() {
public void onComplete(Download download) {
DownloadIntentService.this.notify();
}
}
downloadTask.start()
wait();
}

How to self stop a service started with BIND_AUTO_CREATE?

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! :)

Please, explain some Android Service Concepts

I'm just approaching Android Services, but i have many doubts. Here there are some questions.
Before starting, notice i have read those pages
Android Services (official)
Bounded Services (official)
plus sone Inner classes theory in my language. Please be patient, i'm still a bit confused.
1) First of all, a Services differentiates itself from an AsyncTask mainly because it continues to run also if the app is paused (i.e. the user is watching another app); AsyncTask is stopped in that cases. Is it ok or am i wrong?
2) A Service runs in the same thread of the activity that started it through startService().
To not affect the Activity's performances, i have to create a separate thread for that Service, for example implementing the Runnable interface. Another method is making a service that extends IntentService, which automatically provides a new thread for the service: a new thread is created on any onHandleIntent() call.
Now, let's look at my concrete problem. I need to create a Service that will be used by many Activities: his task will be to connect to the server DB every 60 seconds and check for news. If a news is found, notify there's a new news (if we are on MainActivity) or show the new news's title (if we are in the news reader). How should i code it?
I have made a MainActivity that instantiate a NewsService and immediately calls startService(). On the other side, i have the NewsService extends IntentService, that (creates a new thread when onHandleIntent is called?) and looks for new news. Is it a bad idea to use a IntentService? I realized it will be very ugly to start the service calling startService() indefinitely.
At the start of this exercize i tought it was a good solution because it automatically creates a new thread and makes Service implementation simple. But now i have some doubts (i can't know if there's a news! How can MainActivity know it? And how to get the title)
This should be done with a normal extends Thread class, that makes an infinite cicle in it's run() method, checking for news every 60 seconds and, if there's a new one, reads the title from remote DB AND update activities buttons/views. Then if the App will be closed by user, the Service will be closed too.
But the problem is that if i istantiate such class it's work will be stopped when the MainActivity is paused or stopped, and other Activities (the NewsReader in this case) cannot get any update because the new thread isn't getting news at the moment. So i need a Service.
I hope it's clear. How should i implement a solution in the right way?
Please highlight everything wrong in my text, i really need to learn :D
You seem to have understood everything correctly.
As to your specific problem, I'd recommend the following:
Use AlarmManager to schedule your service. Don't let the Service run when it does not have to.
Use a Broadcast Intent for new news. All Activities will have to have an inner BroadcastReceiver that listens for the Broadcast intent of the service and reacts accordingly.
Services are a good approach for what you want, they are pretty good to do processes that consume few resources like keeping a daemon in background, they are also good to show notifications without an activity and keep running even if you exit the activity.
When you need to do more heavy operations in your service you can still use an AsyncTask, launch it, execute your operation in another thread and automatically receive the result in your main thread.
If you want to keep the service always running you can use START_STICKY in your Service
#Override
public int onStartCommand(final Intent intent, final int flags,
final int startId) {
// Ensure the service will restart if it dies
return START_STICKY;
}
And you can launch the service doing:
final Intent service = new Intent();
service.setComponent(new ComponentName(YourService.PACKAGE_NAME,
YourService.SERVICE_FULL_NAME));
// Start the service
context.startService(service);
1) First of all, a Services differentiates itself from an AsyncTask
mainly because it continues to run also if the app is paused (i.e. the
user is watching another app); AsyncTask is stopped in that cases. Is
it ok or am i wrong?
This is not correct. AsyncTask is a mechanism for offloading background processing into a separate thread and provides a mechanism for informing the user of progress, error and completion of that background processing. AsyncTask does not stop working if the app is paused. It continues to perform its processing in the background. In general, there is tight coupling between an AsyncTask and the Activity that started it.
A Service, on the other hand, is (generally) completely decoupled from the Activity that started it. A Service has its own lifecycle which is independent of the other activities in the app. Also, services have no UI, so they are not linked to the visual elements of the application and they provide no (direct) mechanisms for visual feedback related to progress, error or completion. This needs to programmed separately.
2) A Service runs in the same thread of the
activity that started it through startService(). To not affect the
Activity's performances, i have to create a separate thread for that
Service, for example implementing the Runnable interface. Another
method is making a service that extends IntentService, which
automatically provides a new thread for the service: a new thread is
created on any onHandleIntent() call.
This isn't correct either. A Service doesn't run on any specific thread. The lifecycle methods of a Service (onCreate(), onStartCommand(), etc.) run on the main (UI) thread, which may or may not be the same thread that called startService(). However, a Service can (and usually does) start other background threads, as many as it needs to, to perform the necessary work.
IntentService is a specific kind of Service which manages one or more worker threads that it uses to perform background processing. You send a "command" to an IntentService and the IntentService then puts your command in a queue. At some point (different implementations in different Android versions behave differently), your "command" is dequeued and processed in a background thread. IntentService stops itself after all the "commands" have been processed. IntentService is often not the best choice for a Service due to the way it behaves.
IntentService is definitely not what you want in your case. You should probably use AlarmManager and set an alarm that starts your Service every minute. When your Service starts, it should create a background thread that contacts your server and checks for news. If there is no news it can just go away. If there is new news, it can either start your Activity to inform the user, or it can send a broadcast Intent which your Activity will see (if it is running),or it could create a Notification which the user can then open whenever he wants to. You need to figure out how you want to determine when the app should stop checking the server. Maybe the user should tell you that he isn't interested anymore, or maybe the Service can recognize that the app isn't running anymore (or hasn't been looked at in more than X hours or whatever). There are lots of ways to do this and it depends on your requirements.

Prevent Service to restart

I have an IntentService which is started from a BroadcastReceiver with startService(service). When I get new informations in the BroadcastReceiver the new infos are pushed through an intent with startService(service) again to the IntentService but then the service is restarted. Can I prevent this? I want to push new informations to the IntentService without restating it.
Intent service are intended to be started with an intent, execute their job and finish. They are more like an asynctask from this point of view. This is the reason why your intentservice is restarting.
onHandleIntent should do some work and finish. You could do some tricks to make it blocking but that would go against the nature of intent services.
What you should do is to have a classic Service. If you are getting ANR errors, you should perform all the time intensive operations inside a thread or an asynctask hosted inside the service itself.
I assume you need to share some state for handling subsequent Intents inside your Service. I see two solutions:
Use IntentService and save and restore this state inside onHandleIntent.
Use started Service, and hold this state as a field inside your Service class. To prevent ANRs, process the Intents outside of UI thread, just like the IntentService does. To keep the Service running, just remove the stopSelf call from handleMessage.
If you can get away with restoring and saving state for each processed Intent, the first solution is safer, because your Service may be killed by the OS in case of running out of memory.

Share a thread between different activities

I'm about to develop an application for Android.
I need to continuously run an update thread that executes a task in a given interval. And this thread needs to be shared between activities.
I would also like to notify the active Activity when the thread executes a task.
In iOS I would execute a thread from the AppDelegate and then send notifications, but how do I achieve this with the Android SDK?
I'd say create a Service that does the work for you. When it starts or finishes (or at any point you want), you can send a custom broadcast intent to indicate all parties that your service has passed this point. See Intents.
If you want to start this Service periodically, also have a look at the AlarmManager. That allows you send a broadcast intent periodically - in this case the intent that starts the service.
Also note that it's usually wiser to to terminate a service via stopSelf() when it's work is done. Run it again via intent when needed. Keeping it alive all the time isn't a good idea when it comes to battery life.
Make a service and implement a thread with infinite loop and from there you can do your job....to update data in activity you can make a static method and you can call it from there with appropriate arguments....

Categories

Resources