Android power consumption and timing tasks

Feb 08, 2015


On Android platforms, there are about 3 approaches to execute timing tasks so far:
  • Handler or Timer:
    This is suitable for foreground tasks. The timing state can not be sustained once the app process being killed.
  • AlarmManager:
    Mostly use it to manage background tasks. The timing state is maintained across app process restarts, but not persisted across device reboots. AlarmManager can be used in almost all the circumstances. However, if not without care, it may cause severe power issues.
  • JobScheduler:
    JobScheduler API was introduced with the release of Lollipop to improve the power issues on Android platforms. If the exact time to run a task is not strictly required, use JobScheduler instead of AlarmManager.
  • 1. Handler or Timer

    When the app is at foreground interacting with user, use Handler or Timer to schedule timing tasks. If the app process is killed, we need to re-schedule the tasks. From the perspective of battery power, app developers need to make the schedule frequency as low as possible, and always remeber to cancel existing schedules while not needed any more as early as possible.

    2. AlarmManager

    With AlarmManager, Android system can invoke app components at single or multiple time spots to execute tasks. At the point of execution, if app process is not exist yet, Android creates it first and then triggers the the relevant components.
    2.1 Exact and InExact modes
    In AlarmManager API, there are two modes at the point of when tasks will be triggered:
  • Exact mode:
    The time when a task will be started is as precisely accurate as scheduled.
  • InExact mode:
    In order to reserve battery, Android may defer the task properly based on the scheduling parameter, so that a task can be executed in batch with other tasks.
  • The exsiting of the two modes is a combination of business requirements and power conservation consideration. Android may put the device into low-power sleeping status if there has been no user interaction for a given time (we don't consider the case there are additional WakeLocks accquired). Assume there are 3 timing tasks registered with AlarmManager, all of which can wake up the device (WAKEUP type, see the next chapter) with different execution time. In case all of them are set as Exact mode, then the device will be waken up 3 times, entering sleep state after one task, until waken up again by next task. But if they are InExact mode tasks, Android system may be able to merge them so that the device may be waken up only once, in which all of the 3 tasks are executed; or the device keeps sleeping until there is any user interaction which gives the chance the run the tasks. In such cases the wake-up times of InExact mode is dynamically reduced, effectively preserves the battery power.

    There are multiple methods in AlarmManager for scheduling a task:
    MethodBefore KitKatSince KitKat
    set()ExactInExact
    setExact()Exact
    setInexactRepeating()InExactInExact
    setRepeating()ExactInExact
    setWindow()InExact in range


    All the repeating tasks are set as InExact mode since KitKat. If we need precise triggering time, use setExact() to set the next start time inside each execution. The impact on battery power is much more severe for repeating tasks comparing with single time tasks if the interval is set too short. It is believed to be the reason that Google changes the behavior of scheduling repeating tasks.

    Additionally, besides Exact and InExact, setWindow() provides an intermediate mode. The method provides two long parameters to define a time window. Android ensures a task will be executed at some point inside that window, not outside it. In case the control of InExact mode is too loose while Exact mode is too strict for business requirements, consider using setWindow().
    2.2 Wakeup and Non-wakeup tasks
    There are totally 4 types of tasks scheduled by AlarmManager: ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC, RTC_WAKEUP. In addtion to the difference of time calculation, they differs on whether devices can be waken by tasks. For a Wakeup type task, while it is in Exact mode, it is guaranteed to be started at the exact time as scheduled, no matter whether the device was sleeping or not, which is definitely negative to the power resource. We should always avoid using Wakeup types if there is no strict requirement, or any algorithm can be applied to work around.
    2.3 An example
    A network provider supplies WiFi access service. As the limitation of server side architecture, they need client side to make the billing. Their initial implementation was to register a background timing task for exact every 15 minutes with setExact(RTC_WAKEUP). However, they got large complains from users that the app drained their device batteries quickly. In order to improve the user experience, they considered both InExact mode. Tasks of InExact mode are not predictable as they may not be triggered for one or even more intervals, which may cause too much business loss. So at final they choose to use setWindow() to solve this issue:
    long currentMillis = System.currentTimeMillis();
    setWindow(RTC, currentMillis, 15*60*1000L, <PendingIntent>);
    Note that RTC type is used instead of RTC_WAKEUP. As there is no network activity during system sleep, so billing that time-span is meaningless. With the scheduling window, it is assured that billing task will be executed at some points between 15 and 30 minutes later. The solution takes care of both the business requirements and power conservation.

    The principle of JobScheduler

    Generally speaking, a task should be started only when all the prerequisites are fulfilled. Under traditional AlarmManager mechanism, Android system wakes up the relevant app components at the triggering point, without any consideration of the prerequisite. There are grealy possibilities that the task discovers that some prerequisites are not fulfilled right after being started. In such a case the task checks the conditions and quits immediatelly, which is totally a waste of power resources. If the prerequisites checking can be moved into a system service(system service process doesn't get killed), any un-fulfilling of conditions eliminates the necessity of app process creation and components activation. This is actually the design of JobScheduler.

    With this design, it's not easy to implement a compatible version of JobScheduler for old Android versions. If we make it the way as AppCompat, then it won't provide any benefit --- the checking of prerequisites is still inside app process, so that Android system still needs to create the app process as the first step no matter whether the conditions are met or not. An negative example of this is the JobSchedulerCompat project (https://github.com/evant/JobSchedulerCompat). The library itself has severe power issues. For Android version less than Lollipop, as it depends greatly on WakefulBroadcastReceivers and WakeLocks, as well as invoking setComponentEnabledSetting(DONT_KILL_APP) which makes the app process less likely to be killed, using the library will consume more power resources, which is a paradox and self-contradiction.

    Compared with AlarmManager, JobScheduler has following features:
  • less power consumption:
    It is suitable for background tasks without precise triggering time spots. It's not recommanded for foreground tasks, as the un-predictable of scheduling.
  • back-off mechanism:
    set back-off policy with setBackOffCriteria(). However, back-off policy is easy to be confused on repeating tasks, so only applying back-off for single-time tasks.
  • scheduling state persistant:
    Tasks registered with AlarmManager will not exist after system reboot. To solve this issue, app develpers must register a receiver for reboots, and register tasks again inside that Receiver. JobScheduler persists the data across device reboots so there is no need to worry about that.

    Summary

    For any foreground task, use Handler as it's more simple, intuitive and less likely to be harmful. For background tasks without strict time requirement, use JobScheduler API on Android 5.0, and InExact mode through AlarmManager API on previous Android versions. For background tasks which seems like to be with accurate timing requirement, consider whether the requirement can be degraded by any algorithm with JobScheduler or timing window on AlarmManager. As app developer, we should be sensitive to tasks of both Wakeup type and Exact mode, as it increases the possibility of uninstallation of the app by users greatly.