由于新的后台执行限制,我们的应用程序在Android O上崩溃。我们使用的是Firebase版本10.2.1,它是添加Android O支持的版本。
好像是Firebase的一个问题?或者是否需要进行一些改变才能支持这一点?
java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.firebase.INSTANCE_ID_EVENT pkg=my.package.name cmp=my.package.name/my.package.name.MyFcmIdService (has extras) }: app is in background uid UidRecord{30558fa u0a327 RCVR idle procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at android.support.v4.content.WakefulBroadcastReceiver.startWakefulService(WakefulBroadcastReceiver.java:99)
at com.google.firebase.iid.zzg.b(zzg.java:9)
at com.google.firebase.iid.zzg.a(zzg.java:72)
at com.google.firebase.iid.zzg.a(zzg.java:2)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.java:23)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.java:34)
at com.google.firebase.iid.FirebaseInstanceId.<init>(FirebaseInstanceId.java:31)
at com.google.firebase.iid.FirebaseInstanceId.getInstance(FirebaseInstanceId.java:47)
at com.google.firebase.iid.FirebaseInstanceId.a(FirebaseInstanceId.java:4)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.java:19)
at com.google.firebase.iid.FirebaseInstanceIdService.b(FirebaseInstanceIdService.java:35)
at com.google.firebase.iid.zzb$zza$1.run(zzb.java:24)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
更新升级到11.4.2可以解决此问题。
答案 0 :(得分:18)
@KaMyLL是对的。我的应用程序遇到了同样的问题,可以通过用JobIntentService替换IntentService(我们已经在onTokenRefresh()
内启动)来解决它。
因为我发现JobScheduler
和JobIntentService
文档有点令人困惑,所以我想了解一些代码片段。我希望这能让每个人都明白这个问题。
导致此问题的原因是什么?
由于Android 8的新Background Execution Limits,当应用可能处于后台时,您不应再启动后台服务了:
当应用程序位于前台时,它可以自由地创建和运行前台和后台服务。当应用程序进入后台时,它有一个几分钟的窗口,在该窗口中仍然允许创建和使用服务。在该窗口的末尾,该应用程序被视为空闲。此时,系统会停止应用程序的后台服务,就像应用程序已调用服务的Service.stopSelf()方法一样。
还有:
在许多情况下,您的应用可以使用JobScheduler作业替换后台服务。
因此,对于Android 7.x及更低版本,当应用程序处于后台时使用startService()(据我所知)没问题。但在Android 8中,这会导致崩溃。因此,您现在应该使用JobScheduler
。 JobScheduler
和IntentService
之间的行为差异是立即执行IntentService。另一方面,不能保证立即执行排入JobScheduler的作业。 Android操作系统将确定何时有一个好的时间点,以便更节能。所以可能会有延迟。到目前为止,我不知道这需要多长时间。
因此,一种解决方案可能是检查操作系统版本并使用if-else分支您的代码。幸运的是,支持库可以帮助我们以更优雅的方式解决这个问题,而无需复制任何代码:JobIntentService
,这基本上是为您做的。
如何重现此问题?
上面的第一个引用声明应用程序仍然“有一个几分钟的窗口,它仍然允许创建和使用服务。”,所以为了重现和调试问题(以Firebase中的onTokenRefresh()
为例,您可以在使用startService()
开始服务之前设置断点。关闭应用程序并在那里等待5-10分钟。继续执行,您将看到此问题的IllegalStateException
。
能够将问题重现为根本,以确保我们的修复能够真正解决问题。
如何将我的IntenService迁移到JobIntentService?
我以FirebaseInstanceIdService.onTokenRefresh()
为例:
a)将BIND_JOB_SERVICE权限添加到您的服务中:
<service android:name=".fcm.FcmRegistrationJobIntentService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"/>
b)而不是从IntentService
延伸,只需从android.support.v4.app.JobIntentService
延伸,将onHandleIntent(Intent)
方法重命名为onHandleWork(Intent)
,并添加enqueueWork(Context,Intent)方便的功能:
public class FcmRegistrationJobIntentService extends JobIntentService
{
// Unique job ID for this service.
static final int JOB_ID = 42;
// Convenience method for enqueuing work in to this service.
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, FcmRegistrationJobIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// the code from IntentService.onHandleIntent() ...
}
}
c)使用enqueueWork()
便捷功能启动作业:
public class ComfyFirebaseInstanceIdService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
Intent intent = new Intent(this, FcmRegistrationJobIntentService.class);
// startService(intent);
FcmRegistrationJobIntentService.enqueueWork(this, intent);
}
}
我希望这个例子很有帮助。至少在完成这些步骤之后,我无法再在我的Android 8设备上重现该问题,并且它继续使用我的Android 7设备。
<强>更新强>
在FirebaseInstanceIdService
已弃用时,我们应将其从代码中移除,并使用FirebaseMessagingService
中的onNewToken代替。
答案 1 :(得分:2)
我已经对它进行了一些研究,最好的选择是将IntentService转换为app compat库中提供的JobIntentService。它在所有pre-Oreo设备上的行为类似于IntentService。在Android 8及更高版本上,它会将作业排入android system JobScheduler。默认情况下,此作业将截止时间参数设置为0,因此理论上它应该尽可能快地触发。
答案 2 :(得分:0)
截至今天(2018年10月26日),FirebaseInstanceIDServie为deprecated,请尝试使用此方法解决上述问题link