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" />
Related
I work on one api call in background service every 10 minute, this one worked same as when application is close, how it is possible?
your question is not fully clear. I'm assuming that the following is your question and will try to answer it -
" How to run services that run in background and also continuously runs when the app is closed to connect to remote servers ? "
The best way to achieve this is by using the JobScheduler API. JobScheduler helps you to perform network operations efficiently by queueing your requests in batches thereby saving battery life. This helps you to give a better user experience.
To use the JobScheduler API you will have to create a JobService. JobService extends Service, enabling the system to run your job even if the app is in background. You will be required to implement the following methods :
onStartJob()
onStopJob()
For complicated tasks like network requests, return true in onStartJob() to let the system know that a background network thread is still running and hold on to the wake lock until the network thread has finished. The JobService runs on the main thread like any other service and you have to take care of running network operations in a separate thread like AsyncTask.
onStopJob() is called when the job conditions to run the job are not matched. Return true to tell the system to automatically run/reschedule the job when job conditions are met.
Below is an example code to help you better understand what's going on -
public class GetImageService extends JobService {
private GetImageTask getImageTask;
#Override
public boolean onStartJob(final JobParameters params) {
getImageTask = new GetImageTask() {
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
jobFinished(params, true);
}
};
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
if (getImageTask != null) {
getImageTask.cancel(true);
}
return false;
}
private class GetImageTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
// todo: connect to remote servers and make a network call here
return null;
}
}
}
As JobService is a Service, you must declare it in the application manifest file. Add the BIND_JOB_SERVICE permission and set exported to true to let the system access your JobService.
<service
android:name=".activity.GetImageService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
JobScheduler shows it's real power with it's conditions that you set using the JobInfo object. JobScheduler works based on time and various conditions. This makes you not to write AlarmManager or Service and save the phone battery by not making unnecessary network calls. You can set conditions like network required which means your JobService will only run when there's network connection. Setting a condition as persisted will ensure your job to run even after the phone reboots.
JobScheduler jobScheduler = (JobScheduler)
getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(new JobInfo.Builder(1000,
new ComponentName(this, GetImageService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.build());
Calling the schedule() method ensures your job gets scheduled by the system. This makes your job to run even when all the conditions are met in background without the user even opening the app. For example, you could use this to update your tables with the latest data from the servers even before the user asks for it. This will help you to provide a very good user experience by making the data available as soon as the user opens the app and not making him to wait for data.
With Android O releasing this year you should also consider reading about background limits. With Android O Google prefers developers use JobScheduler extensively.
Refer to this blog post by Google for more info - https://medium.com/google-developers/scheduling-jobs-like-a-pro-with-jobscheduler-286ef8510129
Also an example app on GitHub by developers at Google - https://github.com/romannurik/muzei/tree/master/main/src/main/java/com/google/android/apps/muzei/sync
Use Alarm manager api see below Scheduling Repeating Alarms https://developer.android.com/training/scheduling/alarms.html.
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.
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!
I want a service to run all the time in my application. So I want to restart it even if it is force closed by user. There is definitely a way to do it as apps like facebook are doing it. It's not done using push notification, facebook restarts its service even if internet is off.
First of all, it is really very bad pattern to run service forcefully against the user's willingness.
Anyways, you can restart it by using a BroadcastReceiver which handles the broadcast sent from onDestroy() of your service.
StickyService.java
public class StickyService extends Service
{
private static final String TAG = "StickyService";
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
sendBroadcast(new Intent("YouWillNeverKillMe"));
}
}
RestartServiceReceiver.java
public class RestartServiceReceiver extends BroadcastReceiver
{
private static final String TAG = "RestartServiceReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "onReceive");
context.startService(new Intent(context.getApplicationContext(), StickyService.class));
}
}
Declare the components in manifest file:
<service android:name=".StickyService" >
</service>
<receiver android:name=".RestartServiceReceiver" >
<intent-filter>
<action android:name="YouWillNeverKillMe" >
</action>
</intent-filter>
</receiver>
Start the StickyService in a Component (i.e. Application, Activity, Fragment):
startService(new Intent(this, StickyService.class));
OR
sendBroadcast(new Intent("YouWillNeverKillMe"));
You have to create a sticky service with overriding onTaskRemoved method, where you can set an alarm service to trigger your code again.
public class BackgroundService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
//create an intent that you want to start again.
Intent intent = new Intent(getApplicationContext(), BackgroundService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 5000, pendingIntent);
super.onTaskRemoved(rootIntent);
}
}
Also in some devices like Xiaomi, Huwaei the app gets force closed once it's removed from recent apps. This is because the manufacturers have task manager features which improve ram/battery performance.
You can check this link for more information: https://stackoverflow.com/a/41360159/2798289
As per the Android document
Starting from Android 3.1, the system's package manager keeps track of applications
that are in a stopped state and provides a means of controlling their launch from
background processes and other applications.
Note that an application's stopped state is not the same as an Activity's stopped
state. The system manages those two stopped states separately.
FLAG_INCLUDE_STOPPED_PACKAGES — Include intent filters of stopped applications in the
list of potential targets to resolve against.
FLAG_EXCLUDE_STOPPED_PACKAGES — Exclude intent filters of stopped applications from the
list of potential targets.
When neither or both of these flags is defined in an intent, the default behavior is to
include filters of stopped applications in the list of potential targets.
Note that the system adds FLAG_EXCLUDE_STOPPED_PACKAGES to all broadcast intents.
It does this to prevent broadcasts from background services from inadvertently or
unnecessarily launching components of stopped applications. A background service
or application can override this behavior by adding the FLAG_INCLUDE_STOPPED_PACKAGES
flag to broadcast intents that should be allowed to activate stopped applications.
On Force stop of app, Android just kill the process ID. No warnings, callbacks are given to service/activities. As per the Android document, When the app is killed there are chances that it calls onPause().
When I tried in my app, even onPause() was not called. I think the only way is use to FLAG_INCLUDE_STOPPED_PACKAGES intent flag and send it from another app
If I understand correctly, then actually this is not possible, Android feature to force close application was designed to allow user to get rid of unwanted applications, so it disallows any activities from it until user again starts any of its Activity.
Restart the service even if app is force-stopped and Keep running service in background even after closing the app How?
Whenever a service is killed, its onDestroy method is always called.
Its better to use a BroadcastReceiver to start your service when it is killed.
Here is a sample code illustrating its implementation:-
#Override
public void onDestroy() {
Intent in = new Intent();
in.setAction("StartkilledService");
sendBroadcast(in);
Log.d("debug", "Service Killed");
}
Then register a receiver in AndroidManifest.xml:-
<receiver android:name=".app.ServiceDestroyReceiver" >
<intent-filter>
<action android:name="StartKilledService" >
</action>
</intent-filter>
</receiver>
Finally,create a BroadcastReceiver,and start your service in the onReceive method:-
#Override
public void onReceive(Context context, Intent intent) {
Log.d("debug", "ServeiceDestroy onReceive...");
Log.d("debug", "action:" + intent.getAction());
Log.d("debug", "Starting Service");
ServiceManager.startService();
}
Hope this helps.
on the service's startCommand method return START_STICKY. generally it tell the OS to start the service when it is killed.
If the situation allows to use 'root' it's usually possible to implement Humpty-Dumpty paradigm.
Your application (1st) installs another application (2nd, taking APK from assets) and runs the service of the 2nd app.
2nd app's service bind to the 1st app service and rebinds when disconnected. The 1st app does the same.
Sure it will not help when all apps are killed by some Free RAM or similar application but when Android kills either of those two, the other one will restart its counterpart.
The only real solution for keeping services alive ist to call Service.startForeground(...) with a provided Notification. This will be the only valid solution, every other one will be very dependent on how Google will change the behaviour of it's system. With every API update, Google could prevent every other hack.
This also keeps the user aware, that your app is performing some background task which will keep the app alive and the user has to stop this. If you provide the user the ability to stop it is part of your application, though.
See the Documentation:
void startForeground (int id, Notification notification)
Make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state. By default services are background, meaning that if the system needs to kill them to reclaim more memory (such as to display a large page in a web browser), they can be killed without too much harm. You can set this flag if killing your service would be disruptive to the user, such as if your service is performing background music playback, so the user would notice if their music stopped playing.
There is a very hacky solution to keep service running even you force stop it. I do not recommend that because it is against user willingness. You can define a broadcast receiver to receive intent with action X. onStartCommand handler of your service, broadcast X (if the service is not started yet). on broadcast receiver upon receipt of X, first start the service, then, sleep for some minutes, and finally re-broadcast X.
I think the only foolproof solution here is to have 2 services in separate processes (android:process="somecustomprocessname" in manifest, in the service entry) that both listen to broadcasts and restart each other, because currently the UI doesn't let users kill multiple processes in one action. You can then set up a pinger thread in each service that checks if the other service is running every 100 milliseconds or so, and if not, attempts to restart it. But this is starting to look more and more like malware...
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