Need Code Example on How to Run an Android Service Forever in The Background Even When Device Sleeping, Like Whatsapp

Need code example on how to run an Android service forever in the background even when device sleeping, like Whatsapp?

NOTE: NOW THIS ANSWER IS ONLY VALID FOR ANDROID 7 AND BELOW. SINCE ANDROID 8 GOOGLE HAS CHANGED HOW BACKGROUND TASKS ARE HANDLED

Since I posted this question, I have implemented two different approaches to this solution into multiple apps.


APPROACH 1

This extract is from an app where I use push notifications, which need instant wake up calls for the device. Here what I do is

  1. use WAKE_LOCK permission and
  2. use a Wakelocker abstract class
  3. use it in an Activity as needed:

Manifest:

<uses-permission android:name="android.permission.WAKE_LOCK" />

WakeLocker class:

public abstract class WakeLocker {
private static PowerManager.WakeLock wakeLock;

public static void acquire(Context context) {
if (wakeLock != null) wakeLock.release();

PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE, "WakeLock");
wakeLock.acquire();
}

public static void release() {
if (wakeLock != null) wakeLock.release(); wakeLock = null;
}
}

Activity class example:

private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Waking up mobile if it is sleeping
WakeLocker.acquire(getApplicationContext());
// do something
WakeLocker.release();
}

APPROACH 2

Best when you want to give Android control over wake up, and can live with periodically waking up your code. Simply use an AlarmManager to invoke a Service class at regular intervals. Here is some code from my LifeLog24 app:

MainActivity

Intent ll24 = new Intent(context, AlarmReceiverLifeLog.class);
PendingIntent recurringLl24 = PendingIntent.getBroadcast(context, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, first_log.getTime(), AlarmManager.INTERVAL_HOUR, recurringLl24); // Log repetition

Alarm Class

public class AlarmReceiverLifeLog extends BroadcastReceiver {

private static final String TAG = "LL24";
static Context context;

@Override
public void onReceive(Context context, Intent intent) {

Log.v(TAG, "Alarm for LifeLog...");

Intent ll24Service = new Intent(context, LifeLogService.class);
context.startService(ll24Service);
}
}

and LifeLogService.class is where I do my stuff. Alarm wakes up every hour in this case and triggers the BroadcastReceiver which in return runs the service. There is more to it, to make sure service is not run twice and so on, but you get the point how it is done. And AlarmManager is actually the best way to do it since you don't worry about battery usage, etc. and Android takes care of waking up your Service at regular intervals.

Keep a Service running even when phone is asleep?

Note: This post has been updated to include the JobScheduler API of the Android Lollipop release. The following is still a viable way, but can be considered deprecated if you're targeting Android Lollipop and beyond. See the second half for the JobScheduler alternative.

One way to do recurrent tasks is this:

  • Create a class AlarmReceiver

    public class AlarmReceiver extends BroadcastReceiver 
    {
    @Override
    public void onReceive(Context context, Intent intent)
    {
    Intent myService = new Intent(context, YourService.class);
    context.startService(myService);
    }
    }

    with YourService being your service ;-)

If you require a wake lock for your Task, it is advisable to extend from WakefulBroadcastReceiver. Don't forget to add the WAKE_LOCK permission in your Manifest in this case!

  • Create a Pending Intent

To start your recurrent polling, execute this code in your activity:

Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
//myAlarm.putExtra("project_id", project_id); //Put Extra if needed
PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Calendar updateTime = Calendar.getInstance();
//updateTime.setWhatever(0); //set time to start first occurence of alarm
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, recurringAlarm); //you can modify the interval of course

This code sets up an alarm and a canceable pendingIntent. The alarmManager gets the job to repeat the recurringAlarm every day (third argument), but inexact so the CPU does wake up approximately after the interval but not exactly (It lets the OS choose the optimal time, which reduces battery drain). The first time the alarm (and thus the service) is started will be the time you choose to be updateTime.

  • last but not least: here is how to kill the recurring alarm

    Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
    //myAlarm.putExtra("project_id",project_id); //put the SAME extras
    PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager alarms = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarms.cancel(recurringAlarm);

This code creates a copy of your (probably) existing alarm and tells the alarmManager to cancel all alarms of that kind.

  • of course there is also something to do in the Manifest:

include these two lines

  < receiver android:name=".AlarmReceiver"></receiver>
< service android:name=".YourService"></service>

inside the < application>-tag. Without it, the system does not accept the start of recurrent alarm of a service.


Starting with the Android Lollipop release, there's a new way of solving this task elegantly.
This also makes it easier to only perform an action if certain criteria such as network state are met.

