使用AlarmManager实现定时提醒,解决锁屏后无法正常唤醒CPU和屏幕的问题

之前写过一个demo来实现定时操作,最近帮别人写项目的时候又用到了这个需求,于是coding起来。

首先来说一下我的实现思路:用户选择好闹钟提醒时间后我要启动一个Service,在这个Service里面设置闹钟,通过闹钟直接打开一个Activity来显示提醒信息。我想要创建一个不在通知栏中显示notification的前台Service,这样就可以让Service一直运行了,除非你在手机的“最近任务”中kill掉这个应用,实现灰色启动Service的方法不详细介绍。

来看看AlarmManager的使用和问题。

根据android官方文档的说明:

android API 19之前,我们直接使用AlarmManager#set(@AlarmType int type, long triggerAtMillis, PendingIntent operation)来设置一次性闹钟或者使用AlarmManager#setRepeating(@AlarmType int type, long triggerAtMillis,long intervalMillis, PendingIntent operation)来设置重复闹钟,通过这种方式设置闹钟会定时准确送达,即使你的APP没有运行。

android API 19-android API 23之间,为了最小化唤醒和电池的使用,之前的set方法已经不再像之前那样好用(从API 19开始,Alarm的机制都是非准确传递的),官方提供了新的API进行设置:AlarmManager#setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation)AlarmManager#setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation)。setExact用来设置某个时间点准时送达,用于对时间要求很高的功能上,而setWindow则用来设置在windowStartMillis到windowStartMillis+windowLengthMillis时间范围内送达,用于对时间要求相对宽松一些的功能,推荐使用setWindow,因为setWindow这个方法更加省电一些。

android API 23之后,系统引入了低电耗模式和应用待机模式,API再次发生变化,这次需要使用:AlarmManager#setAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,PendingIntent operation)AlarmManager#setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis, PendingIntent operation)。这两个方法允许在低电耗模式下启动闹钟。

因此,为了兼容各个版本,代码就应该这样写:
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, ClockAlarmActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("id", id);		//为intent添加需要传递的参数
PendingIntent sender = PendingIntent.getActivity(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
		am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,calMethod(week, calendar.getTimeInMillis()),sender);
	} else {
		am.setExact(AlarmManager.RTC_WAKEUP, calMethod(week, calendar.getTimeInMillis()),sender);
	}
} else {
	if (flag == 0) {	//一次性闹钟
		am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
	} else {			//重复闹钟
		am.setRepeating(AlarmManager.RTC_WAKEUP, calMethod(week, calendar.getTimeInMillis()), intervalMillis, sender);
	}
}

按照上面编码后测试,发现在亮屏时可以正常响铃,锁屏后就没办法了。可以确定休眠状态影响了AlarmManager的正常运行。解决办法就好办了,只要保持CPU一直在运行就好了,只是这样会耗电。如果你有更好的解决办法,欢迎留言指导!

WakeLock保持CPU持续运转

首先参考网上的代码写一个电源锁工具类,使用单例模式。
核心代码如下:

    //获取电源锁,保持该服务在屏幕熄灭时仍然获取CPU时,保持运行
    public void acquireWakeLock() {
        if (null == wakeLock) {
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "myapp:PostLocationService");
            if (null != wakeLock) {
                wakeLock.acquire();
            }
        }
    }

    //释放设备电源锁
    public void releaseWakeLock() {
        if (null != wakeLock) {
            wakeLock.release();
            wakeLock = null;
        }
    }

由于我开启了一个Service来设置闹钟,所以我直接在Service#onCreate方法中获取电源锁了。那么什么时候释放电源锁呢?可以在Service#onDestroy和Service#onTaskRemoved中释放电源锁。

测试,ok,在锁屏时可以成功响铃。可是响铃的时候屏幕没有亮啊???有问题就解决呗。

响铃时唤醒屏幕

唤醒屏幕我还是使用了电源管理器。

	//解锁屏幕并亮屏
    private void wakeUpAndUnlock(Context context) {
        //屏锁管理器
        KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock");
        //解锁
        kl.disableKeyguard();
        //获取电源管理器对象
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是LogCat里用的Tag
        wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP |
                PowerManager.SCREEN_DIM_WAKE_LOCK, "myapp:bright");
        //点亮屏幕
        wl.acquire();
    }

我在响铃时其实是启动了一个activity,因此我将唤醒屏幕的操作放在了Activity#onResume方法中了,并在onPause方法中释放电源管理器。

        //释放
        wl.release();

以上工作完成后测试,能够按时响铃并点亮屏幕。任务结束。


经过反复测试,仅有以上代码还不够,还需要将app加入到白名单中。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值