【问题标题】:Bound service crash with "Context.startForegroundService() did not then call Service.startForeground()" error绑定服务崩溃与“Context.startForegroundService() 没有然后调用 Service.startForeground()”错误
【发布时间】:2018-05-16 11:53:59
【问题描述】:

我目前正在开发音频播放应用程序,并且正在使用已启动的绑定服务在后台播放音乐。我使用以下代码启动并绑定到服务。

val intent = Intent(context, MusicService::class.java)
context.startService(intent)
context.bindService(intent, serviceConnection, 0)

播放时提升到前台,音乐暂停时降级。

// onAudioPlay
service.startForeground(NOTIFY_ID, notification)

// onAudioPause
service.stopForeground(false)

到目前为止,服务工作正常。但是,当用户在暂停状态下滑动(删除)通知时,服务会在几秒钟后崩溃并给出此错误。

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

这只发生在奥利奥上,我已经阅读了奥利奥的背景限制。但是以下几点让我感到困惑。

  • 此服务为绑定服务(不受限制)
  • 我从不使用 Context.startForegroundService() 来启动服务。(也不想使用它)
  • 服务从前台降级时不会崩溃,它是在删除通知时发生的。

为什么服务会崩溃?我究竟做错了什么?如果有人告诉我这里发生了什么,我将非常感激。

【问题讨论】:

  • 这很有趣。我建议您将解决方案发布为答案并将其标记为已接受,而不是编辑问题。未来的读者也会更清楚。
  • @YuchenZhong 感谢您的建议。我添加了一个答案。

标签: android service kotlin android-8.0-oreo


【解决方案1】:

扩展UdeshUk's answer - 对MediaButtonReceiver 的任何使用(构建待处理意图或仅接收媒体按钮意图)都可能导致使用startForegroundService 调用您的服务,因为MediaButtonReceiver.onReceive 以这种方式启动您的服务在奥利奥上。我在任何地方都没有看到这方面的记录。

【讨论】:

  • 我在调用 MediaButtonReceiver.handleIntent 时遇到了同样的问题,并且文档中有一条注释“一旦服务启动,它必须开始在前台运行”。在阅读您的答案之前,它非常神秘。
  • 如果您在清单中注册 MediaButtonReceiver,Android 可以尝试通过调用 startForegroundService 重新启动您的媒体会话,如果在这种情况下您没有尽快显示通知,则会导致 ANR。您可以按照 [docs] (developer.android.com/guide/topics/media-apps/…) 中的说明选择退出此行为
  • 您知道使用setMediaButtonReceiver(null) 是否会导致使用外部媒体控件出现任何问题吗?我更新了我的代码以使用它,它仍然可以识别来自我的蓝牙耳机的按钮命令,但将其设置为 null 似乎很奇怪。
  • @RJAylward 但实际上仅适用于 Android 5.0(API 级别 21)及更高版本?
【解决方案2】:

我发现了问题。在我的通知中我已经设置了

setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(service, PlaybackStateCompat.ACTION_STOP))

因此,当用户删除通知时,它会调用 startForegroundService(),因为我之前在暂停时调用了 stopForeground()。

为了克服,我使用了一个自定义的待处理意图并在 onStartCommand() 中处理它,而不是 MediaButtonReveiver 的 PendingIntent

【讨论】:

  • 你能解释一下那行代码是如何隐式调用 startForegroundService() 的吗?我有类似的问题,我从不调用它,它可能会提供线索。谢谢。
  • @SteveM 上面的行创建了一个PendingIntent,它以MediaButtonReceiver 为目标,然后它可以用startForegroundService 调用你的服务(这也取决于你如何在清单中配置你的意图过滤器) .因此,避免那些startForegroundService-calls 的最佳解决方案是避免MediaButtonReveiver,请参阅stackoverflow.com/a/50629196/1394330
  • @UdeshUK 当你说你创建了一个自定义待定意图时,我很困惑。不只是在您的MediaBrowserService 工作中向onStartCommand() 添加通知吗?
  • @KrisB 抱歉,我没有使用MediaBrowserService。我有一个自定义音乐服务来处理播放和媒体文件。
【解决方案3】:

