When is onStartCommand of a service exactly executed?
In the following code my service is started from the onCreate in my activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, CenseDroidService.class);
//onStartCommand is not extecuted immediatly after startService
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
So when does the Android system decide to call onStartCommand? All that I know is that is runs on the main thread some time after calling startService. The onCreate is fully executed before onStartCommand is called.
From the documentation.
Called by the system every time a client explicitly starts the service by calling startService(Intent), providing the arguments it supplied and a unique integer token representing the start request
What this means is, every time you call startService(intent) with an Intent, then onStartCommand will be called. If you have a hundred Activities that all need to start and bind to this service, then they will all call startService() and onStartCommand will be called.
Now when it is exactly called is a bit trickier to answer. It is sometime in the future which is why your ServiceConnection works asynchronously. Most likely it can be within the next UI Thread cycle, but it is not something you should rely on.
If you need a reasonable knowledge of when the Service started, you could use the LocalBroadcastManager to broadcast the event to all registered listeners. The LocalBroadcastManager#sendBroadcastSync will actually block until all listeners respond so it may be useful for this scenario.
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 have tried to start a service and bind to the service in my Activity's onCreate() method. When I try to call a function from service like commSessionManagerService.startCommandUpperM() afterwards, a NullPointerException occurs. Here is the code that I use to start the service and bind to it:
Intent startIntent = new Intent(this, CommSessionManagerService.class);
startService(startIntent);
Intent bindIntent = new Intent(this, CommSessionManagerService.class);
bindService(bindIntent, conn, Context.BIND_AUTO_CREATE);
If I move the function startCommandUpperM() to onStartCommand() in the CommSessionManagerService, the onCreate method will take several seconds to complete. As a related note, I have a created and started a thread in the startCommandUpperM() function.
This is because your Service is actually bound on the UiThread. As onCreate also runs on UiThread, your call to bindService result in Handler.post(Runnable) be called on the main thread's handler.
So when bindService returns, the Service isn't already bound.
To circumvent this problem, you should put your code using your Service inside ServiceConnection.onServiceConnected().
I have a problem with intent not getting delivered to an IntentService under a specific activity flow: This is the scenario:
Consider 3 activities, Home, B and C. C has 2 fragments CF1 and CF2.
B, CF1 and CF2 use the same IntentService class but with different actions.
IntentService gets started using startService(Intent). (getActivity().startService(Intent) for Fragments)
Wherever IntentService gets started, I make sure it gets stopped using stopService(intent) if it is running in Activity/Fragment's onStop().
If the activity flow is Home -> C - > CF1 - >CF2, everything works.
If the activity flow is Home -> B - > C -> CF1 - > CF2, then onHandleIntent is never invoked after startService(Intent) from CF2. B and CF1 intents are handled. For debugging, I tried by waiting for IntentService to complete in Activity B and then go to CF1 -> CF2, still the same problem. CF1 never seems to have any problems in starting the same intent service. When I tried creating a new IntentService class for CF2, it worked.
My understanding was that IntentService has a queue for intents. If the service is running for first time , onStartCommand is invoked (which we are not supposed to handle for IntentService). If the service is already running, onHandleIntent is invoked for every subsequent call of startService.
Obviously, I am doing something wrong but not clear as what. I have tried looking into other stackoverflow questions but didn't help. The code I am using is pretty straightforward:
AndroidManifest.xml
<service android:name=".service.ExampleIntentService" />
Activity B
#Override
public void onCreate(Bundle savedInstanceState)
{
.......
intent = new Intent(getApplicationContext(), ExampleIntentService.class);
intent.setAction(StringConstants.ACTION_B);
serviceRunning = true; //set to false in onReceiveResult
startService(intent);
}
#Override
public void onStop()
{
if(serviceRunning && intent != null)
stopService(intent)
}
Fragment CF1
#Override
public void onResume()
{
super.onResume();
intent = new Intent(getActivity(), ExampleIntentService.class);
intent.setAction(StringConstants.ACTION_CF1);
serviceRunning = true; //set to false in onReceiveResult
startService(intent);
}
#Override
public void onStop()
{
if(serviceRunning && intent != null)
stopService(intent)
}
The code is exactly the same for fragment, CF2
My understanding was... If the service is running for first time , onStartCommand is invoked (which we are not supposed to handle for IntentService). If the service is already running, onHandleIntent is invoked for every subsequent call of startService.
No. onStartCommand() is called for every startService() call. onHandleIntent() is called for every startService() call made to an IntentService, unless you do something in onStartCommand() to change normal behavior.
IntentService gets started using startService(Intent).
You send commands to an IntentService using startService().
Wherever IntentService gets started, I make sure it gets stopped using stopService(intent)
That is an exceptionally bad idea. IntentService will stop itself when all startService() calls have been processed, as mentioned in the documentation:
IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.
I start service by using:
private ServiceConnection _serviceConnection = new ServiceConnection() {...}
bindService(new Intent(this, MainService.class), _serviceConnection, Context.BIND_AUTO_CREATE);
I want to 'restart' the service. (Let's not argue why I want to do that)
I do that by:
unbindService(_serviceConnection);
// Do some initialization on service
bindService(new Intent(this, MainService.class), _serviceConnection, Context.BIND_AUTO_CREATE);
I noticed service doesn't die(onDestroy doesn't run) until I call next bindService();
So some static initialization I did on service got cleared by onDestroy() implementation.
Question: How do you make sure unbindService() will stop service (run onDestory()),
so that I could do initialization after and re-run bindService()?
Android keeps services around at it's own discretion, even after calling unbindService or stopService.
If you need to immediately reinitialize some data on the service side, you most likely need to provide a service method for doing that.
Question: How do you make sure unbindService() will stop service (run onDestory()), so that I could do initialization after and re-run bindService()?
The timing of onDestroy() after the last unbindService() is indeterminate and certainly asynchronous. You could also try calling stopService() yourself, but that too is asynchronous, so you don't know when it will be stopped.
I do not know of a reliable way for you to "restart" a service using the binding pattern.
You can use onRebind() if you return true in onUnbind().
see here:
http://developer.android.com/guide/components/bound-services.html