为了鼓励应用节省系统资源,以 Android 12 及更高版本为目标平台且设置了精确的闹钟的应用必须能够访问“闹钟和提醒”功能,该功能显示在系统设置的特殊应用访问权限屏幕中。如需获取这种特殊应用访问权限,需在清单中请求 SCHEDULE_EXACT_ALARM 权限。
开发者网站请参考:
行为变更:以 Android 12 为目标平台的应用 | Android Developers
简单来说就是代码中如果使用了setAlarmClock()、setExact()、setExactAndAllowWhileIdle()这几种方法,则设置的是精准的闹钟。在Android12环境下,调用这些方法时,如果SCHEDULE_EXACT_ALARM权限未打开,应用就会crash(做之前先测试一下,要是权限关闭以后,调用这些方法,要是应用不会crash,应该不需要对应)。所以,在调用这些方法时,要增加权限检查。
应对方法:
(1).申请权限
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
(2).跳转到授权画面
Google Pixel的处理流程:在一打开应用的时候就进行权限检查,如果check到SCHEDULE_EXACT_ALARM权限没打开,就使用toast提示用户打开授权界面,若用户不打开设置的闹钟都不好用(我的建议是弹dialog,要是用户拒绝就直接退出应用,省得后面麻烦)
Uri uri = Uri.parse("package:"+this.getPackageName());
Intent i = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,uri);
startActivity(i,200);
SCHEDULE_EXACT_ALARM权限 授权画面
(3).判断是否具有权限
alarmManager=(AlarmManager)getSystemService(Context.ALARM_SERVICE);
boolean hasPermission = alarmManager.canScheduleExactAlarms();//true:有权限,false:没有权限
if(hasPermission){
setAlarmClock();//设置精准闹钟的方法
setExact();
setExactAndAllowWhileIdle();
}else{
//如果没检查到权限,应该做出相应的处理
//(1).什么都不做,写一条log。如果权限关闭闹钟将无法使用
//(2).设置不精准的闹钟
//(3).使用dialog、toast等提示用户打开权限(具体项目,具体设计,不建议在每次设置精准闹钟时候都对用户进行提示,这样的用户体验感不好)
}
注意点:
1.如果用户在SCHEDULE_EXACT_ALARM权限打开的时候设置闹钟,在闹钟到点前将闹钟关闭,到时间闹钟是不会响的,如果用户发现闹钟到点没响,就将SCHEDULE_EXACT_ALARM权限打开,闹钟可以响(闹钟过时,闹钟会被miss掉)
对应方法:设定一个广播接收器(静态,动态都可以),接收"AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED"
这里要注意:只有在打开精准闹钟权限的时候才能收到广播;关闭时是收不到的,只能每次使用方法的时候checkPermission。
如果收不到广播的话,在终端运行以下命令,启用行为更:(模拟器也要运行)
adb shell am compat enable REQUIRE_EXACT_ALARM_PERMISSION [packageName]
正常来说,SCHEDULE_EXACT_ALARM权限是默认打开的,用户应该也不会找到这个将它关闭,更不太可能进行这么非人类的操作,但是我们作为开发者应该考虑滴全面些。我就打算等Android 12适配了,我就给各个手机厂商找茬去。Google Pixel已经被我找过一次茬了,我这里面说的情况,Google就还没做出处理(11月为止)。
2.App info和System Setting都有SCHEDULE_EXACT_ALARM 权限,它们俩的授权界面虽然长得一样,但是是不一样的界面。这将会导致一个问题,要是在App info中将权限关闭,在跳转授权界面的时候可能会跳转两个授权界面。
对应方法:在点击跳转按钮的时候再startActivity,这样就能把App info的授权界面(AlarmsAndRemindersAppActivity)给覆盖掉
public void createAlertDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.extra_alarm_permission_descript_title);
builder.setPositiveButton(R.string.extra_alarm_permission_turn_on, new DialogInterface.OnClickListener() {
Uri uri = Uri.parse("package:" + "com.android.deskclock");
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM, uri);
public void onClick(DialogInterface dialog, int which) {
startActivity(intent);
}
});
builder.setNegativeButton(R.string.extra_alarm_permission_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
mExtraAlarmDialog = builder.create();
mExtraAlarmDialog.setCancelable(false);
mExtraAlarmDialog.show();
}