Keeping a background service alive on Android - android

In reference to Accelerometer seems to only work for a few seconds?
I'm using the BOOT_COMPLETED broadcast event to call the following:
public class OnBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, ShakeEventService.class);
context.startService(service);
}
}
Which in turn calls this:
public class ShakeEventService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
sManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sManager.registerListener(thisSensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
return Service.START_STICKY;
}
}
There are lots of other functions and whatnot within that class, obviously, and technically everything works fine, except that regardless of what flag I return (START_STICKY, START_STICKY_COMPATIBILITY, START_NOT_STICKY, or START_REDELIVER_INTENT), the service is being killed after anywhere between 5 and 25 minutes and doesn't re-initiate until the next boot.
I've read a lot about Android 4.2 introducing new battery optimisations that kill off background processes, and I've read all sorts of bug reports with START_STICKY no longer working, but the various work-arounds seem to not be making any difference in my case. Not sure whether I'm attempting to implement them wrongly or if these workarounds have just stopped working in the more recent versions. My test device is running 5.1.1.
I'm at a bit of a loss here and would appreciate some help again please.
EDIT
For anybody else stumbling into this problem, the accepted answer below includes a working wakelock that keeps the service alive on Nexus 7 running Android 5.1.1 and LG G4 running Android 6.
Additionally, prefixing the android:process attribute of the manifest file with a colon does seem important for keeping the wakelock in place. Ref http://developer.android.com/guide/topics/manifest/service-element.html
Still no luck on a Samsung S6 Edge running the same Android 5.1.1 though, so it looks like Samsung implement something within their battery optimisations that's causing a premature kill..

All you have to do to have a service that is constently alive is:
<!-- BackGroundService Service -->
<service
android:name="com.settings.BackgroundService"
android:enabled="true"
android:exported="false"
android:process=":my_process" />
2) onStartCommand should return:
return Service.START_STICKY;
and that's all buddy after I Start the service from the main activity it is on for the whole day/

You need to use a Partial Wakelock with the service. If your service is interactive with the user, you might consider making the service foreground.
This is a working example for a Partial Wakelock:
Use this when service is started:
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock cpuWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
cpuWakeLock.acquire();
When the service is stopped, do call cpuWakeLock.release();
imports android.support.v4.content.WakefulBroadcastReceiver which
Android Studio is telling me doesn't exist.
You need to import the support library for that.

