在我们的应用程序中,有一个服务通常在 Application.OnCreate
期间启动。 (直接调用 context.startService
)以及稍后通过 AlarmManager
(正在进行重构以将其部分工作迁移到 JobScheduler)。
我们的应用程序还有一个 BroadcastReceiver
它是以其直接 Intent 启动的。
鉴于 Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) 中的新限制,我们遇到了如下问题:
这会导致“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 中导航代码:
或在 Project 中搜索文本和图书馆 .
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种启动模式:
!=
)return null
)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/