相关文章推荐
唠叨的碗  ·  【 云原生 kubernetes 】- ...·  1 月前    · 
强健的苹果  ·  使用 ASP.NET Core 创建 ...·  1 年前    · 

Context.startForegroundService()没有随后调用Service.startForeground()。

372 人关注

我正在使用【替换代码0安卓O操作系统上的课。

我计划在背景中使用 Service

The 安卓文档 指出,

如果你的应用程序的目标是API级别26或更高,系统对使用或创建后台服务施加了限制,除非应用程序本身处于前台。如果一个应用程序需要创建一个前台服务,该应用程序应该调用 startForegroundService()

If you use startForegroundService() , the Service throws the following error.

Context.startForegroundService() did not then call
Service.startForeground() 

这有什么问题吗?

5 个评论
IOW,请提供一个 最低限度的可重复的例子 .这将包括整个Java堆栈跟踪和触发崩溃的代码。
Alex
这个错误在API 26和27(27.0.3)中仍然存在。你可以通过在onCreate()和onStartCommand()中加入startForeground()来减少崩溃的次数,但对于一些用户来说,崩溃仍然会发生。目前唯一能解决这个问题的方法是在你的build.gradle中加入targetSdkV版本25。
我也有同样的问题。我解决了这个问题。我在这个话题中分享了我的实现方法 stackoverflow.com/questions/55894636/...
android
service
operating-system
foreground
android-8.0-oreo
NiceGuy
NiceGuy
发布于 2017-06-08
30 个回答
zhuzhumouse
zhuzhumouse
发布于 2020-08-18
已采纳
0 人赞同

From Google's docs on 安卓8.0的行为变化 :

系统允许应用程序调用Context.startForegroundService(),即使应用程序处于后台。然而,应用程序必须在服务创建后五秒内调用该服务的startForeground()方法。

Solution: Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()

另见。 背景执行的限制 适用于Android 8.0 (Oreo)

我在 onStartCommand 方法中这样做了,但我仍然得到这个错误。我在我的 MainActivity 中调用了 startForegroundService(intent) 。也许服务启动得太慢了。我认为在他们能够承诺服务立即启动之前,五秒的限制不应该存在。
5秒的时间肯定是不够的,这种异常在调试阶段经常发生。我怀疑它在发布模式下也会时常发生。作为一个致命的异常,它只会让应用程序崩溃!我试着用Thread.setDefaultUncaught来捕捉它。我试着用Thread.setDefaultUncaughtExceptionHandler()来捕捉它,但即使把它放在那里并忽略掉,安卓也会冻结应用程序。这是因为这个异常是由handleMessage()触发的,而主循环实际上已经结束了......正在寻找解决方法。
@southerton 我认为你应该在 onStartCommand() 上调用它,而不是 onCreate ,因为如果你关闭服务并再次启动它,它可能会转到 onStartCommand() 而不调用 onCreate ...
我们可以在这里查看谷歌团队的回应 issuetracker.google.com/issues/76112072#comment56
Duna
我有一个应用程序,在Play Store上有3万个活跃用户。我有同样的问题:由于启动前台的时间限制为5秒,在安卓8+系统上每天有30次崩溃。崩溃来自各种设备,甚至是今年发布的手机(S20Ultra,摩托罗拉......),安卓的设计不能保证呼叫在5秒内被触发,服务被操作系统杀死。谷歌有一长串关于这个问题的反馈,但他们不承认这是设计缺陷。在强迫我们放弃后台服务之前,他们应该重新设计它。
humazed
humazed
发布于 2020-08-18
0 人赞同

我打电话给 ContextCompat.startForegroundService(this, intent) 来启动服务,然后

In service onCreate

 @Override
 public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();
            startForeground(1, notification);
    
我也是。但我还是偶尔会遇到这个错误。也许安卓系统不能保证它在5秒内调用 onCreate 。所以他们应该在强迫我们遵守规则之前重新设计它。
我在onStartCommand()中调用startForeground,偶尔会出现这个错误。我把它移到了onCreate,从那以后我就再也没有看到过它(祈祷)。
这在奥利奥创建了一个本地通知,说 "APP_NAME正在运行。点击关闭或查看信息"。如何停止显示该通知?
不,安卓团队声称这是一种预期行为。所以我刚刚重新设计了我的应用程序。这很荒唐。由于这些 "预期行为",总是需要重新设计应用程序。这已经是第三次了。
这个问题的主要原因是服务在被提升到前台之前就已经停止了。但服务被销毁后,断言并没有停止。你可以尝试在调用 startForegroundService 后添加 StopService 来重现这个问题。
0 人赞同

为什么会出现这个问题,因为Android框架不能保证你的服务在5秒内启动,但另一方面,框架对前台通知必须在5秒内启动有严格的限制,而没有检查框架是否尝试过启动服务。

