我在Android O操作系统上使用服务类。

我计划在后台使用服务。

Android文档指出

如果你的应用程序的API级别为26或更高,系统会对使用或创建后台服务施加限制,除非应用程序本身在前台。如果应用程序需要创建前台服务,应用程序应该调用startForegroundService()。

如果使用startForegroundService(),服务抛出以下错误。

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

这有什么问题?


当前回答

我已经修复了用startService(intent)而不是context . start前台()启动服务的问题,并在super.OnCreate()之后立即调用start前台()。此外,如果您在引导时启动服务,您可以启动在引导广播时启动服务的Activity。虽然这不是一个永久的解决方案,但它是有效的。

其他回答

很多答案,但没有一个对我有用。

我已经开始这样服务了。

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);
        }

我已经调用停止前台删除通知,但一旦通知删除服务成为后台和后台服务不能从后台运行在android O。在收到推送后启动。

神奇的词是

   stopSelf();

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

我只是分享我对这个的评论。我不确定(100%告诉)上面的代码对我和其他人也不起作用,但有时我会遇到这个问题。假设我运行应用程序10次,那么可能会得到这个问题2到3次。

我尝试了所有的答案,但仍然没有解决这个问题。我已经实现了以上所有代码,并在不同的api级别(api级别26,28,29)和不同的移动设备(三星,小米,MIUI, Vivo, Moto, One Plus,华为等)上进行了测试,并得到了相同的以下问题。

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

我在谷歌开发人员网站上阅读了服务,一些其他博客和一些堆栈溢出问题,并得到了这个问题将发生在我们调用startforgroundservice()方法时,但当时服务还没有启动的想法。

在我的情况下,我已经停止服务后立即启动服务。下面是提示。

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

在这种情况下,由于处理速度和内存不足,服务不会启动,但会调用startForegroundService()方法并触发异常。

为我工作:

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

我改变了代码,并设置了500毫秒的延迟来调用startService()方法,问题解决了。这不是完美的解决方案,因为这样应用程序的性能会下降。

注意: 这只适用于前台和后台服务。使用Bind服务时没有测试。 我之所以分享这些,是因为这是我解决这个问题的唯一方法。

如果你在调用service . start前台(…)之前调用Context.startForegroundService(…),然后再调用Context.stopService(…),你的应用程序会崩溃。

我这里有清晰的复制品前景服务api26

我在谷歌问题跟踪器上打开了一个bug

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

希望我的与明确的复制步骤将使削减。

谷歌团队提供的信息

谷歌问题跟踪器评论36

这不是一个框架错误;这是故意的。如果应用程序使用startForegroundService()启动一个服务实例,它必须将该服务实例转换到前台状态并显示通知。如果服务实例在start前台()被调用之前就停止了,那么这个承诺就没有实现:这是应用程序中的一个bug。

re# 31,发布一个其他应用程序可以直接启动的服务从根本上来说是不安全的。您可以通过将该服务的所有启动操作都视为需要startForeground()来减轻这一点,尽管这显然不是您想要的。

谷歌问题跟踪器评论56

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

完全的语义问题是,用startForegroundService()启动一些东西,但忽略了通过startForeground()实际将其转换到前台,这只是一个语义问题。这被故意视为应用程序漏洞。在将服务转移到前台之前停止服务是应用程序错误。这是OP的关键,也是为什么这个问题被标记为“按预期工作”。

然而,也有关于这个问题的虚假检测的问题。这是一个真正的问题,尽管它是分开跟踪这个特殊的bug跟踪器问题。我们对抱怨不是充耳不闻。

更新onStartCommand中的数据(…)

上绑定(...

与onCreate(…)相比,onBind(…)是一个更好的生命周期事件来初始化start前台,因为onBind(…)传递了一个Intent,其中可能包含初始化服务所需的Bundle中的重要数据。然而,这是不必要的,因为onStartCommand(…)是在服务第一次创建或后续多次调用时调用的。

onStartCommand(...)

onStartCommand(…)中的start前台是很重要的,以便在服务创建后更新它。

当ContextCompat.startForegroundService(…)在服务创建后被调用时,onBind(…)和onCreate(…)没有被调用。因此,更新后的数据可以通过Intent Bundle传递到onStartCommand(…)来更新服务中的数据。

样本

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

Activity / 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()
            })
        })

AudioService.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)
}

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

类似于startService(Intent),但是有一个隐含的承诺 Service将调用start前台(int, android.app.Notification)一次 它开始运行。该服务被赋予了相当的时间 到ANR区间做这个,否则系统会 自动停止服务并声明应用程序ANR。 与普通的startService(Intent)不同,这个方法可以在 任何时候,不管托管服务的应用程序是否在 前景的国家。

一定要打电话给服务处。在onCreate()上的start前台(int, android.app.Notification),所以你确保它将被调用..如果你有任何条件,可能会阻止你这样做,那么你最好使用正常的Context.startService(意图)并调用服务。start前台(int, android.app.Notification)你自己。

似乎Context.startForegroundService()添加了一个看门狗来确保你调用了服务。start前台(int, android.app.Notification)在它被销毁之前…