且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

带有 boot_completed 的运行时异常 Android O

更新时间:2023-01-20 19:15:56

以下是我在 一篇博文:

您的 BroadcastReceiver 接收 ACTION_BOOT_COMPLETED 广播在 Android 上可以调用 startForegroundService() 而不是 startService()8.0+:

Your BroadcastReceiver that receives the ACTION_BOOT_COMPLETED broadcast could call startForegroundService() instead of startService() when on Android 8.0+:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

public class OnBootReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    Intent i=new Intent(context, TestIntentService.class);

    if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) {
      context.startForegroundService(i);
    }
    else {
      context.startService(i);
    }
  }
}

请注意,这在一定程度上有效,即使您的服务实际上没有永远调用 startForeground().您有时间四处走动调用 startForeground(),与执行此操作的 ANR 间隔相当".如果您的工作时间长于一毫秒但少于几秒,您可以跳过 NotificationstartForeground() 调用.然而,你会在 LogCat 中得到一个错误:

Note that this works, to an extent, even if your service does not actually ever call startForeground(). You are given a window of time to get around to calling startForeground(), "comparable to the ANR interval to do this". If your work is longer than a millisecond but less than a few seconds, you could skip the Notification and the startForeground() call. However, you will get an error in LogCat:

E/AndroidRuntime: FATAL EXCEPTION: main
 Process: com.commonsware.myapplication, PID: 5991
 android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1775)
     at android.os.Handler.dispatchMessage(Handler.java:105)
     at android.os.Looper.loop(Looper.java:164)
     at android.app.ActivityThread.main(ActivityThread.java:6541)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

当然,如果你不介意简短的Notification,欢迎你像 Android 期望的那样使用 startForeground(),在这种情况下,您可以正常进行后台工作,尽管在用户的通知中显示了一个条目阴影.

Of course, if you do not mind having a Notification briefly, you are welcome to use startForeground() as Android expects you to, in which case you can do background work normally, albeit with an entry showing up in the user's notification shade.

BroadcastReceiver 从 API 级别 11 开始提供 goAsync().这允许您接收器在主应用程序线程之外工作,这样你就可以摆脱IntentService 并将您的代码移动到 BroadcastReceiver 中.你仍然只有 ANR使用超时期限,但您不会占用主应用程序线.这比第一个解决方法要好,因为它具有相同的时间限制,但避免了令人讨厌的错误.但是,它确实需要一些数量返工.

BroadcastReceiver has offered goAsync() since API Level 11. This allows your receiver to do work off the main application thread, so you could get rid of the IntentService entirely and move your code into the BroadcastReceiver. You still only have the ANR timeout period to work with, but you will not be tying up your main application thread. This is better than the first workaround, insofar as it has the same time limitation but avoids the nasty error. However, it does require some amount of rework.

如果您的工作需要超过几秒钟并且您想避免Notification,你可以修改你的代码来实现一个 JobService 和使用 JobScheduler.这有一个额外的好处,就是只给你控制何时满足其他标准(例如,有可用的互联网联系).然而,这不仅需要重写,而且 JobScheduler仅适用于 Android 5.0+,因此如果您的 minSdkVersion 小于 21,您将需要在旧设备上使用其他解决方案.

If your work will take more than a few seconds and you want to avoid the Notification, you could modify your code to implement a JobService and work with JobScheduler. This has the added advantage of only giving you control when other criteria are met (e.g., there is a usable Internet connection). However, not only does this require a rewrite, but JobScheduler is only available on Android 5.0+, so if your minSdkVersion is less than 21, you will need some other solution on the older devices.

更新:Eugen Pechanec 指出JobIntentService,这是一个有趣的 JobService/IntentService 混搭.

UPDATE: Eugen Pechanec pointed out JobIntentService, which is an interesting JobService/IntentService mashup.