android - 如何检测Android O的进程前台

标签 android android-broadcastreceiver android-8.0-oreo

在我们的应用程序中,有一个服务通常在 Application.OnCreate 期间启动。 (直接调用 context.startService )以及稍后通过 AlarmManager (正在进行重构以将其部分工作迁移到 JobScheduler)。

我们的应用程序还有一个 BroadcastReceiver它是以其直接 Intent 启动的。

鉴于 Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) 中的新限制,我们遇到了如下问题:

  • 应用程序/进程处于后台/死机
  • BroadcastReceiver 被操作系统触发
  • Application.onCreate() 在 BroadcastReceiver
  • 之前执行
  • Application.onCreate() 代码尝试运行服务

  • 这会导致“IllegalStateException:不允许启动服务 Intent ”崩溃。

    我知道 CommonsWare 在此处 https://stackoverflow.com/a/44505719/906362 回答的启动服务的新推荐方法,但对于这种特定情况,我只想拥有 if(process in foreground) { startService } .我目前正在使用以下方法,它似乎有效:
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        private static boolean isProcessInForeground_V21(Context context) {
            ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
            List<ActivityManager.AppTask> tasks = am.getAppTasks();
            return tasks.size() > 0;
        }
    

    但我找不到 Android Oreo 正在做的确切检查(我在 startServiceCommon 方法上得到了 https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java,但从那里 requireForeground 标志似乎转到了一些 native 实现)

    所以我的问题:

    针对Android Oreo新限制的特定目的,调用startService之前如何检查我的进程是否是前台?

    最佳答案

    继续调查:(TL;DR:见底部水平线之间)

    免责声明,我对Android不太了解,我只是喜欢挖掘源代码。
    注意:如果你跳转到文件而不是类,你也可以在 Android Studio 中导航代码:
    enter image description here
    或在 Project 中搜索文本和图书馆 .
    enter image description here
    IActivityManager由 AIDL 定义,这就是为什么没有它的来源:
    https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145

    基于 how AIDL needs to be implemented我发现 ActivityManagerService extends IActivityManager.Stub (上帝保佑谷歌索引)。

    请注意,我还发现了这一点,如果您真的对内部工作方式感兴趣,这可能是一本有趣的读物。
    https://programmer.group/android-9.0-source-app-startup-process.html

    ActivityManagerService sources揭示 in Oreo startService is forwarded to ActiveServices 位于same package .

    假设我们正在寻找这样的异常:

    java.lang.IllegalStateException: Not allowed to start service Intent {...}: app is in background uid UidRecord{af72e61 u0a229 CAC bg:+3m52s273ms idle procs:1 seq(0,0,0)}



    我们必须继续往下走: requireForeground gets assigned to fgRequired parameter and the message is here .允许这样做的条件取决于 ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false) 返回的启动模式。 .

    有4种启动模式:
  • APP_START_MODE_NORMAL(需要与此不同,即 != )
  • APP_START_MODE_DELAYED(没关系,即return null)
  • APP_START_MODE_DELAYED_RIGID
  • APP_START_MODE_DISABLED

  • Ephemeral apps将立即返回 APP_START_MODE_DISABLED ,但假设这是一个普通的应用程序,我们最终会在 appServicesRestrictedInBackgroundLocked .
    注意:这是https://stackoverflow.com/a/46445436/253468 中提到的一些白名单的地方。决定了。
    由于所有分支,但最后一个返回 APP_START_MODE_NORMAL ,这将重定向到 appRestrictedInBackgroundLocked我们在哪里找到我们的most likely suspect :
        int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
            // Apps that target O+ are always subject to background check
            if (packageTargetSdk >= Build.VERSION_CODES.O) {
                return ActivityManager.APP_START_MODE_DELAYED_RIGID;
            }
    

    所以拒绝的原因只是针对O。我认为操作系统如何决定您的应用程序是前台还是后台的问题的最终答案是this condition in getAppStartModeLocked
    UidRecord uidRec = mActiveUids.get(uid);
    if (uidRec == null || alwaysRestrict || uidRec.idle) {
    

    我的猜测是缺少记录意味着它没有运行(但是它是如何启动服务的?!),和idle意味着它是有背景的。请注意,在我的异常消息中,UidRecord是说它是空闲的,并且已经后台处理了 3 分 52 秒。

    我偷看了你的getAppTasks它基于 TaskRecord.effectiveUid ,所以我猜这很接近上市 UidRecord s 为您的应用程序。

    不确定这是否有帮助,但无论如何我都会发布它,所以如果有人想进行更多调查,他们有更多信息。

    关于android - 如何检测Android O的进程前台,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45915959/

    相关文章:

    java - 用户通过Android中的任何第三方应用程序(CC Cleaner或Kill应用程序)清理应用程序后是否可以重新启动后台服务?

    Android O - FLAG_SHOW_WHEN_LOCKED 已弃用

    android - ClassNotFound : android. view.ViewStructure with Support Library 26.0.2 - 27.0.0

    android - Forms.Context 已过时,那么我应该如何获取单个 Activity 应用程序的 Activity ?

    android - 私有(private)广播发送者和接收者权限

    android - Workmanager FCM 依赖

    android - 是否可以从对话框中调用 onReceive 方法?

    android - Google Analytics 服务 Intent 在 Android Oreo 上崩溃

    android - fragment 选项卡中的框架布局

    java - 如何让支持的Toolbar背景透明?