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.
Related
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.
I want to perform some operation when my application gets killed.
Which method can be used for this? I am working on Android 5.0.
The key of this question is that:
you must understand your application whether can receive any
additional callbacks when your application being killed in any kinds of situation.
The following answer is answered by Devunwired in this question:
Android app doens't call "onDestroy()" when killed (ICS)
This will help you more to understand this.
Your application will not receive any additional callbacks if the process it terminated by external means (i.e. killed for memory reasons or the user Force Stops the application). You will have to make do with the callbacks you received when you app went into the background for your application cleanup.
finish() is only called by the system when the user presses the BACK button from your Activity, although it is often called directly by applications to leave an Activity and return to the previous one. This is not technically a lifecycle callback.
onDestroy() only gets called on an Activity as a result of a call to finish(), so mainly only when the user hits the BACK button. When the user hits the HOME button, the foreground Activity only goes through onPause() and onStop().
This means that Android doesn't provide much feedback to an Activity to differentiate a user going Home versus moving to another Activity (from your app or any other); the Activity itself simply knows it's no longer in the foreground. An Android application is more a loose collection of Activities than it is a tightly integrated singular concept (like you may be used to on other platforms) so there are no real system callbacks to know when your application as a whole has been brought forward or moved backward.
Ultimately, I would urge you to reconsider your application architecture if it relies on the knowledge of whether ANY Activity in your application is in the foreground, but depending on your needs, there may be other ways more friendly to the framework to accomplish this. One option is to implement a bound Service inside of your application that every Activity binds to while active (i.e. between onStart() and onStop()). What this provides you is the ability to leverage the fact that a bound Service only lives as long as clients are bound to it, so you can monitor the onCreate() and onDestroy() methods of the Service to know when the current foreground task is not part of your application.
You might also find this article written by Dianne Hackborn to be interesting covering in more detail the Android architecture and how Google thinks it ought to be used.
You have to use Service Class for it like -
public class Myservice extends Service { #Nullable #Override public IBinder onBind(Intent intent) { return null; }
#Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(Constants.TAG, "Service Started"); return START_NOT_STICKY; }
#Override public void onDestroy() { super.onDestroy(); Log.d(Constants.TAG, "Service Destroyed"); }
#Override public void onTaskRemoved(Intent rootIntent) { Log.e(Constants.TAG, "END"); //Perfome here want you want to do when app gets kill stopSelf(); } }
In Manifest -
<service android:name="Myservice"
android:stopWithTask="false" />
In Oncreate of your launcher activity or Application Class to start service -
startService(new Intent(getBaseContext(), OnClearFromRecentService.class));
You can use your activity's onDestroy() method.
My application has an activity and a service running in the same process. When the user terminates the activity by clicking the STOP button, the activity causes the service to terminate.
From the Android documentation I understand that the system can kill a process to reclaim resources:
"Once your activity is stopped, the system might destroy the instance if it needs to recover system memory. In extreme cases, the system might simply kill your app process without calling the activity's final onDestroy() callback, ..."
Question 1: Is it possible for my activity to be killed but not the service? I would prefer my service remain alive.
If the answer is 'yes' then
Question 2: Is there a mechanism where the service can be notified that the activity has been killed?
IBinder.linkToDeath has the functionality I need but seems to apply to a process, not an activity.
I've thought of an indirect method like having the activity take ownership of semaphore and having the service use a thread to wait on it. Then when the activity gets killed it will release the semaphore and the service will get it, providing the notification. But I was wondering if there wasn't an android-centric technique I could use.
[begin edit]
After reading the very helpful comments you've provided I'd like to clarify the scenario I'm presenting.
I've started two components: an activity and a service. The application is configured so that service can continue to run after the activity has stopped and been destroyed. The user can restart/start the activity multiple times and it will use the same instance of the service.
Normally the activity will notify the service it has been destroyed during onDestroy(). But onDestroy() may not be called.
I'd like to know whether there is some android-specific mechanism I can use to notify the service that the activity has been killed without the call to its onDestroy() method having been made.
[end edit]
Thanks for your help.
Will
As I said before, single activity won't be killed by android without calling onDestroy(). If Android needs more memory it kills whole process(with all activities and services). Here is description, documentation is wrong about this.
Service can be also created in other process but it has to be set int the manifest. And then you can use iBinder to get notification when process(with all activities) is killed by Android
Yes services and activities can run independently of each other.
To achieve what you're trying to do I would explicitly start your service using startService() in your activity's onStart() function (or wherever you wish to launch it) and also bind to it at the same point. Binding without an explicit startService will cause the service to stop when you kill the activity (unless some other activity is still bound to it).
In your activity's onStop() call a function on your service to tell it the activity has been killed.
In your Activity:
private ServiceRecordJourney yourService;
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
YourServiceBinder binder = (YourServiceBinder) service;
yourService= binder.getService();
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
#Override
public void onStart(){
super.onStart();
startService(new Intent(this, YourService.class));
// Bind to Service
Intent intent= new Intent(this, YourService.class);
bindService(intent, serviceConnection , Context.BIND_AUTO_CREATE);
}
#Override
public void onStop(){
super.onStop();
if(yourService != null){
yourService.activityKilledFunction();
unbindService(serviceConnection);
}
}
Your service will need to be foreground (and display a notification) to further stop it from being killed off by the OS. Also as it's been explicitly started you will need to handle an explicit stop on the service (either call stopSelf() on the service or stopService() in a context object.
You can handle communication to the service using multiple startService() calls with different intents but I prefer the above approach (and I'm sure that in the case binding is the preferable approach).
Also whilst the service is running the activity will bind and unbind to the service each time the activity is started or stopped by the user.
I have a Base Activity class to implement common behaviour for all the Activities. All of them extend this BaseActivity.
I'm binding to a service in in the BaseActivity's onStart method and unbinding conditionally in the onStop method. With conditionally, I mean that depending on some option selected by the user, the service should or should not keep running in the background when the app goes to background.
The problem is that sometimes the service keeps running when it shouldn't (this is, when the option for killing it is enabled, and unbindService() is effectively called).
I'm thinking that on every Activity change the service is unbound and bound again. Since bound services are reference counted, maybe my service is bound more times than unbound, so that's why it keeps running at the end, even when I call unbindService().
Additionally, the documentation says something about that:
You should usually pair the binding and unbinding during matching bring-up and tear-down moments of the client's lifecycle. For example:
If you only need to interact with the service while your activity is visible, you should bind during onStart() and unbind during onStop().
If you want your activity to receive responses even while it is stopped in the background, then you can bind during onCreate() and unbind during onDestroy(). Beware that this implies that your activity needs to use the service the entire time it's running (even in the background), so if the service is in another process, then you increase the weight of the process and it becomes more likely that the system will kill it.
Since I'm kind of trying to implement both options, what should be the best approach to implement this?
Finally I changed my approach and decided to use only startService() and communicate with the service using a Local Broadcast Receiver.
I start the service in the onCreate() and stop it in the onDestroy() methods of the Base Activity.
Then, to send a message from the Service to the activity, I use this:
private void sendBroadcastMessage(String msg) {
Log.d(LOG_TAG, "send broadcast message: " + msg);
Intent intent = new Intent(MyService.class.getSimpleName());
// Add data
intent.putExtra("message", msg);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
Then, to be notified in the activity:
// handler for the events launched by the service
private BroadcastReceiver mMyServiceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Extract data included in the Intent
String message = intent.getStringExtra("message");
Log.d(LOG_TAG, "Got message: " + message);
// Do stuff...
}
};
And to register the Receiver in the activity:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mMonitorReceiver,
new IntentFilter(MyService.class.getSimpleName()));
}
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?