Android App开发学习第十三天:不同SDK版本AlarmManager实现重复调用。

博文出处:关于使用AlarmManager的注意事项,欢迎大家关注我的博客,谢谢!

快过年了,更新春节前的最后一篇博客。

最近在做一个需求:客户端按照规定的时间间隔向服务端发送定位。一看到这个需求就想到了使用 AlarmManager 来实现。 AlarmManager 经常被用来执行定时任务,比如设置闹铃、发送心跳包等。也许有人会有疑问:为什么不能使用相同具有定时效果的 Timer 和 Handler 呢?

其实答案非常简单,相对于 Handler 来说,使用 sendEmptyMessageDelayed 方法是依赖于 Handler 所在的线程的,如果线程结束,就起不到定时任务的效果;而 AlarmManager 依赖的是 Android 系统的服务,具备唤醒机制。比起 Handler 也就更合适了。

而至于 Timer 可以精确地做到定时操作,但是相比于 AlarmManager 而言还是差了一截。同理,如果手机关屏后长时间不使用, CPU 就会进入休眠模式。这个使用如果使用 Timer 来执行定时任务就会失败,因为 Timer 无法唤醒 CPU 。

所以,综上所述,AlarmManager 就成为了最佳选择。

SDK API < 19
一般情况下,使用 AlarmManager 来执行重复定时任务的代码如下所示:

alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);

或者

alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), TIME_INTERVAL, pendingIntent);

setRepeating 该方法用于设置重复定时任务。

第一个参数表示闹钟类型:一般为 AlarmManager.ELAPSED_REALTIME_WAKEUP 或者 AlarmManager.RTC_WAKEUP 。它们之间的区别就是前者是从手机开机后的时间,包含了手机睡眠时间;而后者使用的就是手机系统设置中的时间。所以如果设置为 AlarmManager.RTC_WAKEUP ,那么可以通过修改手机系统的时间来提前触发定时事件。另外,对于相似的 AlarmManager.ELAPSED_REALTIME 和 AlarmManager.RTC 来说,它们不会唤醒 CPU 。所以使用的频率较少;
第二个参数表示任务首次执行时间:与第一个参数密切相关。第一个参数若为 AlarmManager.ELAPSED_REALTIME_WAKEUP ,那么当前时间就为 SystemClock.elapsedRealtime() ;若为 AlarmManager.RTC_WAKEUP ,那么当前时间就为 System.currentTimeMillis() ;
第三个参数表示两次执行的间隔时间:这个参数没什么好讲的,一般为常量;
第四个参数表示对应的响应动作:一般都是去发送广播,然后在广播接收 onReceive(Context context, Intent intent) 中做相关操作。
至此,一切顺利,畅通无阻。

SDK API >= 19 && SDK API < 23
当你写好代码、满心欢喜地将程序跑在手机上的时候,傻眼了!你会发现在 Android 4.4 及以上版本的定时任务不是按照规定时间间隔来执行的。比如你设置了每隔 3 分钟发出一个 HTTP 请求,结果你一看莫名其妙地变成了隔 5 分钟发一次。

What the fuck?

what the fuck
然后你查阅 Android 官网中关于 Android 4.4 API 会看到如下几句话:

Android 4.4 API
恍然大悟!原来是 Google 为了追求系统省电,所以“偷偷加工”了一下唤醒的时间间隔。但也正如上面官网中所说的那样,如果在 Android 4.4 及以上的设备还要追求精准的闹钟定时任务,要使用 setExact() 方法。

所以,相应的代码就变成了这样:

// pendingIntent 为发送广播
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else {
    alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);
}

private BroadcastReceiver alarmReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 重复定时任务
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
        }
        // to do something
        doSomething();
    }
};

当你写好了“加强版”的 AlarmManager 之后,内心肯定无比小激动。这下总应该行了吧?运行一下,果然没错!在 Android 4.4 上的确按照规定的时间间隔在执行任务。哈哈,这下大功告成了!!!

SDK API >= 23
在 Android 4.4 上品尝到胜利的甜头后,你顺便在 Android 6.0 的设备上测试了一下。结果。。。。。。你又 TMD 傻眼了!

What the fuck
发现在设备关屏静止一段时间后, AlarmManager 又又又不能正常工作了。相必此时你连日狗的心都有了吧!强忍着泪水,再次打开 Android 官网中关于 Android 6.0 变更 ,发现在 Android 6.0 中引入了低电耗模式和应用待机模式。然后接着往下看 对低电耗模式和应用待机模式进行针对性优化 ,发现会有下面一段话:

Android 6.0 API
啊啊啊啊啊啊!之前在 Android 4.4 上能用的 setExact() 方法在 Android 6.0 上因为低电耗模式又不能正常使用了。但是,Google 又又又提供了新的方法 setExactAndAllowWhileIdle() 来解决在低电耗模式下的闹钟触发。

所以,Attention!相关的代码又被改写为这样:

// pendingIntent 为发送广播
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else {
    alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);
}

private BroadcastReceiver alarmReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 重复定时任务
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
        }
        // to do something
        doSomething();
    }

};
到了这里,总算是把因 Android 版本差异导致 AlarmManager 的“坑”填完了。这份代码已经可以满足日常的重复定时任务了。

好了,该讲的都讲完了,上床睡觉。仓促地结尾,预祝大家新年快乐!

Goodbye !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您解答这个问题。首先需要了解一下 AlarmManager 是什么。AlarmManagerAndroid 系统提供的一种定时服务,可以在指定的时间点或者间隔时间到达时,执行指定的操作。而 AppWidget 是 Android 系统提供的一种小部件,可以在桌面上显示应用程序的一些信息或操作。 为了实现 AppWidget 的实时更新,我们可以使用 AlarmManager 来定时触发更新操作。下面是注册 AlarmManager 的步骤: 1. 获取 AlarmManager 实例: ```java AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); ``` 2. 创建 PendingIntent 对象: ```java Intent intent = new Intent(this, UpdateWidgetService.class); PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, 0); ``` 其中,UpdateWidgetService 是我们自定义的服务,用来更新 AppWidget 的内容。 3. 设置 AlarmManager 的触发时间和重复间隔: ```java long intervalMillis = 30 * 60 * 1000; // 30 分钟 long triggerAtMillis = System.currentTimeMillis() + intervalMillis; alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, intervalMillis, pendingIntent); ``` 这里的 setRepeating() 方法会在 triggerAtMillis 时间点开始,每隔 intervalMillis 时间重复触发 PendingIntent 对象。 需要注意的是,AppWidget 的更新间隔时间应该适当,过短会导致电量消耗过大,过长则不能及时更新,影响用户体验。建议根据实际需求来设置更新间隔时间。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值