关于android的alarmmanager使用过程中的坑(包括魅族手机休眠后无法启动闹钟的问题)

  在最近的开发过程中,需要完成一个本地闹钟的功能,需求是固定的3个闹钟,每日重复,可以手动开关,需要时间精准,闹钟响起时需弹出dialog和播放音乐。
  在alarmmanager的使用过程中主要参考了github上的一个demo , Android-AlarmManagerClock ,遇到了一些比较难解决的问题,现在整理一下以防止以后再采坑。

1.关于不同手机版本精确闹钟的兼容

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_REALTIMEAlarmManager.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 分钟发一次。

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

1017209-9aeac689a801b0a7.png

恍然大悟!原来是 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 傻眼了!

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

1017209-958b1bca16fe2651.png

啊啊啊啊啊啊!之前在 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();
    }
};

2.Android 7.0 pendingIntent用intent传递的bug

问题比较少见,只有你在跨进程传递数据的时候会碰到,如pendingIntent中

在7.0中通过pendingIntent的bundle传递的数据时,你会发现serializable和parcelable的数据拿不到

如果你只传了string,那是没问题的,但是如果你传了string和一个serializable你会发现,不光serializable拿不到,连string也拿不到了,黑人问好脸吧

原因参考:

https://commonsware.com/blog/2016/07/22/be-careful-where-you-use-custom-parcelables.html

解决方案1:

https://stackoverflow.com/questions/18000093/how-to-marshall-and-unmarshall-a-parcelable-to-a-byte-array-with-help-of-parcel/18000094#18000094

他的解决方法很独特,将所有数据转为基本类型bytes再去传递,当然,这可以解决我们的问题

然后我就想既然byte能解决,只传string也没问题,那为什么不干脆直接传string嗯,所以另一个简单的修改方法就是把所有的对象全部转成jsonstring去传递,也可以解决问题

解决方案2:

继续寻找有没有更加合适的方案时发现google的issuetracker中有个大神发现了一个更简单的方法,直接把所有数据放到bundle里,然后将bundler作为参数传递即intent.putExtra("data",bundle)也可以解决问题,

详情参考:

https://issuetracker.google.com/issues/37097877

参考代码:

        Intent intent = new Intent(ALARM_ACTION);
        Bundle bundle = new Bundle();
        bundle.putInt(AlarmConfig.DATA_BUNDLE_ALARM_ID,id);
        bundle.putLong(AlarmConfig.DATA_BUNDLE_INTERVALMILLIS, intervalMillis);
        intent.putExtra(AlarmConfig.DATA_BUNDLE, bundle);
        // 这个pendingIntent是Intent的包装类, 在闹钟到点的时候能够发送广播给AlarmOnTimeReceiver
        PendingIntent sender = PendingIntent.getBroadcast(context, id, intent, PendingIntent
                .FLAG_CANCEL_CURRENT);

另外两个问题是关于魅族手机适配的问题:
发现在魅族手机上出现2个问题,魅族M6版本 Flyme 6.1.4.6A ,Android 版本 7.1.2 :

  1. 实现一个通知栏通知的功能,闹钟响起来就出现通知通知栏,setContentText()中的内容要是有 感叹号(!) 就通知就出现不了,代码如下 :
    //发送通知
    private void showNotification(AlarmClock clock) {
    int NOTIFICATION_ID = clock.getId();
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setContentTitle
    ("闹钟响起了").setContentText("时间到了哦!").setAutoCancel(true).setColor(Color
    .rgb(134, 177, 194)).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon
    (BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).setDefaults
    (NotificationCompat.DEFAULT_ALL);
    //要跳转到主页面
    Intent intent = new Intent(this, HomeActivity.class);
    PendingIntent operation = PendingIntent.getActivity(this, NOTIFICATION_ID, intent, 0);
    builder.setFullScreenIntent(operation, false); // 悬挂式Notification(5.0 新增)
    builder.setContentIntent(operation);
    Notification notification = builder.build();
    mNotificationManager.notify(NOTIFICATION_ID, notification);
    }

2.使用AlarmManager ,在App中按Home键锁屏后闹钟失效(只有按Home键锁屏这一种情况,按返回键没有这个问题) , AlarmManager接收不到广播 ,导致闹钟失效

解决问题1最终还是没有解决
解决问题2:通过魅族手机-设置-关闭智能休眠模式

魅族手机的问题说明 手机厂商改动系统太多了, 给开发者带来了无数适配的坑.... = =

参考文章:
关于使用AlarmManager的注意事项
Android 7.0 pendingIntent bug(AlarmManager通过PendingIntent传递数据(跨进程数据传递)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容