由于后台执行限制,FirebaseMessagingService在Android O上崩溃

cot*_*aws 18 firebase firebase-cloud-messaging

由于新的后台执行限制,我们的应用程序在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)
Run Code Online (Sandbox Code Playgroud)

更新升级到11.4.2可以解决此问题.

bsa*_*ter 22

@KaMyLL是对的.我的应用程序遇到了同样的问题,可以通过用JobIntentService替换IntentService(我们已在其中启动onTokenRefresh())来解决它.

因为我发现JobSchedulerJobIntentService文档有点令人困惑,我想用一些代码片段来解决所有问题.我希望这能使每个人都清楚这个问题.

是什么导致了这个问题?

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

当应用程序位于前台时,它可以自由地创建和运行前台和后台服务.当应用程序进入后台时,它有一个几分钟的窗口,在该窗口中仍然允许创建和使用服务.在该窗口的末尾,该应用程序被视为空闲.此时,系统会停止应用程序的后台服务,就像应用程序已调用服务的Service.stopSelf()方法一样.

并且:

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

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

如何重现这个问题?

上面的第一个引用声明该应用程序仍然"有一个几分钟的窗口,它仍然允许创建和使用服务." ,因此,为了重现和调试问题(使用onTokenRefresh()Firebase中的示例),您可以在启动服务之前设置断点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"/>
Run Code Online (Sandbox Code Playgroud)

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() ...
    }
}
Run Code Online (Sandbox Code Playgroud)

c)enqueueWork()使用方便的功能启动作业:

public class ComfyFirebaseInstanceIdService extends FirebaseInstanceIdService {
    @Override
    public void onTokenRefresh() {
        Intent intent = new Intent(this, FcmRegistrationJobIntentService.class);
        // startService(intent);
        FcmRegistrationJobIntentService.enqueueWork(this, intent);
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这个例子很有帮助.至少在执行了这些步骤之后,我无法再在我的Android 8设备上重现该问题,并且它继续使用我的Android 7设备.

更新

因为FirebaseInstanceIdService过时,我们应该从代码中删除这一点,并使用onNewTokenFirebaseMessagingService代替.


KaM*_*yLL 2

我已经对此进行了一些研究,最好的选择是将 IntentService 转换为应用程序兼容库中可用的JobIntentService。它的行为类似于所有 Oreo 之前的设备上的 IntentService。在 Android 8 及更高版本上,它将把作业排入Android 系统 JobScheduler。该作业默认将截止时间参数设置为 0,因此理论上它应该尽可能快地触发。

  • 也许我在这里遗漏了一些东西,但我看到的问题是您的服务必须从“FirebaseMessagingService”扩展,因此您无法更改其功能。Android 系统是控制调用此服务的系统,因此我们无法控制确保它在 Android 8 上正确调用 (4认同)