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.
Related
Basically I have a service running to check my location every 30 minutes and when the location changes, pop up a basic notification. I only need this service when my app is closed (on stop/pause too). So im asking where should I start my service in my activity?
I want something similar as Facebook,Instagram and most of apps have... a service running from the background and when a notification pops up just open de app. While the app is open the service shouldnt do anything.
I tried onDestroy() method (in my MainActivity) but didnt work out, and onStop() method neither.
Do i explain myself?
Thanks!!
Recurring tasks should be scheduled using AlarmManager (or JobScheduler).
This is usually done in a BroadcastReceiver which reacts to BOOT_COMPLETED.
If you want to cancel such a job while your activity is active, call the respective methods on AlarmManager in onResume and onPause.
If you want your service to be running in the background at all times event after closing the app, you need to make your service STICKY and you can do that in the OnStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
Note that it returns START_SICKY which tells the OS to recreate the service after it has enough memory and call onStartCommand() again with a null intent.
Read also about application:persistent which is "Whether or not the application should remain running at all times". This is more troublesome - System will try not to kill your app which will effect others in the system, you should be careful using it.
I have a service that gets started (not bound) by an activity. If the activity gets destroyed (e.g. by pressing the back button), the service continues to run, this is of course intended.
However, if I swipe the activity out of the 'recent apps' list, the service gets restarted immediately. This is reproducible, every time the activity/app is swiped out of the list, there is a new call to the service's onCreate-method. No call to onDestroy in between!
First I thought the service gets killed by android, even though I saw no reason for the kill (neither the activity nor the service do resource consuming things, in fact they are minimalistic and do nothing). But then I noticed that the service actually crashes.
V/MainActivity(856): onDestroy // swipe out of the list
I/ActivityManager(287): Killing 856:com.example.myapp/u0a10050: remove task
W/ActivityManager(287): Scheduling restart of crashed service com.example.myapp/.TestService in 5000ms
The code is not noteworthy, but here it is
Activity:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v(TAG, "onCreate, starting service...");
startService(new Intent(this, TestService.class));
}
#Override
protected void onStart() {
super.onStart();
Log.v(TAG, "onStart");
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.v(TAG, "onDestroy");
}
//[...]
}
Service:
public class TestService extends Service {
private static final String TAG = "Service";
// onBind omitted
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v(TAG, "onDestroy");
}
}
In short:
My service is independent of the activity's lifecycle, but only as long as I don't swipe out the app of the recent apps list. In that case, the service gets restarted but without a call to onDestroy.
Every time this happens, not only the state of the service, but also the work the service is doing is lost. I just want to know why the swipe is the reason for this.
Swiping the app from the recent tasks list actually kills the operating system process that hosts the app. Since your service is running in the same process as your activities, this effectively kills the service. It does NOT call onDestroy() on the service. It just kills the process. Boom. Dead. Gone. Your service does not crash.
Since your service returned START_STICKY from the call to onStartCommand(), Android recognizes that your service should be restarted and schedules a restart of the killed service. However, when your service is restarted it will be in a newly created process (you can see onCreate() called in the service), so it will have to start the work all over again.
Rule #1: Don't ever swipe apps from the recent tasks list ;-)
Maybe it can be a problem with Broadcast receivers defined in the manifest.
Do you have some receiver / intent-filter defined in your manifest at application level ? I used to have same kind of problem and it was due to receiver declared in the manifest at the application level
By swiping, your process is NOT guaranteed to be killed by the system get killed. No. You only remove the applciation task (or back stack). Application task is NOT equal to the application process.
So if you have any background jobs (threads, services etc) tied to your back stack and you have a good cancellation policy. The system may try to cache your process if it's suitable for later.
If you kill the app process(es) from the Task Manager though, then it means that your process will be removed and so your JVM/sandbox aggressively by the system.
Use *START_NOT_STICKY* as onStartCommand return
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand");
return START_NOT_STICKY;
}
As the title says I am having the following problem. My foreground service is being killed when the activity that started it is swyped away from recent tasks list.
I am starting a service with
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(notificationID, notification);
return START_STICKY;
}
Can someone please tell me why is this happening and how can I make it so the service stays running when user swypes the activity away.
I don't have access to public void onTaskRemoved (Intent rootIntent) for some reason but I don't know what to do in that method anyway...
I am starting the service like this this and it's not a bound service
serviceIntent = new Intent(this, RecordingService.class);
startService(serviceIntent);
If little use case description helps I am trying to control sound recorder from a remote view in the notification bar so restarting a service is not an option since it should continue to record even if activity is destroyed.
BTW.I did tried starting a service in another process by android:process=":recordingProcess" and the service does continue to run then but I am suspecting this is not how you should do it.
Even i had the same issue and i had access to onTaskRemoved() function.Please check this link, "Process life cycle" topic.
Try to return from onStartCommand() START_REDELIVER_INTENT, service will get start again.
From Android Developer Reference
A service can be both started and have connections bound to it. In
such a case, the system will keep the service running as long as
either it is started or there are one or more connections to it with
the Context.BIND_AUTO_CREATE flag. Once neither of these situations
hold, the service's onDestroy() method is called and the service is
effectively terminated.
How are you starting your service?
in my service, I download a lot of stuff from the internet and would like to do this every x hours (choice in Preferences)
The current implementation I have written in this code in the main Activity:
Intent svc = new Intent(this, AlarmService.class);
if (startService(svc)!=null) {
Log.i(this.getClass().toString(), "Service already running");
} else {
Log.i(this.getClass().toString(), "Service Started");
}
Because, according to the docs
Request that a given application service be started. The Intent can
either contain the complete class name of a specific service
implementation to start, or an abstract definition through the action
and other fields of the kind of service to start. If this service is
not already running, it will be instantiated and started (creating a
process for it if needed); if it is running then it remains running.
That last sentence that I highlited let me hope that I could write all my heavy code in the onCreate of the service and it will be untouched, but unfortunately, everytime the user starts the app, the service starts again and make a huge download, draining the battery.
The indication if the service is running or not is correct:
Returns 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.
So, my question, How can I let the service untouched if already started, and start it if not yet started?
Thanks
EDIT:
The service in never stopped:
public class AlarmService extends Service {
Alarm alarm = new Alarm();
public void onCreate() {
super.onCreate();
Log.i("**", "onCreate");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// DO NOTHING FOR TESTING alarm.SetAlarm(this);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
in my service, I download a lot of stuff from the internet and would like to do this every x hours (choice in Preferences)
Hopefully you are doing this with an IntentService (possibly my WakefulIntentService) and AlarmManager.
That last sentence that I highlited let me hope that I could write all my heavy code in the onCreate of the service and it will be untouched, but unfortunately, everytime the user starts the app, the service starts again and make a huge download, draining the battery.
That is because either the service had been stopped (via stopService() or stopSelf()), or because your app's process was terminated.
How can I let the service untouched if already started, and start it if not yet started?
Call startService(), as the documentation indicates.
But if you are really trying "to do this every x hours", the user will really appreciate it if your service does not waste RAM trying to stick around during that time. Hence, the user or the OS may well get rid of your process during the "x hours". If you do not want to do the "huge download" every "x hours", then you need to save that information locally to some sort of file.
My application synchronizes data with a remote database via web service calls. I make these calls in an IntentService so they can run in the background (I call it SyncService).
The code to launch my IntentService looks like so:
Intent intent = new Intent();
intent.setClass(appContext, SyncService.class);
// place additional values in intent
intent.putExtra("data_type", SyncService.ITEM_TRACKING);
intent.putExtra("user_id", intUserId);
// call SyncService
appContext.startService(intent);
This, normally, looks great. However, one of my friends, who is also a user of my app, often tells me his data doesn't sync and get displayed on our website. His device happened to be displaying the symptoms while I was around. I plugged his device into my computer and here is what I found:
The code to launch SyncService (ie: the code above) was hit.
I had a breakpoint inside the onHandleIntent method of my IntentService and it never gets hit.
I checked his device's list of running services and SyncService was there and running. Interestingly, it had been running for about 20 minutes. I was under the impression that IntentService killed itself when it was all out of Intents to process.
I force stopped the SyncService (not the app) and, all of the sudden, onHandleIntent started getting hit over and over. It was like all the Intents were queued up somewhere on the device and were just now getting thrown at the SyncService.
Does anyone have any ideas as to what may be the problem? Do you think it's an issue with my app? With Android?
Again, I am handing a message to Android saying, "Start this IntentService or send the message to the already running IntentService." At that point, I have no control. The message never gets to the IntentService. Once I force quit the app, the messages get sent to the IntentService and it does its job.
UPDATE: I think this code is fine, but I'll put it up since a lot of you may want to see it.
Every Intent that comes in to the IntentService has an Extra denoting what "type" of call is to me made (ie: do I call this web service or that web service, etc). When an Intent comes in to the IntentService, I check the "type" and, if there is already an Intent in the queue for that type, I add an Extra to it called "skip" so, when it is reached, I don't execute the search (basically the IntentService can build up lots of Intents and it makes no sense to call this web service when this webservice was called 20 seconds ago). It basically protects the app from spamming the website.
It is important to note that none of this code is hit anyway (once the problem starts occurring). onStartCommand does not get called until the app is killed
#Override
public int onStartCommand (Intent intent, int flags, int startId) {
// here be dragons
// overriding this method and adding your own code is dangerous. i've wrapped
// my code in a try/catch because it is essential that the super method be called
// every time this method is entered. any errors in my code should not prevent this
// or the app will explode.
try {
if (flags == 0 && intent != null && intent.hasExtra("data_type")) {
Integer intDataType = intent.getExtras().getInt("data_type");
if (!mCurrentTypes.containsKey(intDataType)
|| !mCurrentTypes.get(intDataType)) {
mCurrentTypes.put(intDataType, true); // put this type in the list and move on
}
else {
intent.putExtra("skip", true); // mark this Intent to be skipped
}
}
}
catch (Exception e) {
// Log.e("Error onStartCommand", "error: " + e);
}
return super.onStartCommand(intent, flags, startId);
}
private void processIntent(Intent intent) {
// do stuff if no "skip" Extra
mCurrentTypes.put(intDataType, false);
}
There is definitly something that keeps your service running on your friend's device. If so all subsequent call to this intent service are queued until the current one finishes. If it doesn't finish then you will get what you have : next services won't start.
You should double check that :
you give proper timeouts to nework operations
you give proper timeouts to nework connections operations
there is no race condition between threads.
you log any exception that can occur inside the service, you don't wanna loose that kind of information.
Afterwards, if you think everything is green : just log what the service does and use some bug reporting mechanism to get it automatically sent from your friends device. A simple solution could be to use bugsense or equivalent.
Next, put in place some kind of watchdog : a thread that will go on running until your service stops (you just tell your thread to stop when service is stopped). The thread will have to stop your service after some time limit has been passed.
This watchdog thread could be put inside the service itself, or outside, although this may be more complex to put in place.
This answer suggests a solution that worked for me in similar situations. It doesn't fix your current code but suggests another, perhaps simpler (and easier to debug) option:
Add a BroadcastReceiver to your calling Activity that listens for SUCCESS Intents from the IntentService.
In your calling Activity, include the logic for when to start the IntentService (and don't include it in the IntentService). The logic is:
Call startService() and set a flag in the calling Activity to CANNOT_CALL.
If the Activity's BroadcastReceiver has not received a SUCCESS broadcast from the IntentService, then startService() can not be called again.
When the Activity does receive a SUCCESS intent, set the flag to CAN_CALL, and startService() can be called when the timer hits again.
In your IntentService, write your onStartCommand() like so:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
In you IntentService, when you've received, parsed and stores the web service response, call sendBroadcast() with an Intent with custom action SUCCESS.
This logic is just an outline and has to be fine-tuned for error messages from the web service that have to be broadcast from IntentService to the listening Activity.
Hope this helps.
It seems to me that setting a set of flags to your Intent may solve the problem.
Intent intent = new Intent();
intent.setClass(appContext, SyncService.class);
// This way
intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK|Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
You can make your Service start as fresh using the above flag in a fresh task.
One more comment. It's not an answer for your question. However, it may affect overall behavior of a service.
You do following:
return super.onStartCommand(intent, flags, startId);
Internally Service.onStartCommand() looks like following
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
mStartCompatibility is false if your app target SDK API 7 or later (which is most likely a case).
So, as result your service will be started as START_STICKY.
Here is piece from documentation:
For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them. See the linked documentation for more detail on the semantics.
Base on what you have described, I recommend to replace "return super.onStartCommand(intent, flags, startId);" to "return START_NOT_STICKY;"