I'm reading the official documentation about Services, but I'm a little bit confused because some things aren't so explicit.
Let's take this snippet:
public class MyService extends Service {
public int onStartCommand(Intent intent, int flags, int startID) {
while(...any condition...) {
//some actions
}
return START_SOMETHING;
}
public IBinder onBind(Intent intent) {
return null;
}
}
My doubts are the followings:
The Android system can destroy a service not bound to the current focused activity and not declared as in foreground. But can the Android system destroy a service even if it is still in the while loop?
When I call stopService() does it wait for the onStartCommand() to get to the return statement?
Where should I insert the stopSelf() call? Just before the return statement?
Can the Android system destroy a service even if it is still in the while loop?
Yes. For some reason, extreme low memory pressure (for example), Android system kills the Service without calling onDestroy() method. Why? Because of Service always runs on UI thread.
When I call stopService() does it wait for the onStartCommand() to get to the return statement?
No. Return statement's action will be executed when Android system kills the Service. But, if you call stopSelf() manually, the return statement's action will be ignored.
Where should I insert the stopSelf() call? Just before the return statement?
Call stopSelf() wherever and whenever you want. For example, if user press a button, call stopSelf() to stop the Service playing a music.
Related
I am trying to understand the Service Life Cycle while working through some Android Open Source Code.
I was looking at a Service implementation which I distilled down to something like the following...
public class MyService extends Service {
public MyService() { super(); }
#Override
public void onCreate() {
super.onCreate();
init();
//==this seems odd to me
//comment in AOSP says startService() is called to make
//sure Service stays around long enough for the async call
//to complete.
startService(new Intent(this, myservice.class()));
doSomeMoreInitAsync();
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if(actionableIntent(intent,flags,startId)) {
//do something
//NOTE: the Intent passed to startService() in onCreate()
//above will go around this block of code, doing nothing
//except returning START_STICKY
}
return START_STICKY;
}
public void onDestroy() {
//destroy stuff
}
#Override
public IBinder onBind(final Intent intent) {
return mBinder; //an instance of android.os.Binder derivative
// created when this service was instantiated
}
//other stuff
}
Why would someone want to have onCreate() call startService() on itself like above, doing nothing? The comment in code sheds some light, but it's like assumptions are being made about the Life Cycle that I don't understand. I.e., is it reasonable to expect onCreate() to effectively start its own service?
I know that if a service has already been started then onCreate() will only be called once (unless destroyed and restarted, then a new instance of the service is created and onCreate() is called once on it). My first concern with this example would be that there is an expectation placed upon the underlying Service API implementation that the Service is already in the initialized state before onCreate() is called (else there be an infinite recursion, but there is not).
But isn't onCreate() supposed to be part of the initialization (albeit an optional part for the subclass)?
Is this coding logic a reasonable way of making sure the Service is forced to be an Unbounded Service? Or am I looking at a bad example in the AOSP which may have undefined behavior in the future?
You are correct in that a Service will call onCreate and onStartCommand if it is started via Context.startService. So in this sense, when you return START_STICKY, the Service will continually run until an explicit call to stopService() is called. It will also be destroyed and restarted during this lifecycle.
Another way to create a Service, is by binding to it. As per the docs:
Clients can also use Context.bindService() to obtain a persistent connection to a service. This likewise creates the service if it is not already running (calling onCreate() while doing so), but does not call onStartCommand().
So, it's possible for a Service to be created by simply binding to it. However, the lifecycle of a Service indicates that it will remain if it is started or a client is still bound to it. Meaning, that if it was created by a bind command, it will immediately be destroyed as soon as the client unbinds.
So, if a Service starts itself in the onCreate(), it will ensure that it puts itself in the started state regardless of whether it was created by binding or by an explicit call to startService. Since there's no actionable intent, the onStartCommand will just pass straight through. An clients that call startSevice will, presumably, have actionable Intents in which case the Service will perform its duties.
I am calling startService() multiple times in my class.
there is a function in my service's onStartCommand(), like this -
Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(StaticValues.TAG, "service started.");
processItem();
return 0;
}
My question is, if I start service again, onStartComamnd() will be called again. So will this call wait till my previous call is over or it will execute both calls to processItem() parallelly?
Edit : Answer I found from links in comments
startService() is asynchronous. So while you are looping the calls, the service itself hasn't gotten any resources and didn't start yet.
The Service will only run in one instance. However, everytime you start the service, the onStartCommand() method is called.
Check : What happens if a Service is started multiple times?
A Service can only be started once, if you want to and love to complicate things use a boolean flag
Using startService() overrides the default service lifetime that is managed by bindService(Intent, ServiceConnection, int): it requires the service to remain running until stopService(Intent) is called, regardless of whether any clients are connected to it. Note that calls to startService() are not nesting: no matter how many times you call startService(), a single call to stopService(Intent) will stop it.
If the service is being started or is already running, the
ComponentName of the actual service that was started is returned; else
if the service does not exist null is returned.
Note that multiple calls to Context.startService() do not nest (though
they do result in multiple corresponding calls to onStartCommand()),
so no matter how many times it is started a service will be stopped
once Context.stopService() or stopSelf() is called;
Link to the Docs
Life Cycle of a Service
I've created a custom Background Service, who is extending IntentService. I'm calling this class when my app goes to background via .startService(). Firsly onStartCommand is called where I'm returning START_STICKY (like I've read some other posts to keep my service alive and re-create if needed when is killed due to low memory). After that I'm implementing onHandleIntent(intent) method where I'm getting the extras from the caller intent and here I'm starting a Timer to run every minute and execute simple code. But the problem here is that, when the app is in background, after 30 mins my service is killed and not re-created. Here is part of my code, so any suggestions would be perfect.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
protected void onHandleIntent(Intent workIntent) {
//here I'm initializing the Timer
}
class UpdateTimeTask2 extends TimerTask {
public void run() {
backgroundTimer();//here I'm doing some simple coding
}
}
So, as Kumar said, i should extend Service not IntentService. Calling of the service was the same as calling of the IntentService, the only difference was the unimplemented methods that were required by the Service class. Also I'm using Handler's postDelayed method instead of Timer and also in onStartComand I'm returning START_STICKY. So the service is not killed at all. Useful code can be found here and here. I hope this helps.
In this case a normal Service and a Handler should be all you need. As Pankaj wrote IntentServices are not designer for longrunning operations. You can then use the Handler's postDelayed method to excecute your code every minute.
I made a remote service, this service is started by my activity the first time that boot, after that, the activity always look if the service is started to avoid start it again.
The service run some methods in the onCreate function. This service is running always and started on boot time also.
The problem (is not a big problem but I want to know why) is that once the service is created if I stop my activity the onTaskRemoved is called, this is correct, but after few seconds the oncreate method is called again and the service starts again.
Any idea why? And how can I control this?
<service
android:name=".Service"
android:icon="#drawable/ic_launcher"
android:label="#string/service_name"
android:process=":update_process" >
</service>
AndroidManifest.xml
if (!isRunning()) {
Intent service = new Intent(this, UpdateService.class);
startService(service);
} else {
//Just to debug, comment it later
Toast.makeText(this, "Service was running", Toast.LENGTH_SHORT).show();
}
When the service is started if it was not running
The problems is that you service is sticky per default, this means that it will be restarted when killed, until you explicitly ask for it to be stopped.
Override the onStartCommand() method in your service, and have it return START_NOT_STICKY. Then you service will not be restarted when killed.
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return START_NOT_STICKY;
}
Although Bjarke's solution is valid, I would like to propose an alternate solution which covers cases where it might be necessary to perform any recovery in the Service.
Android is invoking onStartCommand() once again after restarting your service to inform you that the Service process crashed unexpectedly (because its task stack was removed), and is now being restarted.
If you look at the intent argument of onCreate(), it will be null (only for such restarts), which indicates that Android is re-creating your previously sticky service which crashed unexpectedly.
In some cases it would be wise to return NON_STICKY ONLY for such restarts, perform any needed cleanup/recovery and stop the service so that you exit gracefully.
When the service is started normally, you should still be returning STICKY otherwise your service would never be restarted to let you perform any recovery.
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// intent is null only when the Service crashed previously
if (intent == null) {
cleanupAndStopServiceRightAway();
return START_NOT_STICKY;
}
return START_STICKY;
}
private void cleanupAndStopServiceRightAway() {
// Add your code here to cleanup the service
// Add your code to perform any recovery required
// for recovering from your previous crash
// Request to stop the service right away at the end
stopSelf();
}
Another option would be to request your service be stopped (using stopSelf()) as part of onTaskRemoved() so that Android does not even have to kill the service in the first place.
I created a service that can be started via the startService() method, but it also be bound by the applications. I wish that it can be started only via the startService() method, or in other words, applications should be able to bind to it only when the service is already started.
The service should be started manually and not when an application binds to it.
If the service is running, applications can bind to it.
If the service is not running, when an application tries to bind to it, the service should not start.
However, the default behavior is quite different: in fact, Android starts the service automatically when an application wants to bind to it. I would like to know if is possible to modify this behavior to achieve the above requirement.
If this is not possible, the only alternative would be to stop the service if it has been initiated as a result of a bindService(). Here are the changes to my service class in order to use this way...
// It says if the service was started manually.
private boolean mCorrectlyStarted = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(LOG_TAG, "onStartCommand()");
mCorrectlyStarted = true; // the service is started manually: ok!
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.i(LOG_TAG, String.format("onBind(intent: %s)", intent));
if (mCorrectlyStarted) {
return binder;
}
else {
stopSelf(); // although I make this call, the service is not stopped
return null;
}
}
Why do my changes do not have the desired effect?
It might be good that the service starts when an application invokes the bindService() method, but after verifying that it was not started manually, it should stop itself.
The fact that something, like an Activity, called bindService() means someone is still bound to the service. Calling stopSelf() does not destroy the service since technically the service is still bound by the activity.
It will be destroyed once unbindService is called eventually by the Activity. Binding and starting a service are orthogonal.
I am not sure what you are trying to achieve with this design, maybe provide more info on why you want the service to be started before something can bind to it?