美文网首页互联网科技Android开发经验谈
合理使用应用唤醒Alarm,让你的应用更省电

合理使用应用唤醒Alarm,让你的应用更省电

作者: 技术爱好 | 来源:发表于2019-03-07 11:22 被阅读4次
    0.jpg

    不少细心的开发者已发现,在华为终端开放实验室发布的最新绿色应用达标率调查报告中,国内千款主流应用有63款应用在灭屏时Alarm占用大于20次/小时,这些应用最终也因为此检测项未通过导致无法获得绿色应用标记。

    那么在绿色应用检测过程中为什么会把灭屏时Alarm占用作为一个重要衡量标准进行检测呢,开发者又该如何针对此项标准对应用进行开发呢?本篇文章将给出你答案。

    安卓设备为了节省电量,在设备无操作时会将屏幕变暗,然后灭屏,最终休眠CPU。但有时应用可能需要不同的行为,比如一些游戏或视频类应用可能需要持续亮屏,还有一些应用可能不需要持续亮屏,但需要CPU保持运行,直到完成相关关键操作。为此,安卓系统提供了在必要时保持设备唤醒且省电的方案,分别是Wakeup和AlarmManager,这也就是Alarm(闹钟)在安卓系统存在的原因。

    一、Alarm是什么

    熟悉安卓系统的开发者都知道,Alarm可以完成闹钟式定时任务,安卓系统主要通过AlarmManager类对其进行管理,我们可以通过AlarmManager在一些Alarm设定的时间点启动服务进行事件处理,同时还可以用Alarm来初始化一些长时间运行的操作,手机每天启动一个服务来下载天气预报的数据其实就是Alarm最熟悉的实用场景。

    我们了解了Alarm是用来做什么的,那么Alarm都有哪些特性呢?

    二、Alarm特性

    1、Alarm允许在设定的时间或时间间隔发送Intent(意图);

    2、将Alarm与Broadcast Receiver(广播接收器)进行结合启动Service(服务),并执行其它操作;

    3、Alarm可在应用之外运行,所以即使应用没有启动、设备处于休眠状态,也可以通过Alarm来触发应用事件及操作;

    4、Alarm可以使应用对于系统资源进行最小化占用,可以在不依赖timer(计时器)或者后台持续运行的Service来执行一些操作;

    三、Repeating Alarm(重复闹钟)

    Repeating Alarm(重复闹钟)是一种相对灵活的简单机制,但并非在所有场景下都适合,在应用需要触发网络操作时,如果使用Repeating Alarm,可能会因为Alarm设计不当导致电量过度消耗,增加服务器负载。但通常情况下,在应用运行之外触发操作需要从服务器同步数据,这就需要借助Repeating Alarm来实现。当要同步数据的服务器是自有的,Google Cloud Messaging(GCM)配合sync adapter同步框架是比AlarmManager更好的解决方案。sync adapter同步框架可以提供AlarmManager所有的功能,而且更加灵活。

    当设备在Doze模式下处于空闲状态时,Alarm不会被触发。任何预先设置的Alarm将被推迟,直到设备退出Doze模式。如果需要设备在空闲状态也能完成相关操作,可以使用setAndAllowWhileIdle()或setExactAndAllowWhileIdle()来保证Alarm被执行;也可以使用新的WorkManager API来完成后台单次或定期的执行工作。使用setInexactRepeating()时,不能像使用setRepeating()时自定义间隔,如果要自定义间隔,必须使用间隔常量:

    INTERVAL_FIFTEEN_MINUTES、INTERVAL_DAY等

    四、Repeating Alarm最佳实践

    使用Repeating Alarm进行的每一个设置都会影响应用对系统资源的占用,那么该如何以最小的系统资源占用使用Repeating Alarm呢?

    在Repeating Alarm触发的网络请求里添加随机性(抖动)操作:

    ①当Alarm触发时,先执行无需访问服务器或从服务器获取数据的操作。

    ②与此同时,对于包含网络请求的Alarm,在设定时间上添加一些随机变量。

    降低Alarm触发频率。

    除非必要,否则不使用唤醒设备的Alarm。

    除非必要,否则不要使用高精度的RTC时钟来触发Alarm。

    使用setInexactRepeating()来替换setRepeating()。当使用setInexactRepeating()时,安卓系统会同步多个应用的Repeating Alarm,并同时触发它们。这样可以减少系统唤醒设备的总次数,从而减少手机电量消耗。从安卓系统 4.4(API级别19)开始,所有Repeating Alarm都是不精确的。尽管setInexactRepeating()相对于setRepeating()做了改进,但如果手机上的应用都在接近的时间内集中访问服务器,仍会给服务器造成压力。因此,很有必要对于网络请求的Alarm添加一些随机性。

    尽可能避免使用基于RTC的Alarm。

    基于RTC的Alarm扩展性不好,使用ELAPSED_REALTIME对Alarm进行设置更好一些。

    五、Repeating Alarm的设置

    Repeating Alarm适合用来执行常规事件及查找数据,接下来我们来认识一下Repeating Alarm具有哪些特性?

    Alarm类型的选择;

    触发时间,如果设定的触发时间已经过去了,则Alarm会立即触发;

    Alarm的间隔:每天、每小时、每5分钟等;

    触发Alarm时需执行的Pending Intent,如果第二个Alarm设置使用之前存在的Pending Intent,系统会默认替换之前的Alarm。

    六、选择一个Alarm类型

    设置Repeating Alarm时首要考虑因素就是它的类型。

    Alarm有两种通用时钟类型:“elapsedreal time(相对时间)”和“real time clock(RTC 实时时间)”。相对时间使用“自系统启动以来的时间”作为参考,实时时间使用UTC时间。这意味着相对时间适合根据时间的推移设置Alarm,因为它不受时区/区域设置的影响,实时时间更依赖于当前区时设置Alarm。

    两种时钟类型都有一个设备“唤醒”机制,可以在屏幕熄灭后唤醒CPU,这就确保了Alarm在设定时间被触发。如果你的应用具有时间依赖性(例如要求在有限窗口执行特定操作),可以使用该机制。如果不使用设备“唤醒”机制,所有Repeating Alarm都将在设备下次唤醒时全部触发。

    如果以特定时间间隔触发Alarm,最好使用相对时间类型。反之,如果需要在一天中的特定时间触发Alarm,则需要选择一种基于UTC时间的实时时间类型。

    以下是所有时钟类型:

    ELAPSED_REALTIME——根据设备启动后的时间来处理Pending Intent,但不会唤醒设备,设备休眠时间也会被统计进来。

    ELAPSED_REALTIME_WAKEUP——在设备启动后经过设定的时间长度,唤醒设备并处理Pending Intent。

    RTC——在设定时间处理Pending Intent,但不会唤醒设备。

    RTC_WAKEUP——唤醒设备并在设定时间处理Pending Intent。

    七、Alarm设置示例

    1、相对时间Alarm设置示例

    以下是使用ELAPSED_REALTIME_WAKEUP进行Alarm设置示例。

    30分钟内唤醒设备并触发Alarm,之后每30分钟触发一次Alarm:

    // Hopefully your alarm will have a lower frequency than this!
    alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    AlarmManager.INTERVAL_HALF_HOUR,
    AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
    一分钟内唤醒设备并触发一个一次性Alarm:

    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;
    ...
    alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmReceiver.class);
    alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

    alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    SystemClock.elapsedRealtime() +
    60 * 1000, alarmIntent);
    2、实时时间Alarm设置示例

    以下是一些使用RTC_WAKEUP进行Alarm设置的示例。

    在下午2点左右唤醒设备并触发Alarm,之后每天在同样的时间重复触发一次这个Alarm:

    // Set the alarm to start at approximately 2:00 p.m.
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 14);

    // With setInexactRepeating(), you have to use one of the AlarmManager interval
    // constants--in this case, AlarmManager.INTERVAL_DAY.
    alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
    AlarmManager.INTERVAL_DAY, alarmIntent);
    在上午8点30分准时唤醒设备,之后每隔20分钟触发一次Alarm:

    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;
    ...
    alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmReceiver.class);
    alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

    // Set the alarm to start at 8:30 a.m.
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 8);
    calendar.set(Calendar.MINUTE, 30);

    // setRepeating() lets you specify a precise custom interval--in this case,
    // 20 minutes.
    alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
    1000 * 60 * 20, alarmIntent);

    八、Alarm设置的取消

    应用取消Alarm设置,需要调用AlarmManager里cancel()的方法,把不想被触发的PendingIntent实例传入cancel()里。例如:

    // If the alarm has been set, cancel it.
    if (alarmMgr!= null) {
    alarmMgr.cancel(alarmIntent);
    }

    九、设备重启时如何触发Alarm

    默认情况下,设备关闭时会同时关闭所有Alarm。为防止这种情况发生,可以在用户重新启动设备时应用自动重新启动Repeating Alarm。可以确保AlarmManager在用户无需手动重启Alarm的情况下继续执行其任务。

    以下是步骤:

    1、在应用manifest中设置RECEIVE_BOOT_COMPLETED权限。允许应用在系统完成启动后接收ACTION_BOOT_COMPLETED广播(仅在用户已经至少启动过应用一次时才有效):

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    2、实现一个BroadcastReceiver来接收广播:

    public class SampleBootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
    // Set the alarm here.
    }
    }
    }
    3、使用可过滤的ACTION_BOOT_COMPLETED操作的Intent过滤器将receiver添加到应用程序的manifest文件中:

    <receiver android:name=".SampleBootReceiver"
    android:enabled="false">
    <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED"></action>
    </intent-filter>
    </receiver>
    4、在manifest文件中,将启动receiver设置为android:enabled="false"。这意味着除非应用明确启用receiver,否则不会被调用,这可以防止启动receiver被不必要地调用。可以按如下方式启用receiver:

    ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
    PackageManager pm = context.getPackageManager();

    pm.setComponentEnabledSetting(receiver,
    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    PackageManager.DONT_KILL_APP);
    以这种方式启用receiver后,即使设备被重新启动,receiver也会保持启用状态。换句话说,通过代码启用receiver会覆盖manifest文件的设置,即使重新启动也是如此,receiver将继续启用,直到应用将它关闭。可以按如下方式禁用receiver:

    ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
    PackageManager pm = context.getPackageManager();

    pm.setComponentEnabledSetting(receiver,
    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    PackageManager.DONT_KILL_APP);

    十、Doze和App Standby模式对Alarm的影响

    Doze和App Standby模式在安卓6.0(API级别23)中被引入,旨在延长设备电池寿命。当设备处于Doze模式时,任何标准Alarm都将会延迟,直到设备退出Doze模式或维护窗口被打开。如果需要在Doze模式下触发Alarm,可以通过使用setAndAllowWhileIdle()或setExactAndAllowWhileIdle()。应用在一段时间内未被使用,同时应用在前台没有任何进程时,将进入App Standby模式。当应用处于App Standby模式时,Alarm会像在Doze模式下一样延迟。当应用运行或设备接入电源时,此限制将被解除。

    DevEco检测方案
    综上所述,频繁唤醒设备的Alarm对设备电池寿命影响较大,华为DevEco云测平台通过检测应用在后台灭屏1小时内触发唤醒设备Alarm的次数来衡量应用是否存在不合理使用Alarm的情况。具体测试方法如下:

    将应用安装,启动,正常操作几分钟后,回到首页,放置后台,灭屏。执行以下指令:

    清理上次的测试数据:adb shell dumpsys batterystats --reset

    允许记录所有wake信息:adb shell dumpsys batterystats --enable full-wake-history

    模拟拔除电缆:adb shell dumpsys battery unplug

    一小时后,执行adb bugreport > bugreport.txt导出bugreport报告

    通过分析bugreport(参考Battery Historian的搭建),Wakeup alarm info里面的Alarm累计唤醒次数进行判断。

    本文参考《Schedule repeating alarms》进行翻译整理

    文章原地址为:

    https://developer.android.com/training/scheduling/alarms

    相关文章

      网友评论

        本文标题:合理使用应用唤醒Alarm,让你的应用更省电

        本文链接:https://www.haomeiwen.com/subject/witcpqtx.html