To do the trick use JobScheduler and JobService which allow you to execute code periodically.
First create a class that extends JobService, then implement required methods and add your service code/start another service from within the JobService onStartJob() method.
Here's an example for executing a JobService operation periodically:
ComponentName serviceComponent = new ComponentName(context, yourJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
builder.setRequiresDeviceIdle(false);
builder.setRequiresCharging(false);
builder.setPeriodic(60000); // <<<<---- 60 seconds interval
JobScheduler jobScheduler = (JobScheduler)context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(builder.build());
You can find more info here:
https://developer.android.com/reference/android/app/job/JobService.html
Good luck!

Related

How can I make my app running also in Doze mode without Partial_Wakelock? [duplicate]

One of my peer developer has written an intent service that makes an API call and then sleeps for 2 mins. After waking up, it sends again.
Below is the code:
public class GpsTrackingService extends IntentService {
....
#Override
protected void onHandleIntent(Intent intent) {
do{
try{
//make API call here
//then go to sleep for 2 mins
TimeUnit.SECONDS.sleep(120);
} catch(InterruptedException ex){
ex.printStackTrace();
}
} while (preferences.shouldSendGps()); //till the user can send gps.
}
....
}
Manifest
<service android:name=".commons.GpsTrackingService" />
This is working fine when the phone is active. However, whenever the phone goes into doze mode it fails to wake.
Will using alarm manager with WAKE permission solve this?
I have just got the code base and need to fix this within today. It'll be great if someone can help.
As the documentation says:
In Doze mode, the system attempts to conserve battery by restricting
apps' access to network and CPU-intensive services. It also prevents
apps from accessing the network and defers their jobs, syncs, and
standard alarms.
Periodically, the system exits Doze for a brief time to let apps
complete their deferred activities. During this maintenance window,
the system runs all pending syncs, jobs, and alarms, and lets apps
access the network.
In few words, while in Doze mode the system suspends network accesses, ignores Wake Locks, stops acquiring data from sensors, defers AlarmManager jobs to the next Doze maintenance window (which are progressively less frequently called), also WiFi scans, JobScheduler jobs and Sync adapters do not run.
Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 (?) minutes, per app.
And it seems that the Foreground Services are also involved into this "Doze Drama", at least in MarshMellow (M).
To survive in this situation, tons of applications need to be at least rewiewed. Can you imagine a simple mp3 player which stops playing music when the device enters in Doze Mode?
Doze mode starts automatically, when the device is unplugged from the power supply and left on the table for about 1 hour or so, or even earlier when the user clicks the power button to power down the screen, but I think this could depend by the device manufacturer too.
I tried a lot of countermeasures, some of them really hilarious.
At the end of my tests I reached a possible solution:
One possible (and maybe the only) way to have your app running even when the host device is in Doze mode, is basically to have a ForegroundService (even a fake one, doing no jobs at all) running in another process with an acquired partial WakeLock.
What you need to do is basically the following (you could create a simple project to test it):
1 - In your new project, create a new class which extends Application (myApp), or use the
main activity of the new project.
2 - In myApp onCreate() start a Service (myAntiDozeService)
3 - In myAntiDozeService onStartCommand(), create the Notification
needed to start the service as a foreground service, start the
service with startForeground(id, notification) and acquire the
partial WakeLock.
REMEMBER! This will work, but it is just a starting point, because you have to be careful with the "Side Effects" this approach will generate:
1 - Battery drain: The CPU will work for your app forever if you
don't use some strategy and leave the WakeLock always active.
2 - One notification will be always shown, even in the lockscreen,
and this notification cannot be removed by simply swiping it out, it
will be always there until you'll stop the foreground service.
OK, let's do it.
myApp.java
public class myApp extends Application {
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
#Override
public void onCreate() {
super.onCreate();
// start foreground service
startForeService();
}
private void stopForeService() {
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STOPFOREGROUND_ACTION);
stopService(service);
}
private void startForeService(){
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STARTFOREGROUND_ACTION);
startService(service);
}
#Override
public void onTerminate() {
stopForeService();
super.onTerminate();
}
}
myAntiDozeService.java
public class myAntiDozeService extends Service {
private static final String TAG = myAntiDozeService.class.getName();
private static boolean is_service_running = false;
private Context mContext;
private PowerManager.WakeLock mWakeLock;
private static final int NOTIFICATION_ID = 12345678;
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!is_service_running && STARTFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Start Foreground Intent ");
showNotification();
is_service_running = true;
acquireWakeLock();
} else if (is_service_running && STOPFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Stop Foreground Intent");
is_service_running = false;
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
#Override
public void onDestroy() {
releaseWakeLock();
super.onDestroy();
}
private void showNotification(){
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(mContext)
.setContentTitle("myApp")
.setTicker("myApp")
.setContentText("Application is running")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
// starts this service as foreground
startForeground(NOTIFICATION_ID, notification);
}
public void acquireWakeLock() {
final PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
releaseWakeLock();
//Acquire new wake lock
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG+"PARTIAL_WAKE_LOCK");
mWakeLock.acquire();
}
public void releaseWakeLock() {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
AndroidManifest.xml changes.
In the AndroidManifest.xml add this permission:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Don't forget to add the name of your app in the <application> tag:
<application
....
android:name=".myApp"
....
And finally add your foreground service running into another process:
<service
android:name=".myAntiDozeService"
android:process=":MyAntiDozeProcessName">
</service>
A couple of notes.
In the previous example, the notification created, when clicked,
opens the ActivityMain activity of your test project.
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
but you can use another kind of intent too.
To test it, you have to add some job to be performed into your
ActivityMain.java, for example some repeating alarm (which was
normally stopped when the device falls in Doze Mode), or a ripetitive
network access, or a timed tone played, or.... whatever you want.
Remember that the job performed by the main activity has to run
forever because to test this AntiDoze you need to wait at least 1
hour to be sure the device enters in Doze Mode.
To enter in Doze mode, the device has to be quiet and unplugged, so
you can't test it while you are debugging. Debug your app first,
check that everything is running then stop it, unplug, restart the
app again and leave the device alone and quiet on your desk.
The adb commands suggested by the documentation to simulate Doze
and StandBy modes could and could not give you the right results
(it depends, I suppose, by the device manufacturer, drivers, bla
bla). Please make your tests in the REAL behaviour.
In my first test, I used an AlarmManager and a tone generator to play a tone every 10 minutes just to understand that my app was still active.
And it is still running from about 18 hours, breaking my ears with a loud tone exactly every 10 minutes. :-)
Happy coding!
One of my peer developer has written an intent service that makes an API call and then sleeps for 2 mins. After waking up, it sends again.
Only have a service running while it is actively delivering value to the user. Sitting around for two minutes, watching the clock tick, is not actively delivering value to the user.
Will using alarm manager with WAKE permission solve this?
That depends on what you mean by "solve this". You can use AlarmManager to request to get control every two minutes so that you can do work. While the device is in Doze mode, you will not actually get control every two minutes, but once per maintenance window.

