在我们的应用程序上,通常在Application.OnCreate
期间启动服务(直接调用context.startService
),随后又通过AlarmManager
启动(正在进行重构以将其某些工作迁移到JobScheduler)。
我们的应用程序还具有直接出于启动目的的BroadcastReceiver
。
鉴于Android Oreo(https://developer.android.com/about/versions/oreo/android-8.0-changes.html)的新限制,我们遇到了如下问题:
这会导致崩溃,并显示“ IllegalStateException:不允许启动服务意图”。
我知道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正在做的确切检查(我在https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java方法上到达了startServiceCommon
的位置,但是从那里requireForeground
标志似乎去了一些本机实现)
所以我的问题:
出于Android Oreo新限制的特定目的,如何在调用startService
之前检查进程是否为前台?
继续调查:(TL; DR:在底部的水平线之间看到]
免责声明,我对Android不太了解,我只喜欢挖掘源代码。注意:如果您跳到文件而不是类,也可以在Android Studio中导航代码:或在项目和图书馆]中搜索文本。
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
(上帝保佑Google编制索引)。
注意,我也发现了这一点,如果您真的很感兴趣内部的工作原理,这可能是一个有趣的阅读。https://programmer.group/android-9.0-source-app-startup-process.html
[ActivityManagerService
sources显示位于ActivityManagerService
中的in Oreo startService
is forwarded to ActiveServices
。
假设我们正在寻找这样的异常:
java.lang.IllegalStateException:不允许启动服务意图{...}:应用程序在后台uid UidRecord {af72e61 u0a229 CAC bg:+ 3m52s273ms空闲过程:1 seq(0,0,0)}] >>
我们必须继续沿着兔子洞前进:
startService
。允许的条件取决于ActiveServices
返回的启动模式。有4种启动模式:
requireForeground
gets assigned to fgRequired
parameter and the message is here)requireForeground
将立即返回fgRequired
,但是假设这是一个正常的应用程序,我们将以ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false)
结尾。注意:这是确定ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false)
中提到的一些白名单的位置。由于除了最后一个分支以外的所有分支都返回!=
,因此它将重定向到return null
,在此我们找到我们的Ephemeral apps:
APP_START_MODE_DISABLED
所以拒绝的原因只是针对O。我认为关于OS如何确定您的应用是前台还是后台的问题的最终答案是
appServicesRestrictedInBackgroundLocked
appServicesRestrictedInBackgroundLocked
我的猜测是,缺少记录意味着它没有在运行,而https://stackoverflow.com/a/46445436/253468意味着它在后台。请注意,在我的异常消息中,
APP_START_MODE_NORMAL
表示它处于空闲状态,并且已后台运行3m52s。我偷看了您的
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; }
。
不确定是否有帮助,但是我还是会张贴它,因此,如果有人想进行更多调查,他们会获得更多信息。