由于Target31对前台服务的限制,现在尝试用精准闹钟来启动前台服务,进而避免ForegroundServiceStartNotAllowedException
异常。但是项目在实际运行中却发现了另外的问题。
调用的代码片段如下:
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || checkSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM) == PackageManager.PERMISSION_GRANTED) {
PendingIntent alarmIntent;
Intent intent = new Intent(context, TestFGService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
alarmIntent = PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
} else {
alarmIntent = PendingIntent.getService(context, 0, intent, 0);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC, System.currentTimeMillis() + duration, alarmIntent);
} else {
alarmMgr.setExact(AlarmManager.RTC, System.currentTimeMillis() + duration, alarmIntent);
}
}
这里已经检查了SCHEDULE_EXACT_ALARM
权限,但是线上还是统计到了少数的crash,其中有下面这段日志:
Unable to create application com.skyline.fgserviceapplication.AppContext: java.lang.SecurityException: Caller com.skyline.fgserviceapplication needs to hold android.permission.SCHEDULE_EXACT_ALARM to set exact alarms.
我又检查了一遍代码,我确实给了权限,并且在设置闹钟前检查是否得到权限。但当我在应用设置里把对应app的闹钟与提醒功能关闭时,我发现checkSelfPermission并没有按照预期返回PERMISSION_DENIED
,而是返回PERMISSION_GRANTED
。后来才知道闹钟与提醒功能那里关闭时并没有拒绝精准闹钟权限,应对这种情况需要使用AlarmManager
的canScheduleExactAlarms
方法来进一步检查是否可以启动闹钟。示例代码如下:
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || alarmMgr.canScheduleExactAlarms()) {
PendingIntent alarmIntent;
Intent intent = new Intent(context, TestFGService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
alarmIntent = PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
} else {
alarmIntent = PendingIntent.getService(context, 0, intent, 0);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC, System.currentTimeMillis() + duration, alarmIntent);
} else {
alarmMgr.setExact(AlarmManager.RTC, System.currentTimeMillis() + duration, alarmIntent);
}
}