Job Scheduler vs Background Service

I have an app which has a feature A which should run in background every minute. Feature A is that the app should connect to a database, read some data then get the current location of the device and based on them check a condition, if the condition is true it should send a statusbar notification to the user so that when the user clicks on the notification the UI of the app will be displayed and something happens.
This background task should run permanently every minute, regardless the app is used, closed, terminated (like facebook or Whatsapp that show us notifications regardless they are in the app stack or not).
Now I have searched and have found that Android offers Job Scheduler,Background Service, AlarmManager and Handlers.
But the more I read about them the more contradictory the statements appear to me.
About Handlers I have read that they do not exist for long delays
and will be terminated after system reboot. So they won't be
appropriate for my task.
But AlarmManager seems to be a good candidate for the problem because when permitted they exist even after system reboot and can
rerun the app. But in the Android Documentation that the Alarm
Manager is intended to be used for tasks that have to be run at a
specific time (like the Alarm Clock). But my task has to be run
every minute.
Then there is Background Service. This is more for tasks like downloading in the background as I have read and not intended for
doing something I have explained.
JobScheduler seems not to be for a task that has to be done in permanently, but for tasks that fulfill a specific constraint like
idle, or no network... So which of these (or other ones if they
exist) do you recommend to use for the task I explained in the first
part
I have an app which has a feature A which should run in background every minute.
That will not happen on hundreds of millions of Android devices, those running Android 6.0 and higher, due to Doze mode (and, possibly, app standby, depending on the rest of your app).
But AlarmManager seems to be a good candidate for the problem because when permitted they exist even after system reboot
No, they do not. You need to reschedule all alarms scheduled with AlarmManager after a reboot.
the Alarm Manager is intended to be used for tasks that have to be run at a specific time
AlarmManager supports repeating options.
This is more for tasks like downloading in the background as I have read and not intended for doing something I have explained.
A Service will be essential for whatever solution you wind up using.
JobScheduler seems not to be for a task that has to be done in permanently, but for tasks that fulfill a specific constraint like idle, or no network
JobScheduler, as with AlarmManager, supports repeating jobs.
So which of these (or other ones if they exist) do you recommend to use for the task I explained in the first part
Use none of them, as you cannot run things every minute on Android 6.0+ once the device goes into Doze mode, which will be within an hour of the screen turning off. Instead, either redesign the app to only need background work a few times per day, or do not bother writing the app.
You can use modern JobScheduler API which was introduced in Android 5.0 if your minSdkVersion=21.
Also there is https://github.com/firebase/firebase-jobdispatcher-android which requires installed Google Play minSdkVersion=9
But I recommend to use this library https://github.com/evernote/android-job where depending on the Android version either the JobScheduler, GcmNetworkManager or AlarmManager will be used.
With these APIs you can schedule your job and run service which describes task.
UPDATE
Now it is better to use new WorkManager (docs). android-job will be deprecated soon
First, a JobService is a Service. A background service is ambiguous, let me guess you mean a service that runs in the background thread. Job Service runs on the ui thread but you can create an async task object within it to make it run in the background.
From your question, JobService is not the way to go.What i suggest is:
You can create a class that extends IntentService (this runs on the background thread) in the onDestroy method of that class, send a broadcast and make the broadcast restart the service.
#onDestroy(){
Intent broadcastIntent = new
Intent("com.example.myapp.serviceRestarted");
sendBroadcast(broadcastIntent);}
Create a class that extends broadcast reciever
public class RestartServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context,
MyService.class));
}
}
In your manifest, register your service and reciever
<receiver
android:name=".RestartServiceReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.myapp.serviceRestarted" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
The boot permision is to enable the reciever be called the the system has finished booting, and once the reciever is called, the service will be called again.
Above Lollipop, i.e, API version 21, You can use a JobScheduler to schedule a JobService. To repeat a job every minute, you'll have to schedule the job everytime it is finished by setting the minimum latency to 60*1000 milliseconds.
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
boolean isWorking = false;
boolean jobCancelled = false;
#Override
public boolean onStartJob(JobParameters params) {
Log.d("_____TAG_____", "MyJobService started!");
isWorking = true;
doWork(params);
return isWorking;
}
private void doWork(JobParameters params) {
if (jobCancelled)
return;
//Create a new thread here and do your work in it.
//Remember, job service runs in main thread
Log.d("_____TAG_____", "MyJobService finished!");
isWorking = false;
boolean needsReschedule = false;
jobFinished(params, needsReschedule);
scheduleRefresh();
}
#Override
public boolean onStopJob(JobParameters params) {
Log.d("_____TAG_____", "MyJobService cancelled before being completed.");
jobCancelled = true;
boolean needsReschedule = isWorking;
jobFinished(params, needsReschedule);
return needsReschedule;
}
private void scheduleRefresh() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
ComponentName componentName = new ComponentName(getApplicationContext(), MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
builder.setMinimumLatency(60*1000); //1 minute
JobInfo jobInfo = builder.build();
JobScheduler jobScheduler = (JobScheduler)getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
int resultCode = jobScheduler.schedule(jobInfo);
if (resultCode == JobScheduler.RESULT_SUCCESS) {
Log.d("_____TAG_____", "MyJobService scheduled!");
} else {
Log.d("_____TAG_____", "MyJobService not scheduled");
}
}
}
}
You can write a common function, anywhere you like, to schedule the job for the first time -
public void scheduleMyJobService() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
ComponentName componentName = new ComponentName(context, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
builder.setMinimumLatency(60*1000);
JobInfo jobInfo = builder.build();
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
int resultCode = jobScheduler.schedule(jobInfo);
if (resultCode == JobScheduler.RESULT_SUCCESS) {
Log.d("_____TAG_____", "MyJobService scheduled!");
} else {
Log.d("_____TAG_____", "MyJobService not scheduled");
}
}
}
According to this
and other link in comment 1 below
You should use AlarmManager for your task.
If you need to set alarms that fire while in Doze, use:
setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
For a full easy to understand explanation for the different ways to do stuff in the background read:
https://www.bignerdranch.com/blog/choosing-the-right-background-scheduler-in-android/
Good Luck!
In the previous versions of Android, people used Handler or background services for this purpose. After a while, they announced alarm manager class for permanent, scheduled works.
Whatsapp, facebook or some social media applications mostly use google cloud messaging for the notification purpose which is not useful for you.
I will recommend you to use Alarm manager for this. After the KitKat version(4.2), Operating System blocks the background handler for better use of battery.
Background services are mostly used for image upload or some heavy process which has an ending time. When you are sending a video to your friend on Whatsapp, background process starts and uploads the video to backend server.
I am not sure about JobScheduler api for supporting the older versions of support, but it is as good as Alarm Manager.
you can do it by using service, with return start_sticky in "START_STICKY tells the OS to recreate the service after it has enough memory and call onStartCommand() again with a null intent. START_NOT_STICKY tells the OS to not bother recreating the service again. There is also a third code START_REDELIVER_INTENT that tells the OS to recreate the service and redeliver the same intent to onStartCommand()"
and set a TIMER with period 1 minute and do execute your code.
As well if you want to restart the service when the user force stop it, you can do that "as previous answers"
You can create a class that extends IntentService (this runs on the background thread) in the onDestroy method of that class, send a broadcast and make the broadcast restart the service.
#onDestroy(){
Intent broadcastIntent = new Intent("com.example.myapp.serviceRestarted");
sendBroadcast(broadcastIntent);
}
Create a class that extends broadcast receiver
public class RestartServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, MyService.class));
}
}
In your manifest, register your service and receiver
<receiver
android:name=".RestartServiceReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.myapp.serviceRestarted" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Also, you can use AlarmManager and If you need to set alarms that fire while in Doze, use:
setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
set it "current time in second + 60 sec" so you will set it next minute.
and execute your code and in the last, reset the AlarmManager next minute.
Also, you can start your service or AlarmManager after reboot the device
just use a brodcastReciever when "RECEIVE_BOOT_COMPLETED"
and put this permission:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Android Service running on separate Process gets killed when I swipe out my App (running in other process)

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.

