前言

前几天接触的一个闹钟APP项目,闹钟触发从后台启动一个activity,执行之后的响铃操作,但是却失效了,闹钟并没有按时响铃。最后发现是系统拦截了从后台启动的Activity。

具体原因

  • 一、AndroidQ从后台启动Activity的限制
    Android 10 (API 级别 29) 及更高版本对后台应用可启动 Activity进行限制。Android10中, 当App的Activity不在前台时,其启动Activity会被系统拦截,导致无法启动。
  • 二、后台弹出界面、锁屏显示权限(小米等部分国产手机)
    小米手机有个特殊的权限,“后台弹出界面(允许应用在后台弹出界面)”,默认是拒绝的,如下图所示.
  • Android activity intent后 不finish activity状态 activity无法启动_移动开发

  • 华为手机没有这个权限,目前vivo/oppo/小米手机有这个“后台弹出界面”权限.
  • 其他原因,有待更新,暂时没有遇见

解决方案

  • 一、针对Android10的问题
    方案1
    官方给予的折中方案是使用全屏Intent(full-screen intent), 既创建通知栏通知时, 加入full-screen intent 设置。示例代码如下(基于官方文档修改):
Intent fullScreenIntent = new Intent(this, CallActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
        fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
NotificationCompat.Builder notificationBuilder =
        new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.notification_icon)
    .setContentTitle("Incoming call")
    .setContentText("(919) 555-1234")
    //以下为关键的3行
    .setPriority(NotificationCompat.PRIORITY_HIGH)
    .setCategory(NotificationCompat.CATEGORY_CALL)
    .setFullScreenIntent(fullScreenPendingIntent, true);
    
NotificationManager notifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notifyManager.notify(notifyId, builder.build());

注意:在Target SDk为29及以上时,需要在AndroidManifest上增加USE_FULL_SCREEN_INTENT申明

//AndroidManifest中
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />

方案2
向用户申请 SYSTEM_ALERT_WINDOW权限,系统就不会拦截该程序后台启动的Activity。
示例代码如下:

//AndroidManifest中
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
//检查是否已经授予权限
if (!Settings.canDrawOverlays(this)) {
    //若未授权则请求权限
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, 0);
}

其中,Settings.canDrawOverlays(this)方法是在API level 23也就是Android M中新加入的用于检查当前是否拥有出现在“出现在其他应用上”权限的方法。在6.0以前的系统版本,悬浮窗权限是默认开启的,直接使用即可。

  • 二、针对后台弹出界面锁屏显示权限的问题

应用检查后台启动权限的方法如下:

public static boolean canBackgroundStart(Context context) {
        AppOpsManager ops = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        try {
            int op = 10021; // >= 23
            // ops.checkOpNoThrow(op, uid, packageName)
            Method method = ops.getClass().getMethod("checkOpNoThrow", new Class[]
                    {int.class, int.class, String.class}
            );
            Integer result = (Integer) method.invoke(ops, op, Process.myUid(), context.getPackageName());
            return result == AppOpsManager.MODE_ALLOWED;
        } catch (Exception e) {
            Log.e(TAG, "not support", e);
        }
        return false;
    }

该方法是申请“后台弹出页面”权限白名单时小米官方给出的检查后台启动权限的方法。或跳转到权限设置页面,请求用户打开相应权限。
欢迎阅读另一篇,跳转手机权限管理页面