I am trying to keep alive a service that reacts to screen on/off changes. The service would work perfectly for awhile, but then eventually it would be killed. I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying. I understand that there is no way to keep a process alive forever, without error, but I feel like I must be doing something wrong, as adding startForeground() added no extra life to the process. Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user).. however, due to the nature of what I am trying to accomplish, the receiver needs to run until explicitly told to stop.
Any suggestions?
Relevant Code:
public class UpdateService extends IntentService {
public UpdateService() {
super(null);
}
#Override
protected void onHandleIntent(Intent intent) {
final int myID = 1234;
Intent notificationintent = new Intent(this, Main.class);
notificationintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationintent, 0);
Notification notice = new Notification(R.drawable.icon_image, "***********", System.currentTimeMillis());
notice.setLatestEventInfo(this, "*************", "***********", pendIntent);
notice.flags |= Notification.FLAG_NO_CLEAR;
startForeground(myID, notice);
boolean screenOn = intent.getBooleanExtra("screen_state", false);
// Blah Blah Blah......
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
(Updated) I suppose there are the following possible cases:
1) documentation for IntentService states:
the service is started as needed, handles each Intent in turn using a
worker thread, and stops itself when it runs out of work.
So, it might be that your service is normally stopped after onHandleIntent() is finished (especially, as you mentioned that startForeground() added no extra life to the process).
2) You might try to check if it's somehow can be related to device going to sleep (or maybe you are starting your service by schedule and awkening device - in this case you might need to acquire WakeLock)
3) In the very rare cases, the system still can kill foreground process - so if you do a lot of allocations (really lot) and some other work in onHandleIntent() (instead of "Blah Blah Blah" at your code) - you might run into it - but I suppose it's not the case.
As question's title is "Using startForeground() with an IntentService" - would like to clarify that too:
I believe nothing (architecture, best practices, android framework, java docs for IntentService) prevents you from running your intent service as a foreground. Of course you need to thought out carefully its usage and whether you actually need a foreground service. Some ideas are available here. For sample code see below. (Sample code can end up showing multiple notifications if you queued multiple jobs/intents into IntentService, so there might be better solution depending on your need.)
public class ForegroundService extends IntentService {
private static final String TAG = "FrgrndSrv";
public ForegroundService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
Notification.Builder builder = new Notification.Builder(getBaseContext())
.setSmallIcon(R.drawable.ic_foreground_service)
.setTicker("Your Ticker") // use something from something from R.string
.setContentTitle("Your content title") // use something from something from
.setContentText("Your content text") // use something from something from
.setProgress(0, 0, true); // display indeterminate progress
startForeground(1, builder.build());
try {
doIntesiveWork();
} finally {
stopForeground(true);
}
}
protected void doIntesiveWork() {
// Below should be your logic that takes lots of time
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
IntentService automatically shuts down when onHandleIntent() completes. It is to perform a brief bit of work when something occurs. It is not supposed to live for more than a few seconds, typically.
I am going to assume that this is tied to what I wrote in your last question in this area.
Something in the rest of your app will be registering and unregistering the BroadcastReceiver for the screen on/off events -- apparently, from your comments, it is an activity. If what you want to do when those things occur is very very quick (on the order of a few milliseconds), just do the work in onReceive(), and be done with it.
If, on the other hand, you have more work than a few milliseconds' worth, you will need to have that work be done by something else that can do the work on a background thread. For example, if the "something in the rest of your app" that registered the BroadcastReceiver is indeed an activity, the activity might just spawn an AsyncTask to do the work.
Another possibility is to use an IntentService. You elected to go down this path in your work prior to that last question. I do not know why. Regardless, an IntentService, like an AsyncTask, is supposed to be a short-lived component -- you send it a command via startService(), it does its work in onHandleIntent(), and it goes away.
With all that in mind, let's talk about your specific points.
The service would work perfectly for awhile, but then eventually it would be killed.
It is unclear what you think "killed" means. An IntentService automatically goes away once onHandleIntent() returns, and that ideally should occur within a handful of seconds.
I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying.
Again, it is unclear what you think "dying" means. Bear in mind that the mere existence of an IntentService does not stop the CPU from shutting down once the screen turns off, and startForeground() has nothing to do with that.
Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user)..
You also need to unregister the receiver before the user exits the activity. It is usually a good idea to call registerReceiver() in onResume() and unregisterReceiver() in onPause().
If memory is low and your consuming too much memory and your sitting in the background for too long then you WILL be killed by the ActivityManager.
Related
I have an Android Service (implementation of Servcie interface) which is running on a separate process compared to my real app. Unfortunately when I leave my real App (in which I clicked the Button to start my Service) and swipe it out from Task Manager, my Service gets killed as well.
I know there are a lot of questions like this here, but somehow none are targeting the Problem in my concrete constellation or they are vaguely answered.
So in my manifest I have something like:
<application ...>
<activity .../>
<service Android:name="MyService"
Android:label="MyLabel"
Android:export="false"
Android:process=":MyRemoteProcessName" />
</application>
I first have played around with an IntentService, but also switched to an own implementation of the Service Interface (eliminating the IntentService to be the point of failure) which looks something like:
public class MyService extends Service {
private ScheduledExecutorService scheduledWorker = Executors.newSingleThreadScheduledExecutor();
#Override
public void onStart() {
// Init components
startForeground(this, MyNotification);
}
#Override
public int onStartCommand(Intent i, int startId) {
// Execute Work on Threadpool here
scheduledWorker.execute(new ScheduledStopRequest(this, startId), 5, TimeUnit.Minutes);
return START_REDILIVER_INTENT;
}
// Overwritten onDestroy-Method
#Override
public void onLowMemory() {
Log.e(LOG_TAG, "On Low Memory called!");
}
#Override
public IBind onBind() {
// Dont't want to let anyone bind here
return null;
}
// Simply tries to stop the service after e.g. 5 Minutes after a call
private static class MyRunnable implements Runnable {
// Constructor with params used in run method..
#Override
public void run() {
mReferenceToMyService.stopSelfResult(startId);
}
}
}
I'm starting my Service in an onClick-Listener on a special button, with an explicit Intent, which kinda looks like the following:
#Override
public void onClick(View v) {
Intent i = new Intent(this, MyService.class);
startService(i);
}
My Intention is to keep the Service running when the user leaves the app, so that the Service can finish downloading and storing some important data. When the user comes back to my app again, he can view the data (That's why I'm executing it in a separate process). So is this possible?
My assumption right now is, that Android somehow notices my Service is just being used by my App (due to missing IntentFilters in Manifest or explicit call rather than by filter?!) and thus kills it immediately when my App is closed (even when running as ForegroundService as you can see above).
Does it seem possible to you and might some changes in the call of the service fix this problem or am I getting the concept of a service wrong?
(One last note: onLowMemory-Method doesn't get called -> No log entries.)
So, according to your hints (and so new keywords for me to look for) and after some additional research by myself, I think I have solved my problem. During my research I have found an very interisting blog post on this topic, maybe also for you, which is why I would like to share it with you: http://workshop.alea.net/post/2016/06/android-service-kill/ .
After verifying and going through the steps in this article everything seems to work fine (so startForeground seems to solve the problem). I want to point out here, that I have only tested it, with my service instance still running in separate process, so manifest entries as is above.
The actual thing which really confused me at the beginning was my android studio debug session being killed everytime, just after swiping out my app from recent apps (menu). This made me think my service being killed by the system as well. But according to the article (I have added some logs to the callback methods provided) when
Opening my app
starting service
swiping out app
starting app again and finally
calling service again,
I only received callbacks to the methods as if my service would still be running. Having an explicit look at DDMS (tool) also prooved my 2nd process, and thus my service, being still alive. Having verified this, I then cleared all my app data and repeated the steps above (excluding step no. 5). Having had a look in the database afterwards, prooved the data having been downloaded by the service.
For the curious of you:
The process of swiping out my app from recent apps (and thus having the onTaskRemoved callback method being called) lead to another problem. It somehow increases the startId parameter of onStartCommand by 1 so that my DelayedStopRequest malfunctiones and doesn't stop my service anymore.
This means: Repeating above steps 1 - 3 makes me receive startId = 1 in onStartCommand. By calling stopSelfResult(1) later on (which was the latest startId) it returnes false and the service keeps running. Continuing to follow step 4 + 5 then, makes onStartCommand being called with startId = 3 (but should actually be 2! which is skipped somehow). Calling stopSelfResult(3) with parameter 3 later on is then going to stop the service again (also visible in screenshots).
I hope my answer is correct so far (, understandable) and also helpful for you. Thank you for all of your answers which provided beneficial input and also pointed me to the solution. The android version I have been working with is:
4.1.2 - Jelly Bean | API Level : 16
I also added screenshots of the log entries from DDMS (imgur is rejecting my uploads so you'll temporarily have a link to my dropbox):
screenshots from logs from DDMS
Unfortunately running service in a separate process does not help you. I think there is no way you can prevent your service from being closed if user removes its task. You can however restart your service overriding onTaskRemoved. See this answer.
If you want to run this service class indefinitely after closing the app also.. you should use Alaram Manager class ..
public void scheduleAlarm() {
// Construct an intent that will execute the AlarmReceiver
Intent intent = new Intent(this, LocationListnerServiec.class);
// Create a PendingIntent to be triggered when the alarm goes off
final PendingIntent pIntent = PendingIntent.getBroadcast(this, MyAlarmReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Setup periodic alarm every 5 seconds
long firstMillis = System.currentTimeMillis(); // alarm is set right away
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
// First parameter is the type: ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC_WAKEUP
// Interval can be INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_DAY
alarm.setRepeating(AlarmManager.RTC_WAKEUP, firstMillis,
60000, pIntent);
}
Use this method for keep checking the service class is on or off.. By using this method Your service class will keep working after destroying of you application.
I m actually developping an Android application with data storage, and I proceed like this :
Activity -> Business services -> Repo (with Spring REST fw). Using this, I m forced to let the activity finish his storing job before closing it (thread treatment, progress dialog ...).
Is this a bad way of coding to use an android service to store data ?
With this, users can continue to navigate, and have the impression to work with a very fluid application. Is this a good solution ?
Thanks
There is no need to keep your activity in the foreground waiting for a background logic to complete.
What you should do instead is to execute this background logic in a way which is 'detached' from your activity.
There are two ways of solving this problem: risky and safe.
The Risky Way
class MyActivity extends Activity {
void calledWhenActivityNeedsToBeClosed() {
// start a thread to do background work
new Thread() {
public void run() {
perform long running logic here
}
}.start();
// and clos the activity without waiting for the thread to complete
this.finish();
}
}
You can use an AsyncTask or any of the java.concurrent constructs instead of the thread. They will all do the work.
I have used this way for years. It mostly works fine. But.. it is inherently flawed.
Why? Because once activity is finish()-ed, Android can at any time reclaim it together with all its resources and including halting of all worker threads.
If your long running work does not exceed several seconds, and I assume your repo update is such, the risk here
is minimal. But why take it?
The Safe Way
Declare a Service and before activity going down activate it to perform the long running action:
class MyActivity extends Activity {
void calledWhenActivityNeedsToBeClosed() {
// delegate long running work to service
startService(this, new Intent(this, MyWorkerService.class));
// and close the activity without waiting for the thread to complete
this.finish();
}
}
This is much safer. Android can, and often does, also kill running services, but is does so much more reluctantly than killing background activities.
Note that if you can see a scenario in which your UI is visible while the worker service is still running,
you would probably want to use an IntentService instead.
Finally - if you want to be absolutely assured that background logic will not be cleared by Android, you
should use a foreground service. Below is how to do it, but please be warned - in cases like you have described, a foreground service is probably over engineering:
static final int NOTIF_ID = 100;
// Create the FG service intent
Intent intent = new Intent(getApplicationContext(), MyActivity.class); // set notification activity
showTaskIntent.setAction(Intent.ACTION_MAIN);
showTaskIntent.addCategory(Intent.CATEGORY_LAUNCHER);
showTaskIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pIntent = PendingIntent.getActivity(
getApplicationContext(),
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notif = new Notification.Builder(getApplicationContext())
.setContentTitle(getString(R.string.app_name))
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pIntent)
.build();
startForeground(NOTIF_ID, notif);
Having read most of the available documentation on Android services on the developer site and here in stackoverflow, I'm still confused by several aspects of running a service in a separate task. Hopefully someone can put me on the right track.
Let's say we have trival service framework such as
public class HliService extends Service {
#Override
public void onCreate() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
}
}
and in the manifest, I have
<service android:name=".HliService" android:process=":HLI_Comms"/>
so that the service runs in its own thread.
The intent of the service is to provide a background task that will communicate
to a device using a TCP socket and do some other stuff. At the risk of ignoring battery issues etc, basically I'd like it to run forever.
Something like
// Method that communicates using a TCP socket, and needs to send
// information back to the activity and receive messages from activity
// not shown here.
private void dummytask() {
boolean keepGoing = true;
while (keepGoing) {
// do useful stuff in here
// sets keepGoing false at some point
}
stopSelf();
}
What is the best way to initiate this method/task ?
I have looked at code in the developer site that uses a message handler and a looper, which I only partly understand, but it seems very complicated and perhaps more than I require?
I don't believe I can call this method from either onCreate() or onStartCommand() since then neither would complete when invoked from the system ? Should I start it with a timer or alarm?
I will need to add a message handler to communicate with the the gui activity, but since I'm starting the service in another thread (by virtue of the manifest "process" instruction), do I need to use AIDL instead?
I have also looked at using AysnchTask rather than extending Service, but it seems better suited to running a task and then terminating.
so that the service run in its own thread.
That puts the service in its own process. This is generally something to be avoided, as it consumes extra RAM and CPU (for IPC). You can create a thread just by creating a Thread or any number of other means, most of which have been in Java for a decade or so.
At the risk of ignoring battery issues etc, basically I'd like it to run forever.
It is pretty much impossible for a service to run forever. Users or the OS will get rid of your service eventually.
What is the best way to initiate this method/task ?
Call dummytask() from a background thread.
do I need to use AIDL instead?
No. Your service can broadcast an Intent, or invoke a PendingIntent supplied by the activity, or send a Message via a Messenger supplied by the activity, etc. The best would be to use the LocalBroadcastManager from the Android Support package, but that will not work across process boundaries, forcing you into more expensive communications options.
I think you could use a IntentService which you run by setting up a (regular) alarm (AlarmManager.setRepeating) with a PendingIntent in it. You can notify the UI by broadcasting an Intent from the IntentService and receiving it in your UI through a BroadcastReceiver.
I have a widget that, when pressed, sends a broadcast that starts a CountDownTimer that updates my widget, and at the end plays an alarm. This works beautifully in the emulator.
On my phone, however, it's a different story. My phone is so resource constrained that my process is killed regularly, which, of course, means that the CountDownTimer no longer updates my widget.
It seems to be that the only way to reliably do stuff in the future is to use the AlarmManager, as this sets a system-level alarm. However, the documentation states, and I agree, that you're not supposed to use it for ticks. However, since anything else you're likely to use has the possibility of being shut down arbitrarily, they're not really giving us much choice.
My question is: is there a way to ensure that a CountDownTimer keeps ticking and finally calls onFinish(), or do I have to simply drop it and switch to AlarmManager, and "misuse" it? Any other options of guaranteeing that the thing ticks and finishes are also welcome.
I should add that I can't rely on the OS calling onUpdate(), both because it will do so no faster than every 30 minutes, and also because most of the time the widget just does nothing. It's only when it is clicked that it ticks down every second for a handful of minutes.
I think this is a solution.. Create a dummy service..
public class DummyService extends Service{
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
and start it from your activity using countdowntimer like this.. in oncreate of the activity
Intent intent = new Intent(this, DummyService.class);
startService(intent);
and also dont forget to declare this service in your manifest like this..
<service android:name=".DummyService" >
</service>
hope this helps..
What I ended up doing was moving from a BroadcastReceiver to a Service.
Not just using a dummy service, but having a real service embody what the BroadcastReceiver did previously.
As this won't guarantee that my timers won't get killed, if requested, android will automatically try to restart your service after it's been killed. In this event, I plan to have some code that will restore the running state from disk and continue.
To add to the accepted answer: from what I remember, the BroadcastReceiver only lives as long as it takes to process the broadcast.
So launching a timer or something from within the BroadcastReceiver will not work (as stated).
This is why it is recommended to use the BroadcastReceiver to launch a Service that will do the timing. Sure, the service can still be killed, but not as early as the BroadcastReceiver.
I have a BroadcastReceiver that can successfully catch a broadcast, but if I try to call another method from it, it won't always work. Here's my setup:
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (working == true) {
//toast to make sure we got here
doWork();
}
}
}
The toast within the if gets called, but doWork doesn't execute. Has anyone dealt with a similar issue?
The Broadcast receiver doesn't have the same context and life cycle as your application, you can't do a lot of normal stuff in it. All your supposed to do is handle the event and return as quick as possible. In other words, start a service, or notify the user.
From: http://developer.android.com/reference/android/content/BroadcastReceiver.html
A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.
This has important repercussions to what you can do in an onReceive(Context, Intent) implementation: anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.
In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the NotificationManager API. For the latter, you can use Context.startService() to send a command to the service.
Most likely it's not staying awake long enough. Are you using a WakeLock to keep Android from going back to sleep?
So, firstly you'll need to add this to your manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
and then in your receiver //pseudo code except for the parts that matter...
public WakeLock wl;
#Override
public void onReceive(c,i){
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);//this matters
wl.getWakeLock().acquire(); //as does this
//do whatever but DON'T do it here... call a Service like this...
Intent i = new Intent(context, MyExcellentService.class);
try {
PendingIntent.getService(context, 0,i, 0).send();
}catch(Exception e){}
//hold on to wl in some way and release it in the service, not here,
wl.getWakeLock().release();
}
If you're going to use a service you'll need to register it in your manifest like so...
<service android:name="MyExcellentService" />
And I would extend the Application and hold on to the WakeLock there, so that you don't have to pass it around in more convoluted ways. You can just reach up into the Application and talk straight to it!
You'll want to look up the differences in the *kinds of wakeups PARTIAL_WAKE_LOCK will not keep the screen awake, for example, just the background processing.
Let me know if this works, I'm cutting and pasting from various places, so, I may be overlooking something.