BroadcastReceiver dies with app

If i let the phone sit for a long time like 15 minutes i lose my receiver but i thought it was to persist like a service after being killed for memory.
Manifest:
<receiver
android:name=".WearableReceiver"
android:enabled="false">
<intent-filter>
<action android:name="com.example.johnbravado.MESSAGE_PROCESSED"/>
</intent-filter>
</receiver>
In Activity to start receiver
ComponentName component = new ComponentName(CounterActivity.this, WearableReceiver.class);
getPackageManager()
.setComponentEnabledSetting(component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
The receiver
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
//MyConstants.getInstance().showToast("Message Rcvd");
PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"com.example.johnbravado");
wakeLock.acquire();
// Do Work
MyConstants.getInstance().msgReqAction(intent.getIntExtra(MyConstants.BROADCAST_DATA_REQ, 0));
wakeLock.release();
}
The broadcast sender
String BROADCAST_ACTION_RESP = "com.example.johnbravado.MESSAGE_PROCESSED"
#Override
public void onMessageReceived(final MessageEvent messageEvent) {
nodeId = messageEvent.getSourceNodeId();
String incomingPath = messageEvent.getPath();
int incomingReq = Integer.parseInt(new String(messageEvent.getData()));
if(incomingPath.equalsIgnoreCase(MyConstants.MSG_COUNTER_REQ_PATH)) {
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(BROADCAST_ACTION_RESP);
broadcastIntent.putExtra(MyConstants.BROADCAST_DATA_REQ, incomingReq);
sendBroadcast(broadcastIntent);
}else if(incomingPath.equalsIgnoreCase(MyConstants.MSG_DEFAULT_PATH)){
}
}
only way I get this to persist for long periods of time is to invoke a service
wearableReceiverIntent = new Intent(this, WearableReceiverService.class);
if(!WearableReceiverService.isRunning())
startService(wearableReceiverIntent);
the service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Let it continue running until it is stopped.
IntentFilter filter = new IntentFilter(MyConstants.BROADCAST_ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new WearableReceiver();
registerReceiver(receiver, filter);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notif_bible)
.setContentText("Preaching").build();
startForeground(MyConstants.NOTIF_COUNTING_SERVICE, notification);
isRunning = true;
return START_STICKY;
}
If I run the service it persists for long periods of time but it drains the battery unnecessarily considering I interact only once every 10 minutes. I was under impression Broadcast receiver would work like service except for short bursts of work. invoke the service if you need to do long actions.
A BroadcastReceiver handles an intent and then stops again. This handling of an intent should be fast. If you want to do a lot of stuff, you should start an Service from the BroadcastReceiver and handle it from there.
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.
A BroadcastReceiver is started using the sendBroadcast intent.
So remove android:enabled="false" and use sendBroadcast, which will startup the Receiver by Android.
http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html
Greenify was killing my app when the screen went off. I was battling something I had no hope of defending against with code. After I explicitly told Greenify to not kill my app, I never told it to kill my app to begin with, everything worked as intended.
I had the same problem due on my Asus ZenPad due to the Asus Mobile Manager app, specifically the "Auto start manager" was blocking the intent to my app.
Deactivating the app (uninstall is not possible) worth nothing, the solution has been to leave the app installed but whitelist my developing app so it can receive broadcast like PACKAGE_REPLACE. (Pay attention that the switches are confusing, you actually have to touch on "blocked" so it turns on into "allowed" to enable it.
I think another option is to update or change the ROM (choosing one without all that bloatware).
If your BroadcastReceiver is setup in your manifest, there is no need to try and adjust the PackageManager component information for your package. As long as you remove the enabled="false" part.
Your BroadcastReceiver should be very short with what it does: typically update some internal data or start another component which can do the heavy lifting of your app's operation. You can use it to trigger a Service to do this type of thing in the background. But, note that "background" in this case means without user-interaction. It does not mean a background context of execution, such as a secondary thread. It is up to you do manage the thread(s) in your Service. Your BroadcastReceiver and Service callback entry points (onReceive() and onStartIntent()) run in the context of the main thread of your app.
Power management definitely plays a roll in all of this. Is your broadcast Intent actually being sent and done in a way which will wake the device? If it does wake the device and send the Intent, the device will only stay awake long enough for the BroadcastReceiver to run its onReceive(); after that returns there are no guarantees. The device will aggressively sleep, which is why wakelocks are a thing. However, use of wakelocks can cause excessive battery drain, unless used properly. If you are running on Marshmallow or newer, the Doze functionality can also wreck havoc on your plans. Wakelocks are ignored when in Doze mode and won't be considered until the user brings the device out of doze.
I had the same issue and it was resolved by granting auto launch permission for the app.
Go to
Settings->Permissions->Manage Auto Launch
and allow auto launch for your app.

START_STICKY does not work on Android KitKat

One of my apps has a backgrouod service that uses the START_STICKY return code from onStartCommand to automatically restart when the system kills it.
It seems that this is no longer working on Android KitKat.
Is there any solution for this ?
Should I be doing something different on Kitkat to keep the service running ?
Note: There is a similar discussion on the Android-Devlopers group about swiping the app from the recent apps list behaves. Could this two issues be related ?
https://groups.google.com/forum/#!topic/android-developers/H-DSQ4-tiac
Edit: Saw that there are open bugs on Android issue tracker:
https://code.google.com/p/android/issues/detail?id=63793
https://code.google.com/p/android/issues/detail?id=63618
Edit2: The same happens even if service is running using startForeground, in a separate process and with the flag android:stopWithTask="false" in the AndroidManifest.xml file...
Edit3: More related bugs on Android issue tracker:
https://code.google.com/p/android/issues/detail?id=62091
https://code.google.com/p/android/issues/detail?id=53313
https://code.google.com/p/android/issues/detail?id=104308
Is there some sort of workaround to get the previous behavior ?
Seems that this is a bug present in Android 4.4, got around it with the following:
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartService = new Intent(getApplicationContext(),
this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);
}
Found this answer from this post
The problem here appears to not to occur on AOSP based ROMs. That is, I can easily recreate this on a CyanogenMod 11 based ROM, but on an AOSP ROM (and on an Emulator), START_STICKY behaves exactly as I'd expect. That said, I am seeing reports from folks on Nexus 5's that appear to be seeing this behavior, so perhaps it is still an issue in AOSP.
On an emulator and on an AOSP ROM, I see the following from a logcat when I do a 'kill 5838' against the process (as I'd expect):
12-22 18:40:14.237 D/Zygote ( 52): Process 5838 terminated by signal (15)
12-22 18:40:14.247 I/ActivityManager( 362): Process com.xxxx (pid 5838) has died.
12-22 18:40:14.247 W/ActivityManager( 362): Scheduling restart of crashed service com.xxxx/com.xxxx.NotifyingService in 5000ms
12-22 18:40:19.327 I/ActivityManager( 362): Start proc com.xxxx for service xxxx.pro/com.xxxx.NotifyingService: pid=5877 uid=10054 gids={50054, 3003, 3002, 1028}
I see the same restart behavior if I end the task by 'swiping' from the recent tasks list. So this is all good - it means that the core AOSP code is behaving as it has in previous levels.
I am looking at the Cyanogenmod service code to try and figure out why things aren't getting scheduled for restart - no luck yet. It appears that it should reschedule it. Cyanogenmod uses a service map which AOSP doesn't - but unclear whether that is an issue or not (doubtful)
https://github.com/CyanogenMod/android_frameworks_base/blob/cm-11.0/services/java/com/android/server/am/ActiveServices.java#L2092
A rather hackish workaround you can do is to use a similar mechanism as your onTaskRemoved AlarmService to enable an alarm for X minutes later. Then every few minutes while your app is up and running, you can reset the alarm - so it only goes off if things really have been killed and not restarted. This isn't foolproof - using a Handler gives you uptime vs the alarm service which uses realtime, so it's possible for your alarm to trigger even though it was set at a longer time than your 'reset' handler. But if you set an intent extra you can chose to ignore the onStartCommand if your service was already up and running, turning this into a noop.
I'm not a fan of the following hack at all - but it shouldn't do any real harm. If the user does an explicit Force Close, then the alarm manager will destroy any alarms set so that the service won't restart (which is what the user wants).
First, create a helper method that will set an alarm for 20 minutes which will cause onStartCommand to be triggered for your service. Every 2 minutes have a Handler which will reset the 20 minute alarm. If the handler runs within the realtime 20 minutes, the alarm will never go off. The handler isn't guaranteed to run though if the device is asleep (which is good).
private void ensureServiceStaysRunning() {
// KitKat appears to have (in some cases) forgotten how to honor START_STICKY
// and if the service is killed, it doesn't restart. On an emulator & AOSP device, it restarts...
// on my CM device, it does not - WTF? So, we'll make sure it gets back
// up and running in a minimum of 20 minutes. We reset our timer on a handler every
// 2 minutes...but since the handler runs on uptime vs. the alarm which is on realtime,
// it is entirely possible that the alarm doesn't get reset. So - we make it a noop,
// but this will still count against the app as a wakelock when it triggers. Oh well,
// it should never cause a device wakeup. We're also at SDK 19 preferred, so the alarm
// mgr set algorithm is better on memory consumption which is good.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
// A restart intent - this never changes...
final int restartAlarmInterval = 20*60*1000;
final int resetAlarmTimer = 2*60*1000;
final Intent restartIntent = new Intent(this, NotifyingService.class);
restartIntent.putExtra("ALARM_RESTART_SERVICE_DIED", true);
final AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Handler restartServiceHandler = new Handler()
{
#Override
public void handleMessage(Message msg) {
// Create a pending intent
PendingIntent pintent = PendingIntent.getService(getApplicationContext(), 0, restartIntent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + restartAlarmInterval, pintent);
sendEmptyMessageDelayed(0, resetAlarmTimer);
}
};
restartServiceHandler.sendEmptyMessageDelayed(0, 0);
}
}
In your onCreate you can call this method. Also - in your onStartCommand, be sure to ignore this if your service is already up and running. EG:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
if ((intent != null) && (intent.getBooleanExtra("ALARM_RESTART_SERVICE_DIED", false)))
{
Log.d(TAG, "onStartCommand after ALARM_RESTART_SERVICE_DIED");
if (IS_RUNNING)
{
Log.d(TAG, "Service already running - return immediately...");
ensureServiceStaysRunning();
return START_STICKY;
}
}
// Do your other onStartCommand stuff..
return START_STICKY;
}
This is not a 100% working solution but it's the best so far as it almost completely eliminates the problem. So far I integrated this solution along with overriding onTaskRemoved (See this answer) and a keep-alive notification (See this answer).
Additional answers are very appreciated !
After further investigation, it seems that the bug already exists in Jelly Bean and looks like there is a solution for that (At least in my case that seems to work. will keep on testing and update the answer if required).
From what I observed this only happens with services that receive broadcasts set by AlarmManager.
To reproduce the bug follow these steps:
Start the app
start the service as a foreground service (use startForeground for that) from within the app
Swipe the app from "Recent Apps" list
Send a broadcast that is handled by the service
The service is killed !
Using adb shell dumpsys >C:\dumpsys.txt you can monitor the state of the service between the different steps. (look for Process LRU list in the dumpsys output)
on steps 2 and 3 you will see something like this:
Proc # 2: prcp F/S/IF trm: 0 11073:<your process name>/u0a102 (fg-service)
Specifically, notice the F/S/IF and the (fg-service) that indicate the service is running as a foreground service (more details on how to analyze the dumpsys at this link: https://stackoverflow.com/a/14293528/624109).
After step 4 you will not see your service in the Process LRU list.
Instead, you can look at the device logcat and you will see the following:
I/ActivityManager(449): Killing 11073:<your process name>/u0a102 (adj 0): remove task
What seems to be causing that behavior is the fact that the received broadcast takes the service out of its foreground state and then killed.
To avoid that, you can use this simple solution when creating your PendingIntent for the AlarmManager (Source: https://code.google.com/p/android/issues/detail?id=53313#c7)
AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent("YOUR_ACTION_NAME");
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, 0);
Pay attention to the following steps:
Call addFlags on the intent and use FLAG_RECEIVER_FOREGROUND
Use a non-zero request code in PendingIntent.getBroadcast
If you leave any of those steps out it will not work.
Note that the FLAG_RECEIVER_FOREGROUND was added on API 16 (Jelly Bean) so it makes sense that this is when the bug first appeared...
Most likely that KitKat is just more aggressive when it comes to killing processes and this is why it was emphasized with KitKat, but looks like this was already relevant on Jelly Bean.
Note 2: Notice the details in the question about the service configuration - running in a separate process, as a foreground service, with endWithTask set to false in the manifest.
Note 3: The same thing happens when the app receives the android.appwidget.action.APPWIDGET_CONFIGURE message and shows a configuration activity for a new widget (Replace step 4 above with creating a new widget). I found that only happens when the widget provider (the receiver that handles android.appwidget.action.APPWIDGET_UPDATE) is set to run on a different process than the activity process. After changing that so both the configuration activity and the widget provider are on the same process, this no longer happens.
i found this simple trick to solve this problem without using AlarmManager.
create a broadcast receiver that listens broadcast everytime onDestroy() method in service is called:
public class RestartService extends BroadcastReceiver {
private static final String TAG = "RestartService";
public RestartService() {
}
#Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "onReceive");
context.startService(new Intent(context, YourService.class));
}
}
add customized broadcast intent to your manifest
<receiver
android:name=".RestartService"
android:enabled="true" >
<intent-filter>
<action android:name="restartApps" />
</intent-filter>
</receiver>
then, send broadcast from onDestroy(), probably like this:
#Override
public void onDestroy() {
Intent intent = new Intent("restartApps");
sendBroadcast(intent);
super.onDestroy();
stopThread();
}
call onDestroy() from onTaskRemoved(Intent intent)
this trick will restart your service everytime user close service from both task manager and force close from settings, i hope this will help you too

Categories

Resources