// wrap your stuff in a componentName
ComponentName mServiceComponent = new ComponentName(context, MyJobService.class);
// set up conditions for the job
JobInfo task = JobInfo.Builder(mJobId, mServiceComponent)
.setPeriodic(mIntervalMillis)
.setRequiresCharging(true) // default is "false"
.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED) // Parameter may be "ANY", "NONE" (=default) or "UNMETERED"
.build();
// inform the system of the job
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(task);

You may also provide a deadline with setOverrideDeadline(maxExecutionDelayMillis).

To get rid of such a task, just call jobScheduler.cancel(mJobId); or jobScheduler.cancelAll();.

Run a service in the background forever..? Android

At my first try, I use a timer in a
service class, and schedule the
battery check every 10 mins. But soon
I found that the service got paused
when the screen goes off.

You probably did not hold a WakeLock, so the device fell asleep.

it seems that the AlarmService stop at
some point after several hours

I rather doubt it.

So my question is, has anyone written
some service to run (like forever) in
the background before?

It is not possible to create a service that will run forever. It should be possible to create a scheduled task via AlarmManager that will be invoked "forever".

I am currently reading some posts
saying that there's a partial wake
lock I can use to keep the service
alive.. is this the correct way to do
it?

I'm not sure what "it" is. But, if you want to keep the device awake -- whether for your first approach or just while you are doing work triggered by an AlarmManager -- you need to hold a WakeLock.

Here is a project that does almost exactly what you describe for the AlarmManager, minus checking the battery level, but using a WakefulIntentService to ensure the device stays awake. If you cannot get this code to run until the battery shuts down, join the cw-android Google Group and report your findings, and I'll take a look at it.

Service is killed in sleep mode.Why?

The murder mystery has been solved, and I know what killed my service. Here's what I did:

  1. After I realized that startsticky, startforeground, alarmmanager, scheduleTaskExecutor, and even wakelock were unable to save my service, I realized the murderer couldn't be the Android system, because I had taken every measure possible to prevent the system from killing my service and it still would get killed.
  2. I realized I needed to look for another suspect, since the service wasn't dying because of the system. For that, I had to run an investigation. I ran the following command:

    adb shell dumpsys activity processes > tmp.txt

This would give me a detailed log of all the processes running and their system priorities. Essentially, tmp.txt would be the detective in this murder mystery.


  1. I looked through the file with lots of detail. It looked like my service was prioritized properly by the system:

    Proc #31: adj=prcp /FS trm= 0 2205:servicename.service/uID (fg-service)

The above line indicates the exact priority of a process running on the Android device. adj=prcp means the service is a visible foreground service.

At this point, I realized that my service must be encountering some error a couple hours after running, so I let it run and die. After it died, I produced a dumpsys again to examine the error:


  1. At this point, my service wasn't listed as a task in the tmp.txt file. Excited, I scrolled to the bottom of the dumpsys and solved the mystery!

com.curlybrace.ruchir.appName.MyService$2.onForeground(MyService.java:199)
at com.rvalerio.fgchecker.AppChecker$2.run(AppChecker.java:118)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)

The stack trace that caused the killing of my service was displayed right there! Essentially, a variable that would check for the foreground app being used would become null after a few hours of inactivity, which would cause an exception, and kill the service!

Key Takeaways:
If your service is getting killed, and you've done everything you can to make sure that it shouldn't be killed, perform a dumpsys and examine the nitty gritty of your device's activity process. I guarantee you will find the issue that way.

I still would like to have the bounty awarded to @Khemraj since his answer could be a great solution for someone who hasn't started their service properly. However, I am accepting this answer since it is the solution that actually fixed the issue.

How to run an application forever after installing?

In AndroidManifest.xml

<receiver android:name=".BootUpReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

Create Java File named with BootUpReceiver

public class BootUpReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
//Do your coding here...
}
}
}

Keep app service active during device sleep

It depends on the Android version, for version older than 6 a partial wakelock is enough to keep the device awake, for Android 6 you also need a foreground service, that's a Service that calls startForeground() and shows a notification, but to keep the device awake has a big impact in battery usage.

You do not necessarily need to transfer all the code to the Service due it is the whole application that stays awake.

A more elegant solution to replace all this would probably be to use Push Notifications, it is what most messaging applications use. Firebase has Push Notifications.

How can I periodically run a service/thread in background even when the screen is locked?

I have changed a few things and it works well now.

1.As my phone is 4.4.2(api=19), alarmmanager.setrepeating is inexact. So I turn to use .setExact (new method of .set()) and reschedule the alarm at the end of AsyncTask(network) in Service.

2.Make wakelock instance global, acquiring it in AlarmReceiver and releasing at the end of the AsyncTask. I used to put .release() in onDestroy() which releases the lock before the task is done.

3.There is a setting about protected-background applications in my phone and I didn't turn it on. That can allow system kill the application and disable the alarm manager.



Related Topics



Leave a reply



Submit