绑定服务崩溃与“Context.startForegroundService() 没有然后调用 Service.startForeground()”错误
Posted
技术标签:
【中文标题】绑定服务崩溃与“Context.startForegroundService() 没有然后调用 Service.startForeground()”错误【英文标题】:Bound service crash with "Context.startForegroundService() did not then call Service.startForeground()" error 【发布时间】: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 感谢您的建议。我添加了一个答案。 【参考方案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
,请参阅***.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;
【讨论】:
以上是关于绑定服务崩溃与“Context.startForegroundService() 没有然后调用 Service.startForeground()”错误的主要内容,如果未能解决你的问题,请参考以下文章