您必须在 Service 类的 onCreate() 方法中调用此方法:->

private void startServiceOreoCondition(){
        if (Build.VERSION.SDK_INT >= 26) {


            String CHANNEL_ID = "my_service";
            String CHANNEL_NAME = "My Background Service";

            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    CHANNEL_NAME,NotificationManager.IMPORTANCE_NONE);
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(R.drawable.ic_tgc_icon).setPriority(PRIORITY_MIN).build();

            startForeground(101, notification);
        }
    }

【讨论】:

    【解决方案4】:

    晚会,但如前所述,它是MediaButtonReceiver;它有这个代码:

        @Override
        public void onReceive(Context context, Intent intent) {
            ...
            startForegroundService(context, intent);
            ...
        }
    
    

    我在绑定服务中使用接收器,所以我创建了一个与MediaButtonReceiver 具有完全相同代码的类,除了这一行。我将其替换为:

            context.startService(intent);
    

    【讨论】:

    • 谷歌真的很高兴让我们意识到这一点。如果 Google 修改了 MediaButtonReceiver 中的代码会怎样?每次有任何更改时,您是否都将代码复制到您的项目中?
    • 我猜是的,我猜他们这样做是出于某种强制;您只能访问前台的媒体按钮。虽然这是短视的,但整个服务 api 无论如何都是一种痛苦。
    • 我刚刚将setMediaButtonReceiver 设置为null,这似乎像这里提到的那样工作:developer.android.com/guide/topics/media-apps/… 我担心它会影响从蓝牙耳机处理媒体控制,但它不会。
    • 你还能收听媒体按钮的事件吗?
    • @KrisB 但实际上仅适用于 Android 5.0(API 级别 21)及更高版本?
    【解决方案5】:

    来自文档8.0 Behavior Changes

    系统甚至允许应用调用 Context.startForegroundService() 当应用程序在后台时。但是,应用程序必须调用它 服务的 startForeground() 方法在 5 秒内 服务已创建。

    因此,您必须在 onCreate() 中为您的服务调用 startForeground。

    【讨论】:

    • 是的。但这发生在我的活动开放时。所以我的应用程序不在后台。而且我也不用 startForegroundService() 启动服务。
    【解决方案6】:

    我有同样的问题,但我使用通知服务修复了它

    public void createNotificationChannel(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
            NotificationChannel serviceChanel =new NotificationChannel(
                    CHANNEL_ID,
                    "Forground service Chane"
                    , NotificationManager.IMPORTANCE_DEFAULT
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(serviceChanel);
        }
    }
    

    在课程开始时创建 Chanel id

    public static final String CHANNEL_ID = "ForegroundServiceChannel";

    和服务 onStartCommand indside

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
            createNotificationChannel();
            Intent notificationIntent = new Intent(this , MainActivity.class);
            PendingIntent pendingIntent =PendingIntent.getActivity(this,0,notificationIntent,0);
            Notification notification = new NotificationCompat.Builder(this,CHANNEL_ID)
                    .setContentTitle("ForgroundService")
                    .setContentText("Any message")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .build();
            startForeground(1,notification);
        }
    

    输入代码

    https://androidwave.com/foreground-service-android-example/

    【讨论】:

      【解决方案7】:

      MediaButtonReceiver 调用 startForegroundService,这对我来说是罪魁祸首。当我没有可播放的媒体(播放列表为空)时出现了我的问题。我通过在onStartCommand 中显示通知并立即取消它解决了这个问题:

          if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
              NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
      
              builder.setSmallIcon(R.drawable.notification)
                      .setContentTitle("")
                      .setContentText("")
                      .setSmallIcon(R.drawable.notification);
              startForeground(99, builder.build());
              stopForeground(true);
              
              MyMediaButtonReceiver.handleIntent(mediaSessionCompat, intent);
          
              return START_NOT_STICKY;
            }
      

      【讨论】:

        猜你喜欢
        • 2019-01-09
        • 2018-03-29
        • 2018-03-04
        • 2017-11-09
        • 2019-12-07
        • 1970-01-01
        • 1970-01-01
        • 2019-09-17
        相关资源
        最近更新 更多