FirebaseMessagingService 在 Android O 上因后台执行限制而崩溃

19

我们的应用在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可解决此问题。


你在这个问题上得到了任何解决方案吗? - VVB
我没有...... - cottonBallPaws
请查看上面的更新,他们最新的版本可能已经解决了这个问题。 - cottonBallPaws
我也在11.8.0中看到了这个问题。 - JPM
在17.6.0上同样适用。 - MRX
3个回答

24

@KaMyLL是正确的。我们的应用程序也遇到了同样的问题,并通过将我们在onTokenRefresh()内启动的IntentService替换为JobIntentService来解决。

由于我发现JobSchedulerJobIntentService文档有点令人困惑,因此我想用一些代码片段来总结一下。我希望这能使所有遇到此问题的人都清楚明白。

是什么导致了这个问题?

由于Android 8的新后台执行限制,当应用程序可能处于后台时,您不应再启动后台服务:

当一个应用程序在前台时,它可以自由地创建和运行前台和后台服务。当一个应用程序进入后台时,它有几分钟的时间窗口,在这段时间内它仍被允许创建和使用服务。在那个窗口结束时,该应用被认为是空闲的。此时,系统停止应用程序的后台服务,就像应用程序调用了服务的Service.stopSelf()方法一样。

还有:

在许多情况下,您的应用程序可以用JobScheduler作业替换后台服务。

因此,在Android 7.x及以下版本中,当应用程序处于后台时使用startService()(就我所知)没有问题。但在Android 8中,这会导致崩溃。因此,现在应该使用JobSchedulerJobSchedulerIntentService之间的行为差异是,IntentService会立即执行。另一方面,排队到JobScheduler的作业不能保证立即执行。为了更节能效率,Android OS将确定何时执行。因此可能会有延迟。到目前为止,我不知道这需要多长时间。因此,一种解决方案是检查操作系统版本并使用if-else分支代码。幸运的是,支持库帮助我们以更优雅的方式解决这个问题,而不重复任何代码:JobIntentService,它在幕后基本上为您完成此操作。

如何重现这个问题?

上述第一个引用说明应用程序仍然有“几分钟的时间窗口,在这段时间内它仍被允许创建和使用服务。”,因此为了重现和调试问题(以Firebase的onTokenRefresh()示例为例),可以在使用startService()启动服务之前设置断点。关闭应用程序并等待5-10分钟。继续执行,您将看到来自此问题的IllegalStateException。能够重现这个问题是确保我们的修复程序真正解决问题的基础。

如何将我的IntentService迁移到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 已被弃用,我们应该将其从代码中删除,并改用FirebaseMessagingServiceonNewToken


我们需要执行ComfyFirebaseInstanceIdService服务,但我在你的代码中没有看到它。 - Neri
这个解决方案非常有效。我只需要等待1分钟就能够准确地复现问题,做得好。 - Sca09

2
我已经对此进行了一些研究,最好的选择是将IntentService转换为app compat库中可用的JobIntentService。它在所有Android Oreo之前的设备上都像IntentService一样运行。在Android 8及以上版本中,它将作业排队到android系统JobScheduler。默认情况下,该作业的截止时间参数设置为0,因此理论上应尽快触发。

4
也许我漏掉了什么,但我看到的问题是你的服务必须从FirebaseMessagingService继承,所以你无法改变它的运作方式。Android系统控制着调用这个服务,我们无法控制确保它在Android 8上被正确调用。 - cottonBallPaws
你是对的,在我的情况下,当我尝试在onTokenRefresh()方法内启动自定义IntentService时,遇到了类似的问题。 - KaMyLL

0

截至今天(2018年10月26日),FirebaseInstanceIDServie已经过时,请尝试使用此链接来解决以上问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接