这绝对是一个框架问题,但并不是所有面临这个问题的开发者都能做到最好。

  • 替换代码0】的通知必须同时出现在 onCreate onStartCommand 中,因为如果你的服务已经被创建,并且你的活动试图再次启动它, onCreate 将不会被调用。

  • 通知ID不能是0,否则会发生同样的崩溃,即使不是同样的原因。

  • 替换代码4】不得在 startForeground 之前调用。

  • 有了以上3点,这个问题可以减少一些,但仍然不能解决,真正的解决方法或者说变通方法是将你的目标SDK版本降级到25。

    并注意到,很可能Android P仍然会带有这个问题,因为谷歌甚至拒绝了解发生了什么,不相信这是他们的错,请阅读 #36 #56 了解更多信息

    为什么同时在onCreate和onStartCommand?你能不能只把它放在onStartCommand里?
    Lxu
    我执行了一些测试,如果服务已经创建,即使onCreate没有被调用,你也不需要再次调用startForeground。因此,你可以只在onCreate方法中调用它,而不是在onStartCommand中。可能他们认为服务的单个实例在第一次调用后就处于前台,直到结束。
    mr5
    我使用0作为 startForeground 的初始调用的通知ID。把它改成1对我来说解决了问题(希望如此)
    【替换代码0- 如果服务在之前启动,那么这个服务就已经在前台了,因为它在 onCreate 中成为前台,所以这句话没有任何意义......
    我浪费了很多时间来弄清楚它为什么会崩溃,却不明白原因。问题在于 startForeground() 的通知ID。把它改成0以外的东西。
    Oleg Gryb
    Oleg Gryb
    发布于 2020-08-18
    0 人赞同

    我知道,已经有太多的答案了,然而事实是--startForegroundService不能在应用层面上被修复,你应该停止使用它。谷歌建议在Context#startForegroundService()被调用后5秒内使用Service#startForeground()API,这不是一个应用程序可以一直做到的。

    安卓系统同时运行很多进程,不能保证Looper会在5秒内调用你的目标服务,即调用startForeground()。如果你的目标服务没有在5秒内收到调用,你就不走运了,你的用户会遇到ANR的情况。在你的堆栈跟踪中,你会看到类似这样的情况。

    Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}
    main" prio=5 tid=1 Native
      | group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
      | sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
      | state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
      | stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
      | held mutexes=
      #00  pc 00000000000712e0  /system/lib64/libc.so (__epoll_pwait+8)
      #01  pc 00000000000141c0  /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
      #02  pc 000000000001408c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
      #03  pc 000000000012c0d4  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
      at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
      at android.os.MessageQueue.next (MessageQueue.java:326)
      at android.os.Looper.loop (Looper.java:181)
      at android.app.ActivityThread.main (ActivityThread.java:6981)
      at java.lang.reflect.Method.invoke (Method.java)
      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
    

    据我所知,Looper已经分析了这里的队列,发现了一个 "滥用者",并直接将其杀死。现在系统是快乐和健康的,而开发者和用户则不是,但由于谷歌限制了他们对系统的责任,他们为什么要关心后两者?显然,他们不知道。他们可以让它变得更好吗?当然,例如,他们可以提供 "应用程序正在忙 "的对话框,要求用户做出决定,是等待还是关闭应用程序,但为什么要这样做,这不是他们的责任。最重要的是,这个系统现在是健康的。

    根据我的观察,这种情况发生得相对较少,在我的案例中,1000个用户一个月大约有一次崩溃。复制它是不可能的,而且即使它被复制了,你也无法永久地修复它。

    在这个主题中,有一个很好的建议,即使用 "bind "而不是 "start",然后当服务准备好时,处理onServiceConnected,但同样,这意味着根本不使用startForegroundService调用。

    我认为,谷歌方面正确和诚实的行动是告诉大家,startForegourndServcie有缺陷,不应该被使用。

    问题仍然存在:用什么来代替?幸运的是,现在有JobScheduler和JobService,它们是前台服务的一个更好的选择。这是一个更好的选择,因为如此。

    当一个作业正在运行时,系统会代表你的应用程序持有一个wakelock。 应用程序。由于这个原因,你不需要采取任何行动来保证 保证设备在作业期间保持清醒状态。

    这意味着你不需要再关心处理wakelocks的问题,这就是为什么它与前台服务没有区别。从实现的角度来看,JobScheduler不是你的服务,它是一个系统的服务,想必它将正确处理队列,而且谷歌永远不会终止它自己的孩子:)

    三星在他们的三星附件协议(SAP)中从startForegroundService切换到JobScheduler和JobService。当智能手表等设备需要与手机等主机对话时,这是非常有帮助的,其中作业确实需要通过应用程序的主线程与用户互动。由于作业是由调度员发布到主线程上的,所以它成为可能。但你应该记住,作业是在主线程上运行的,并将所有繁重的东西卸给其他线程和异步任务。

    该服务在运行在你的应用程序主线程上的处理程序上执行每个传入的作业。 应用程序的主线程上运行的处理程序上执行每个传入的作业。这意味着,你必须将你的 执行逻辑到你选择的另一个线程/处理程序/AsyncTask上

    转换到JobScheduler/JobService的唯一隐患是你需要重构旧的代码,这并不好玩。在过去的两天里,我一直在做这个工作,以使用新的三星的SAP实现。我会注意我的崩溃报告,如果再看到崩溃,就会告诉你。理论上它不应该发生,但总有一些细节是我们可能不知道的。

    不再有Play Store报告的崩溃了。这意味着JobScheduler/JobService没有这样的问题,并且切换到这种模式是永远摆脱startForegroundService问题的正确方法。我希望,谷歌/安卓能读到它,并最终为大家提供评论/建议/提供官方指导。

    对于那些使用SAP的人来说并询问SAP V2如何利用JobService的解释如下。

    在你的自定义代码中,你需要初始化SAP(它是Kotlin)。

    SAAgentV2.requestAgent(App.app?.applicationContext, 
       MessageJobs::class.java!!.getName(), mAgentCallback)
    

    现在你需要反编译三星的代码,看看里面发生了什么。在SAAgentV2中,看一下requestAgent的实现和下面一行。

    SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
    where d defined as below
    private SAAdapter d;
    

    现在转到SAAdapter类,找到onServiceConnectionRequested函数,该函数使用以下调用安排工作。

    SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12); 
    

    SAJobService只是Android的JobService的一个实现,它是做工作调度的一个。

    private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
        ComponentName var7 = new ComponentName(var0, SAJobService.class);
        Builder var10;
        (var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
        PersistableBundle var8;
        (var8 = new PersistableBundle()).putString("action", var1);
        var8.putString("agentImplclass", var2);
        var8.putLong("transactionId", var3);
        var8.putString("agentId", var5);
        if (var6 == null) {
            var8.putStringArray("peerAgent", (String[])null);
        } else {
            List var9;
            String[] var11 = new String[(var9 = var6.d()).size()];
            var11 = (String[])var9.toArray(var11);
            var8.putStringArray("peerAgent", var11);
        var10.setExtras(var8);
        ((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
    

    正如你所看到的,这里的最后一行使用Android的JobScheduler来获得这个系统服务并安排一个工作。

    在 requestAgent 调用中,我们传递了 mAgentCallback,这是一个回调函数,当一个重要事件发生时,它将接受控制。这就是我的应用程序中回调的定义方式。

    private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
        override fun onAgentAvailable(agent: SAAgentV2) {
            mMessageService = agent as? MessageJobs
            App.d(Accounts.TAG, "Agent " + agent)
        override fun onError(errorCode: Int, message: String) {
            App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
    

    MessageJobs是我实现的一个类,用于处理来自三星智能手表的所有请求。这不是完整的代码,只是一个骨架。

    class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {
        public fun release () {
        override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
            super.onServiceConnectionResponse(p0, p1, p2)
            App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)
        override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
            super.onAuthenticationResponse(p0, p1, p2)
            App.d(TAG, "Auth " + p1.toString())
        override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {
        override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
        override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
            super.onError(peerAgent, errorMessage, errorCode)
        override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {
    

    正如你所看到的,MessageJobs也需要MessageSocket类,你需要实现它来处理来自设备的所有消息。

    一句话,这不是那么简单,它需要对内部和编码进行一些挖掘,但它可以工作,最重要的是--它不会崩溃。

    很好的回答,但有一个主要问题, JobIntentService 在Oreo以下立即作为 IntentService 运行,但在Oreo以上安排一个Job,所以 JobIntentService 不会立即启动。 More info
    你是对的 前台服务可能不会在时间限制的5秒内启动(而谷歌是应该为此负责的),但 JobService 是针对像15分钟间隔的东西,我给你的帖子投了票,因为这个方法很好,但没有用。
    @CopsOnRoad 它非常有用,在我的情况下,它立即开始。正如我写的,它用于手机和智能手表之间的实时互动。不可能,用户会等待15分钟来发送这两者之间的数据。工作非常好,从不崩溃。
    也许在时间允许的情况下我会这样做。我需要将它与定制的特定代码脱钩,并解压缩一些三星专有的库。
    因此,把 JobService 称为好的替换是错误的,因为它不可能一直运行,只有粘性的前台服务可以做到这一点。
    swooby
    swooby
    发布于 2020-08-18
    0 人赞同

    如果你调用 Context.startForegroundService(...) ,然后在 Service.startForeground(...) 被调用之前调用 Context.stopService(...) ,你的应用程序就会崩溃。

    我这里有一个清晰的再现 前台服务API26

    我在......开设了一个关于这个问题的错误。 谷歌问题追踪器

    这方面的几个bug已经被打开和关闭 不会修复。

    希望我的带有清晰的重现步骤的作品能够入选。

    谷歌团队提供的信息

    谷歌问题追踪器 Comment 36

    这不是一个框架错误;这是故意的。 如果应用程序用 startForegroundService() 启动一个服务实例,它就会 must 将该服务实例过渡到前台状态并显示通知。 如果服务实例在 startForeground() 被调用之前就停止了,那么这个承诺就无法实现:这是应用程序的一个错误。

    Re #31 但是,发布一个其他应用程序可以直接启动的服务从根本上来说是不安全的。 你可以通过以下方式来缓解这一问题 所有 该服务的启动动作需要 startForeground() ,尽管这显然可能不是你所想的。

    谷歌问题追踪器 Comment 56

    这里有几种不同的情况会导致同样的结果。

    The outright semantic issue, that it's simply an error to kick something off with startForegroundService() but neglect to actu所有y transition it to foreground via startForeground() , is just that: a semantic issue. That's treated as an app bug, intention所有y. Stopping the service before transitioning it to foreground is an app error. That was the crux of the OP, and is why this issue has been marked "working as intended."

    然而,也存在以下问题 spurious 检测这个问题。 这就是 is 被当作一个真正的问题来处理,尽管它与这个特定的bug追踪器问题被分开追踪。 我们并不是对投诉充耳不闻。

    我看到的最好的更新是 issuetracker.google.com/issues/76112072#comment56 .我重写了我的代码,使用Context.bindService,这完全避免了对Context.startForegroundService的问题性调用。你可以在以下网址看到我的示例代码 github.com/paulpv/ForegroundServiceAPI26/tree/bound/app/src/...
    是的,正如我所写的,绑定应该是可行的,但我已经切换到JobServive,不打算改变任何东西,除非这个人也被打破了':)
    @swooby 你的解决方案对我帮助很大。
    在我的例子中,这是不可能的,因为我没有使用 context.stopService 。我从活动中发送本地广播来停止服务,服务注册到这个广播,调用 stopSelf ,所以它自己停止,当它调用 stopSelf 时, startForeground 在之前被调用了100%。
    大家看我的答案,它对我有用 stackoverflow.com/a/72754189/12228079
    Furkan Yurdakul
    Furkan Yurdakul
    发布于 2020-08-18
    0 人赞同

    由于访问这里的每个人都在遭受同样的事情,我想分享我的解决方案,之前没有人尝试过(无论如何在这个问题上)。我可以向你保证,它是有效的。 即使是在一个停止的断点上 which confirms this method.

    问题是要从服务本身调用 Service.startForeground(id, notification) ,对吗?遗憾的是,Android框架不能保证在5秒内调用 Service.startForeground(id, notification) ,但还是抛出了异常,所以我想出了这个办法。

  • Bind the service to a context with a binder from the service before calling Context.startForegroundService()
  • If the bind is successful, call Context.startForegroundService() from the service connection and immediately call Service.startForeground() inside the service connection.
  • IMPORTANT NOTE: Call the Context.bindService() method inside a try-catch because in some occasions the call can throw an exception, in which case you need to rely on calling Context.startForegroundService() directly and hope it will not fail. An example can be a broadcast receiver context, however getting application context does not throw an exception in that case, but using the context directly does.
  • 当我在绑定服务后,在触发 "startForeground "调用前,在断点上等待时,这也能起作用。在3-4秒之间的等待不会触发异常,而5秒之后就会抛出异常。(如果设备不能在5秒内执行两行代码,那么就该把它扔进垃圾桶了)。

    因此,从创建一个服务连接开始。

    // Create the service connection.
    ServiceConnection connection = new ServiceConnection()
        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
            // The binder of the service that returns the instance that is created.
            MyService.LocalBinder binder = (MyService.LocalBinder) service;
            // The getter method to acquire the service.
            MyService myService = binder.getService();
            // getServiceIntent(context) returns the relative service intent 
            context.startForegroundService(getServiceIntent(context));
            // This is the key: Without waiting Android Framework to call this method
            // inside Service.onCreate(), immediately call here to post the notification.
            myService.startForeground(myNotificationId, MyService.getNotification());
            // Release the connection to prevent leaks.
            context.unbindService(this);
        @Override
        public void onBindingDied(ComponentName name)
            Log.w(TAG, "Binding has dead.");
        @Override
        public void onNullBinding(ComponentName name)
            Log.w(TAG, "Bind was null.");
        @Override
        public void onServiceDisconnected(ComponentName name)
            Log.w(TAG, "Service is disconnected..");
    

    在你的服务中,创建一个返回你的服务实例的绑定器。

    public class MyService extends Service
        public class LocalBinder extends Binder
            public MyService getService()
                return MyService.this;
        // Create the instance on the service.
        private final LocalBinder binder = new LocalBinder();
        // Return this instance from onBind method.
        // You may also return new LocalBinder() which is
        // basically the same thing.
        @Nullable
        @Override
        public IBinder onBind(Intent intent)
            return binder;
    

    Then, try to bind the service从该上下文。如果它成功了,它将从你使用的服务连接中调用ServiceConnection.onServiceConnected()方法。然后,处理上面显示的代码中的逻辑。一个例子的代码会是这样的。

    // Try to bind the service
         context.bindService(getServiceIntent(context), connection,
                        Context.BIND_AUTO_CREATE);
    catch (RuntimeException ignored)
         // This is probably a broadcast receiver context even though we are calling getApplicationContext().
         // Just call startForegroundService instead since we cannot bind a service to a
         // broadcast receiver context. The service also have to call startForeground in
         // this case.
         context.startForegroundService(getServiceIntent(context));
    

    它似乎在我开发的应用程序上起作用,所以当你尝试时,它也应该起作用。

    谢谢你,刚刚在一个安装量超过500万的应用程序中实施了你的解决方案,让我们看看。希望能有最好的结果。
    到目前为止,它对我来说是有效的--差不多一个月后,不再有与startForegroundService()有关的崩溃或ANR了--谢谢!
    一个棘手的解决方案,我会尝试的,我的应用程序有8万个活跃用户
    但你必须在活动周期的onStop中解除对服务的绑定。
    `//这是关键所在。不用等Android框架在Service.onCreate()里面调用这个方法//,立即调用这里来发布通知。` - 我不确定这一点,你怎么能在Service的 onCreate 被调用之前就开始通知。而当 onServiceConnected 被调用时,就意味着 onCreate 已经被调用了,所以你的这句话是很无稽的
    Ahmad Arslan
    Ahmad Arslan
    发布于 2020-08-18
    0 人赞同

    现在在Android O中,你可以设置背景限制,如下所示

    正在调用一个服务类的服务

    Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        SettingActivity.this.startForegroundService(serviceIntent);
    } else {
        startService(serviceIntent);
    

    而服务类应该是这样的

    public class DetectedService extends Service { 
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return START_STICKY;
        @Override
        public void onCreate() {
            super.onCreate();
            int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
            // Do whatever you want to do here
        
    Alex
    你有没有在一些至少有几千个用户的应用程序中试过?我试过了,有些用户仍然有崩溃的问题。
    Ahmad Arslan
    不,不,我正在使用准确的代码,我没有看到客户得到崩溃。
    Alex
    你有多少用户?我看到每天有1万名用户的应用程序,每天有10-30多个崩溃(有错误)。当然,这只发生在android 8.0和android 8.1的用户身上,如果targetSdkV版本是27的话。在我将targetSdkV版本设置为25之后,所有问题都消失了,变成了0崩溃。
    只有2200个用户:-/但没有得到任何崩溃。
    Alex
    @Jeeva,不是的。目前,我使用targetSdkV 25版本和compileSdkV 27版本。在进行了大量的实验后,这看起来是最好的方法....。希望他们能完成 developer.android.com/topic/libraries/architecture/... 在2018年8月之前,因为 android-developers.googleblog.com/2017/12/...
    Roy Solberg
    Roy Solberg
    发布于 2020-08-18
    0 人赞同

    我有一个 widget 当设备处于清醒状态时,它做了相对频繁的更新,而我在短短几天内就看到了成千上万的崩溃。

    The issue trigger

    我甚至在我的Pixel 3 XL上也注意到了这个问题,而我根本不认为这个设备有多少负载。而任何和所有的代码路径都被 startForeground() 所覆盖。但后来我意识到,在很多情况下,我的服务真的能快速完成工作。 我相信我的应用程序的触发因素是,在系统真正开始显示通知之前,服务已经完成。

    The workaround/solution

    我能够摆脱所有的崩溃。我所做的是删除对 stopSelf() 的调用。 (我在考虑推迟停止的时间,直到我非常确定通知被显示出来,但是如果没有必要,我不想让用户看到通知)。当服务空闲了一分钟或系统正常销毁它而不抛出任何异常。

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        stopForeground(true);
    } else {
        stopSelf();
        
    但是,根据文档,stopForeground ...does not stop the service from running (for that you use stopSelf() or related methods)
    @DimaRostopira 是的,在实践中,我看到系统会在服务不活动一段时间(也许是一分钟)后停止其运行。这并不理想,但它阻止了系统的抱怨。
    我也遇到过这样的问题:当我的服务运行时间很短时,前台通知没有被删除。在调用stopForeground(true); stopSelf();之前添加100ms的延迟,这对我有帮助。你的解决方案并没有帮助。(Pixel 3, Android 11)
    Hexise
    Hexise
    发布于 2020-08-18
    0 人赞同

    我有一个解决这个问题的方法。我已经在我自己的应用程序(30万以上的DAU)中验证了这个修复方法,它可以减少至少95%的这种崩溃,但仍然不能100%避免这个问题。

    即使你保证在服务启动后才调用startForeground(),这个问题也会发生,正如Google所记录的那样。这可能是因为在许多情况下,服务的创建和初始化过程已经花费了5秒钟以上,那么无论你在何时何地调用startForeground()方法,这种崩溃是不可避免的。

    我的解决方案是确保startForeground()在startForegroundService()方法之后的5秒内被执行,无论你的服务需要创建和初始化多长时间。下面是详细的解决方案。

  • 首先不要使用startForegroundService,使用带有auto_create标志的bindService()。它将等待服务的初始化。下面是代码,我的样本服务是MusicService。

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
            applicationContext.unbindService(this);
        @Override
        public void onServiceDisconnected(ComponentName name) {
    }, Context.BIND_AUTO_CREATE);
    
  • 然后这里是MusicBinder的实现。

    * Use weak reference to avoid binder service leak. public class MusicBinder extends Binder { private WeakReference<MusicService> weakService; * Inject service instance to weak reference. public void onBind(MusicService service) { this.weakService = new WeakReference<>(service); public MusicService getService() { return weakService == null ? null : weakService.get();
  • 最重要的部分,MusicService的实现,forceForeground()方法将确保startForeground()方法在服务启动后就被调用。

    public class MusicService extends MediaBrowserServiceCompat {
        private final MusicBinder musicBind = new MusicBinder();
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after service start.
                startForeground(Constants.NOTIFICATION_ID, notification);
    
  • 如果你想在一个挂起的意图中运行步骤1的代码片段,例如你想在一个小部件中启动一个前台服务(点击小部件按钮)而不打开你的应用程序,你可以将代码片段包裹在一个广播接收器中,并发射一个广播事件而不是启动服务命令。

    这就是全部。希望能有所帮助。祝你好运。

  • 这仍然没有帮助,我有8万个活跃用户(20万次下载)。
    @user924 正如我所提到的,它至少可以减少95%的这种崩溃,但仍然不能100%避免这个问题。我没有找到100%解决这种异常的方法。对不起。
    如果Context.startForeground()有必要从应用程序的后台状态启动一个服务,这怎么会是一个解决方案?
    尽管@CommonsWare在他的评论中推荐了这个解决方案 stackoverflow.com/questions/55894636/... ,它的工作并不完美。在调用startForeground()unbindService()后,该服务有时会被系统销毁,并立即重新启动。当服务在启动过程中初始化一个工作线程时,我们会得到一个错误 java.lang.InternalError。线程在运行时关闭时启动 :-(
    tobalr
    tobalr
    发布于 2020-08-18
    0 人赞同

    我只是提醒一下,因为我在这方面浪费了太多的时间。即使我在 onCreate(..) 中首先调用 startForeground(..) ,我也一直得到这个异常。 最后我发现这个问题是由使用 NOTIFICATION_ID = 0 引起的。使用任何其他值似乎都能解决这个问题。

    在我的案例中,我使用的是ID 1,谢谢,问题得到了解决。
    我使用的是ID 101,但有时还是会收到这个错误。
    @RavindraKushwaha 并非如此。
    Shrdi
    Shrdi
    发布于 2020-08-18
    0 人赞同

    当你使用目标SDK 28或更高版本时,你必须为安卓9的设备添加一个如下的权限,否则异常会一直发生。

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
        
    我的应用程序有这个权限,但还是遇到了问题中描述的问题。
    @JohnnyLambada 似乎只有活动才能启动地面服务,尝试在后台启动服务。
    sandy
    sandy
    发布于 2020-08-18
    0 人赞同

    这么多答案,但在我的案例中没有一个是有效的。

    我已经开始了这样的服务。

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

    而在我的服务中的onStartCommand中

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(NOTIFICATION_ID, notification);
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(NOTIFICATION_ID, notification);
    

    还有,别忘了把NOTIFICATION_ID设置为非零

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    private static final int NOTIFICATION_ID = 555;
    

    因此,一切都很完美,但在8.1上仍然崩溃,原因如下。

         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                stopForeground(true);
            } else {
                stopForeground(true);
    

    我已经调用了停止前台和移除通知,但是一旦通知被移除,服务就变成了后台,后台服务不能从后台运行,在收到推送后启动。

    所以神奇的词是

       stopSelf();
    

    到目前为止,你的服务因任何原因而崩溃,请遵循上述所有步骤并享受。

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { stopForeground(true); } else { stopForeground(true); } what is this if else for? both case you were doing the same, stopForeground(true);
    我相信你是想把stopSelf();放在else块里面,而不是stopForeground(true)。
    是的,@JustinStanley 使用stopSelf()。
    Aba
    Shouldn't startForeground be called in onCreate ?
    Prajwal Waingankar
    我正在使用NotificationManager而不是Notification类。我怎样才能实现这一点呢?
    almisoft
    almisoft
    发布于 2020-08-18
    0 人赞同

    这个错误也发生在Android 8+上,当 服务.startForeground(int id, Notification notification) 被调用,而 id is set to 0.

    id int:按照NotificationManager.notify(int, Notification)的规定,该通知的标识符。 must not be 0 .

    通知ID也不要使用较高的数字。尽量使用一位数的ID。 stackoverflow.com/a/12228555/2527204
    Shalu Gupta
    Shalu Gupta
    发布于 2020-08-18
    0 人赞同

    请不要在内部调用任何StartForgroundServices。 onCreate() 方法,你必须调用StartForground服务中的 onStartCommand() 否则你将总是得到ANR,所以请不要在主线程中写复杂的登录。 onStartCommand() ;

    public class Services extends Service {
        private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                        .setContentTitle(getString(R.string.app_name))
                        .setContentText("SmartTracker Running")
                        .setAutoCancel(true);
                Notification notification = builder.build();
                startForeground(1, notification);
                Log.e("home_button","home button");
            } else {
                NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                        .setContentTitle(getString(R.string.app_name))
                        .setContentText("SmartTracker is Running...")
                        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                        .setAutoCancel(true);
                Notification notification = builder.build();
                startForeground(1, notification);
                Log.e("home_button_value","home_button_value");
            return super.onStartCommand(intent, flags, startId);
    

    EDIT: 注意!startForeground函数不能把0作为第一个参数,它将引发一个异常。这个例子包含错误的函数调用,把0改成你自己的常数,这个常数不能是0,也不能大于Max(Int32)。

    makovkastar
    makovkastar
    发布于 2020-08-18
    0 人赞同

    我一直在研究这个问题,这是我目前发现的情况。如果我们有类似这样的代码,就可能发生这种崩溃。

    MyForegroundService.java

    public class MyForegroundService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(...);
    

    MainActivity.java

    Intent serviceIntent = new Intent(this, MyForegroundService.class);
    startForegroundService(serviceIntent);
    stopService(serviceIntent);
    

    The exception is thrown in the following block of the code:

    ActiveServices.java

    private final void bringDownServiceLocked(ServiceRecord r) {
        if (r.fgRequired) {
            Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                      + r);
            r.fgRequired = false;
            r.fgWaiting = false;
            mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                        AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            mAm.mHandler.removeMessages(
                        ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
            if (r.app != null) {
                Message msg = mAm.mHandler.obtainMessage(
                    ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
                msg.obj = r.app;
                msg.getData().putCharSequence(
                    ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
                mAm.mHandler.sendMessage(msg);
    

    这个方法在MyForegroundServiceonCreate()之前执行,因为Android将服务的创建安排在主线程处理器上,但bringDownServiceLocked是在BinderThread上调用的,这是一个竞赛条件。这意味着MyForegroundService没有机会调用startForeground,这将导致崩溃。

    为了解决这个问题,我们必须确保bringDownServiceLocked不在MyForegroundServiceonCreate()之前被调用。

    public class MyForegroundService extends Service {
        private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";
        private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                context.removeStickyBroadcast(intent);
                stopForeground(true);
                stopSelf();
        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(...);
            registerReceiver(
                stopReceiver, new IntentFilter(ACTION_STOP));
        @Override
        public void onDestroy() {
            super.onDestroy();
            unregisterReceiver(stopReceiver);
        public static void stop(Context context) {
            context.sendStickyBroadcast(new Intent(ACTION_STOP));
    

    通过使用粘性广播,我们确保广播不会丢失,而stopReceiver一旦在MyForegroundServiceonCreate()中注册,就会收到停止意图。这时我们已经调用了startForeground(...)。我们还必须删除那个粘性广播,以防止stopReceiver下次被通知。

    请注意该方法sendStickyBroadcast已被弃用,我只把它作为解决这个问题的一个临时工作方法。

    当你想停止服务时,你应该调用它而不是context.stopService(serviceIntent)。
    Duna
    Duna
    发布于 2020-08-18
    0 人赞同

    From Google's docs on 安卓12的行为变化 :

    To provide a streamlined experience for short-running foreground services on Android 12, the system can delay the display of foreground service notifications by 10 seconds for certain foreground services. This change gives short-lived tasks a chance to complete before their notifications appear.
    

    解决方案。在onCreate()中为你使用Context.startForegroundService()的服务调用startForeground()。

    这个问题对安卓12来说太老了
    Kunal Kalwar
    Kunal Kalwar
    发布于 2020-08-18
    0 人赞同

    在crashlytics中,大约有10个用户在我们的应用程序中得到这个错误。

    正如Kimi Chiu所回答的--这个问题的主要原因是服务在被提升到前台之前就已经停止了。但在服务被销毁后,断言并没有停止。你可以尝试在调用startForegroundServices后添加StopService来重现这个问题--Kimi Chiu

    所以我测试了一下,能够重现这个情况。

    我采用的一个解决方案是,我让服务至少停留5秒钟,这样服务就会提升到前台。现在我在测试时无法重现这个问题。

    private fun stopService() {
            lifecycleScope.launch {
                delay(5000L)
                try {
                    stopForeground(true)
                    isForeGroundService = false
                    stopSelf()
                } catch (e: Exception) {
                    e.printStackTrace()
    

    Lets see if the issue is reproduce in our next build.

    Update :)-> This time there was no issue related to Context.startForegroundService() did not then call Service.startForeground()

    Before/After comparission->

    Before->enter image description here

    After-> enter image description here

    不错的解决方案。我们不得不这样做,而谷歌/AOSP不能为我们做一些更好的事情,这真是太愚蠢了。
    0 人赞同

    https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)。

    类似于startService(Intent),但有一个隐含的承诺,即 服务一旦开始运行,将调用startForeground(int, android.app.Notification)。 它开始运行。该服务有一定的时间,相当于 的时间来完成这个任务,否则系统将 自动停止该服务,并宣布该应用的ANR。

    与普通的startService(Intent)不同,这个方法可以在任何时候使用。 任何时候都可以使用这个方法,无论承载该服务的应用程序是否处于 前台状态。

    确保你在onCreate()上调用 Service.startForeground(int, android.app.Notification) ,这样你就能确保它被调用。如果你有任何可能阻止你这样做的条件,那么你最好使用正常的 Context.startService(Intent) 并自己调用 Service.startForeground(int, android.app.Notification)

    似乎 Context.startForegroundService() 增加了一个看门狗,以确保你在 Service.startForeground(int, android.app.Notification) 被破坏之前调用它......。

    Sambhaji Karad
    Sambhaji Karad
    发布于 2020-08-18
    0 人赞同

    我也面临着同样的问题,在花了一些时间后,我找到了一个解决方案,你可以试试下面的代码。如果你使用的是 Service ,那么把这段代码放在onCreate中,如果你使用的是 Intent Service ,那么把这段代码放在onHandleIntent中。

    if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_app";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();
            startForeground(1, notification);
        
    Andy Cass
    Andy Cass
    发布于 2020-08-18
    0 人赞同

    关于安卓O API 26的问题

    如果你立即停止服务(所以你的服务实际上没有真正运行(措辞/理解),而且你远远低于ANR间隔,你仍然需要在stopSelf之前调用startForeground。

    https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

    试过这个方法,但还是产生了一个错误:-。

    if (Util.SDK_INT > 26) {
        mContext.startForegroundService(playIntent);
    } else {
        mContext.startService(playIntent);
    

    我正在使用这个,直到错误得到解决。

    mContext.startService(playIntent);
        
    Should be Util.SDK_INT >= 26, not just bigger
    Emil
    你的意思是startService对所有的SDK版本仍然有效吗?
    Gaurav Singla
    Gaurav Singla
    发布于 2020-08-18
    0 人赞同

    即使在调用 Service 中的 startForeground 后,如果我们在调用 onCreate 之前调用 stopService ,它在某些设备上会崩溃。 因此,我通过用一个额外的标志来启动服务来解决这个问题。

    Intent intent = new Intent(context, YourService.class);
    intent.putExtra("request_stop", true);
    context.startService(intent);
    

    并在onStartCommand中增加了一个检查,看它是否被启动到停止。

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //call startForeground first
        if (intent != null) {
            boolean stopService = intent.getBooleanExtra("request_stop", false);
            if (stopService) {
                stopSelf();
        //Continue with the background task
        return START_STICKY;
    

    P.S. 如果服务没有运行,它将首先启动服务,这是一个开销。

    你不需要创建一个服务来停止它,只要使用本地广播管理器就可以了))
    这不是一个不同的服务。它是同一个服务,我想停止它。
    user924
    我没有说什么不同的服务,我的意思是为什么你要在启动的时候试图停止?
    使用广播定位信息
    亲爱的用户924,如果我必须停止服务,不需要BroadCast,我可以直接调用context.stopService()。上面的回答是解决在三星设备上发生的问题的一个技巧,如果你在实际调用onStartCommand之前就停止服务的话。
    AdamHurwitz
    AdamHurwitz
    发布于 2020-08-18
    0 人赞同

    Updating Data in onStartCommand(...)

    onBind(...)

    相对于 onCreate(...) onBind(...) 是一个更好的启动 startForeground 的生命周期事件,因为 onBind(...) 传递了一个 Intent ,其中可能包含初始化 Service 所需的 Bundle 的重要数据。然而,这并不是必须的,因为 onStartCommand(...) 是在第一次创建 Service 时被调用的,或者在之后的时间里被调用。

    onStartCommand(...)

    onStartCommand(...) 中的 startForeground 是很重要的,以便在已经创建的 Service 中更新它。

    ContextCompat.startForegroundService(...) 在创建了 Service 之后被调用时, onBind(...) onCreate(...) 不会被调用。因此,更新的数据可以通过 Intent 传入 onStartCommand(...) Bundle 来更新 Service 中的数据。

    Sample

    我正在使用这种模式来实现 PlayerNotificationManager 中的 Coinverse 加密货币新闻应用程序。

    活动 / Fragment.kt

    context?.bindService(
            Intent(context, AudioService::class.java),
            serviceConnection, Context.BIND_AUTO_CREATE)
    ContextCompat.startForegroundService(
            context!!,
            Intent(context, AudioService::class.java).apply {
                action = CONTENT_SELECTED_ACTION
                putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                    audioUrl = uri.toString()
    

    音频服务.kt

    private var uri: Uri = Uri.parse("")
    override fun onBind(intent: Intent?) =
            AudioServiceBinder().apply {
                player = ExoPlayerFactory.newSimpleInstance(
                        applicationContext,
                        AudioOnlyRenderersFactory(applicationContext),
                        DefaultTrackSelector())
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        intent?.let {
            when (intent.action) {
                CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                    val intentUri = Uri.parse(content.audioUrl)
                    // Checks whether to update Uri passed in Intent Bundle.
                    if (!intentUri.equals(uri)) {
                        uri = intentUri
                        player?.prepare(ProgressiveMediaSource.Factory(
                                DefaultDataSourceFactory(
                                        this,
                                        Util.getUserAgent(this, getString(app_name))))
                                .createMediaSource(uri))
                        player?.playWhenReady = true
                        // Calling 'startForeground' in 'buildNotification(...)'.          
                        buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
        return super.onStartCommand(intent, flags, startId)
    // Calling 'startForeground' in 'onNotificationStarted(...)'.
    private fun buildNotification(content: Content): Unit? {
        playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
                this,
                content.title,
                app_name,
                if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
                object : PlayerNotificationManager.MediaDescriptionAdapter {
                    override fun createCurrentContentIntent(player: Player?) = ...
                    override fun getCurrentContentText(player: Player?) = ...
                    override fun getCurrentContentTitle(player: Player?) = ...
                    override fun getCurrentLargeIcon(player: Player?,
                                                     callback: PlayerNotificationManager.BitmapCallback?) = ...
                object : PlayerNotificationManager.NotificationListener {
                    override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                        startForeground(notificationId, notification)
                    override fun onNotificationCancelled(notificationId: Int) {
                        stopForeground(true)
                        stopSelf()
        return playerNotificationManager.setPlayer(player)
        
    a54studio
    a54studio
    发布于 2020-08-18
    0 人赞同

    好吧,我注意到了一些东西,可能对其他一些人也有帮助。这完全是通过测试,看我是否能找出如何解决我所看到的情况。为了简单起见,假设我有一个方法,从presenter调用这个。

    context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    

    这将以同样的错误崩溃。 在该方法完成之前,服务将不会启动,因此服务中没有onCreate()

    因此,即使你在主线程之外更新UI,如果你有什么东西可能会阻碍该方法之后,它就不会按时启动,并给你带来可怕的前台错误。 在我的例子中,我们正在把一些东西加载到一个队列中,并且每个都调用了startForegroundService,但在后台每个都有一些逻辑参与。 因此,如果逻辑花了太长时间来完成该方法,因为它们是背靠背调用的,那么崩溃时间就到了。旧的startService只是忽略它,继续它的方式,因为我们每次都调用它,下一轮会完成。

    这让我想知道,如果我从后台的线程中调用这个服务,它能不能不在启动时被完全绑定并立即运行,所以我开始实验。 尽管这不是立即启动它,但它没有崩溃。

    new Handler(Looper.getMainLooper()).post(new Runnable() {
            public void run() {
                   context.startForegroundService(new Intent(context, 
               TaskQueueExecutorService.class));
                   try {
                       Thread.sleep(10000);
                   } catch (InterruptedException e) {
                      e.printStackTrace();
    

    我不会假装知道它为什么不崩溃,尽管我怀疑这迫使它等待,直到主线程能够及时处理它。 我知道把它和主线程绑在一起并不理想,但由于我的用法是在后台调用它,我并不关心它是否能等到它能完成而不是崩溃。

    你是在背景中还是在活动中开始你的服务?
    背景。 问题是,安卓系统不能保证服务在规定的时间内启动。 人们会认为他们会直接抛出一个错误,如果你想忽略它,但可惜的是,安卓不是这样。 它必须是致命的。 这对我们的应用程序有帮助,但并没有完全解决它。
    Khemraj
    Khemraj
    发布于 2020-08-18
    0 人赞同

    我在@humazed的回答中添加了一些代码。所以没有初始通知。这可能是一个变通办法,但对我来说是可行的。

    @Override
     public void onCreate() {
            super.onCreate();
            if (Build.VERSION.SDK_INT >= 26) {
                String CHANNEL_ID = "my_channel_01";
                NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                        "Channel human readable title",
                        NotificationManager.IMPORTANCE_DEFAULT);
                ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
                Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                        .setContentTitle("")
                        .setContentText("")
                        .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                        .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();
                startForeground(1, notification);
    

    我在小图标中添加了透明的颜色,在通知中添加了颜色。 它可以工作。

    Pankaj Kant Patel
    Pankaj Kant Patel
    发布于 2020-08-18
    0 人赞同

    一个问题可能是在AndroidManifest文件中没有启用服务类。 请你也检查一下。

    <service
            android:name=".AudioRecorderService"
            android:enabled="true"
            android:exported="false"
            android:foregroundServiceType="microphone" />
        
    this is what I needed inside Manifest to be able to continue Play Audio <service android:name=".common.audio.AudioPlayerService" android:foregroundServiceType="mediaPlayback" ....
    Malachiasz
    Malachiasz
    发布于 2020-08-18
    0 人赞同

    我在Pixel 3、Android 11中遇到一个问题,当我的服务运行时间很短时,那么前台通知就不会被驳回。

    在stopForeground()stopSelf()之前添加100ms的延迟,似乎有帮助 .

    人们在这里写道,stopForeground()应该在stopSelf()之前调用。我无法确认,但我猜它并不屑于这样做。

    public class AService extends Service {
    @Override
    public void onCreate() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            startForeground(
                getForegroundNotificationId(),
                channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText()),
                ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
        } else {
            startForeground(getForegroundNotificationId(),
                channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText())
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground();
        if (hasQueueMoreItems()) {
            startWorkerThreads();
        } else {
            stopForeground(true);
            stopSelf();
        return START_STICKY;
    private class WorkerRunnable implements Runnable {
        @Override
        public void run() {
            while (getItem() != null && !isLoopInterrupted) {
                doSomething(getItem())   
            waitALittle();
            stopForeground(true);
            stopSelf();
        private void waitALittle() {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
        
    SaimumIslam27
    SaimumIslam27
    发布于 2020-08-18
    0 人赞同

    我只是在调用 PendingIntent 的空或不空之前检查一下 context.startForegroundService(service_intent) 函数。

    this works for me

    PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
                context.startForegroundService(service_intent);
                context.startService(service_intent);
        
    a54studio
    这实际上只是把服务踢到startService而不是startForegroundService(如果空的话)。 if语句必须是嵌套的,否则应用程序在某个时候试图使用后来版本的服务时就会崩溃。
    Hossein Karami
    Hossein Karami
    发布于 2020-08-18
    0 人赞同

    只要在服务或IntentService创建后立即调用startForeground方法即可。

    import android.app.Notification;
    public class AuthenticationService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(1,new Notification());
        
    Gerry
    致命的异常:main android.app.RemoteServiceException。startForeground的坏通知:java.lang.RuntimeException:服务通知的通道无效。Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1821) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6626)
    melianor
    melianor
    发布于 2020-08-18
    0 人赞同

    我已经解决了这个问题,用 startService(intent) 而不是 Context.startForeground() 来启动服务,并在 super.OnCreate() 之后立即调用 startForegound() 。此外,如果你在启动时启动服务,你可以在启动广播时启动启动服务的活动。虽然这不是一个永久的解决方案,但它是有效的。

    Mateen Chaudhry
    在一些小米设备中,活动不能从后台启动
    Farmer
    Farmer
    发布于 2020-08-18
    0 人赞同

    我只是分享我对此事的看法。我不确定(100%告诉你)上述代码对我和其他人都不适用,但有些时候我遇到这个问题。假设我运行应用程序 10 time then might be got this issue 2 to 3 three time .

    我已经尝试了上面所有的答案,但仍然没有解决这个问题。我已经实现了上述所有代码,并在不同的api级别(API级别26、28、29)和不同的手机(三星、小米、MIUI、VIVO、MOTO、一加、华为等)上进行了测试,得到的问题如下。

    Context.startForegroundService() did not then call Service.startForeground();
    

    我已经读过service在谷歌开发者网站、其他一些博客和一些堆栈溢出的问题上,我得到的想法是,当我们调用startForgroundSerivce()方法时,这个问题会发生,但当时服务还没有启动。

    在我的案例中,我已经停止了服务,然后立即启动服务。以下是提示。

    ....//some other code
    ...// API level and other device auto star service condition is already set
    stopService();
    startService();
    .....//some other code
    

    在这种情况下,由于处理速度和RAM中的低内存,服务没有被启动,但startForegroundService()方法被调用并引发异常。

    Work for me:

    new Handler().postDelayed(()->ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);