Android 9(饼图),上下文.startForegroundService()随后未调用服务.startForeground():服务记录

o4tp2gmn  于 2022-12-16  发布在  Android
关注(0)|答案(8)|浏览(175)

首先,我看了这些;

我有一个流媒体应用程序,几乎有一百万人在使用。我正在为播放器使用前台服务。我还没有实现MediaSession。我有99.95%的无崩溃会话。所以这个应用程序可以在所有版本上工作,但我开始在android 9上收到崩溃报告(ANR)。这种崩溃只发生在Samsung手机上,特别是s9, s9+, s10, s10+, note9机型。
我试过了,

  • onCreate()中调用startForeground()方法
  • Context.stopService()之前调用Service.startForeground()
  • 其他堆栈溢出类似问题的答案

我看了一些谷歌开发者的评论,他们说这只是Intended Behavior。我想知道这是三星的系统还是Android操作系统发生的。有人对此有意见吗?我该如何解决这个问题?

ryhaxcpt

ryhaxcpt1#

经过太多的斗争与此崩溃终于,我修复了这个异常完全找到解决方案。
请确保在您的服务中做过这些事情,我将它们列出如下:(有些东西是重复的,正如在另一个答案中提到的,我只是再写一遍)。
1-呼叫
开始前景()
onCreateonStartCommand中。(可以多次调用startForeground())

@Override
public void onCreate() {
    super.onCreate();
    startCommand();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent == null) {
        return START_NOT_STICKY;
    }
    final int command = intent.getIntExtra(MAIN_SERVICE_COMMAND_KEY, -1);

    if (command == MAIN_SERVICE_START_COMMAND) {
        startCommand();
        return START_STICKY;
    }
    return START_NOT_STICKY;
}

 private void startCommand() {
    createNotificationAndStartForeground();
    runningStatus.set(STARTED);
}

2-停止您的服务使用
停止服务()
,则无需调用stopForeground()stopSelf()

try {
        context.stopService(
                new Intent(
                        context,
                        NavigationService.class
                )
        );
    } catch (Exception ex) {
        Crashlytics.logException(ex);
        LogManager.e("Service manager can't stop service ", ex);
    }

3-启动服务时使用
上下文兼容启动前台服务()
它将处理不同的API版本。

ContextCompat.startForegroundService(
            context,
            NavigationService.getStartIntent(context)
    );

4-如果您的服务具有操作(需要待定意图)使用广播接收器而不是当前服务处理待定意图(它将在Create()上调用您的服务,这可能很危险,或者使用PendingIntent.FLAG_NO_CREATE),因此,最好使用特定的广播接收器来处理您的服务通知操作,我的意思是使用**PendingIntent.getBroadcast()**创建所有待定Intent。

private PendingIntent getStopActionPendingIntent() {
    final Intent stopNotificationIntent = getBroadCastIntent();

    stopNotificationIntent.setAction(BROADCAST_STOP_SERVICE);

    return getPendingIntent(stopNotificationIntent);
}

private PendingIntent getPendingIntent(final Intent intent) {
    return PendingIntent.getBroadcast(
            this,
            0,
            intent,
            0
    );
}

new NotificationCompat.Builder(this, CHANNEL_ID)
            .addAction(
                    new NotificationCompat.Action(
                            R.drawable.notification,
                            getString(R.string.switch_off),
                            getStopActionPendingIntent()
                    )
            )

5-始终在停止服务之前确保已创建并启动服务(I创建具有我的服务状态的全局类)

if (navigationServiceStatus == STARTED) {
            serviceManager.stopNavigationService();
        }

6-将notificationId设置为一个长数字,例如121412。
7-使用NotificationCompat.Builder将处理不同的API版本,您只需为构建版本〉= Build.VERSION_CODES.O.创建通知通道。(这不是一个解决方案,只是使您的代码更具可读性)
8-加

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

权限到你的清单。(这一个在android文档中提到)Android Foreground Service

希望对你有帮助:))

wkyowqbh

wkyowqbh2#

我一直在等待我的崩溃报告分享解决方案。我没有得到任何崩溃或ANR近20天。我想分享我的解决方案。它可以帮助那些谁遇到这个问题。
onCreate()方法中

  • 首先,我的应用是一个媒体应用,我还没有实现媒体会话,我正在onCreate() . Official doc的顶部创建一个通知通道
  • 我在Context.startForegroundService()方法之后调用Service.startForeground()方法。在我的prepareAndStartForeground()方法中。

注意:我不知道为什么**ContextCompat.startForegroundService()**不能正常工作。
因此,我手动将相同的函数添加到我的服务类中,而不是调用ContextCompat.startForegroundService()

private fun startForegroundService(intent: Intent) {
    if (Build.VERSION.SDK_INT >= 26) {
        context.startForegroundService(intent)
    } else {
        // Pre-O behavior.
        context.startService(intent)
    }
}

prepareAndStartForeground()方法

private fun prepareAndStartForeground() {
    try {
        val intent = Intent(ctx, MusicService::class.java)
        startForegroundService(intent)

        val n = mNotificationBuilder.build()
        // do sth
        startForeground(Define.NOTIFICATION_ID, n)
    } catch (e: Exception) {
        Log.e(TAG, "startForegroundNotification: " + e.message)
    }
}

这是我的onCreate()

override fun onCreate() {
    super.onCreate()
    createNotificationChannel()
    prepareAndStartForeground()
}

我的onStartCommand()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (intent == null) {
        return START_STICKY_COMPATIBILITY
    } else {
        //....
        //...
    }
    return START_STICKY
}

onRebindonBindonUnbind方法,例如

internal var binder: IBinder? = null

override fun onRebind(intent: Intent) {
    stopForeground(true) // <- remove notification
}

override fun onBind(intent: Intent): IBinder? {
    stopForeground(true) // <- remove notification
    return binder
}

override fun onUnbind(intent: Intent): Boolean {
    prepareAndStartForeground() // <- show notification again
    return true
}

调用onDestroy()时需要清除一些内容

override fun onDestroy() {
    super.onDestroy()
    releaseService()
   }
private fun releaseService() {
    stopMedia()
    stopTimer()
    // sth like these
    player = null
    mContext = null
    afChangeListener = null
    mAudioBecomingNoisy = null
    handler = null
    mNotificationBuilder = null
    mNotificationManager = null
    mInstance = null
}

我希望这个解决方案对您有效。

dkqlctbz

dkqlctbz3#

Android Service组件要正常工作有点棘手,尤其是在操作系统增加了额外限制的更高版本的Android上。正如其他答案中提到的,当启动Service时,使用ContextCompat.startForegroundService()。接下来,在Service.onStartCommand()中,立即调用startForeground()。将您要显示的Notification存储为成员字段,并使用它,除非它为null。示例:

private var notification:Notification? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (notification == null) {
        notification = createDefaultNotification()
    }
    startForeground(NOTIFICATION_ID, notification)

    // Do any additional setup and work herre

    return START_STICKY
}

总是在Service中返回START_STICKY。其他任何东西都可能是错误的,特别是如果你正在做一个任何类型的音频播放器。事实上,如果你正在做一个音频播放器,你不应该实现自己的服务,而是使用MediaBrowserServiceCompat(来自AndroidX)。
我也推荐我写的博客文章:https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd

mwecs4sa

mwecs4sa4#

我浪费了太多时间去弄清楚它为什么会崩溃,却不明白为什么。问题出在startForeground()的通知ID上。把它改成0以外的东西。这是用安卓11运行的。

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        
        //Create notification here
        
        startForeground(5, builder.build()); //Change the ID to something other than 0

        return START_NOT_STICKY;
    }
bihw5rsg

bihw5rsg5#

在相同的手机上遇到同样的问题后,我做了一些改变,崩溃也消失了。我不知道是什么造成了这个问题,但我猜是在onCreate和onStartCommand中调用startForeground。我不知道如果服务已经启动,并且在onCreate中正确调用了所有内容,为什么需要这样做。
其他变更:- 将serviceId更改为某个较低的数字(1-10)-减少通过单例同步类调用startFororegroundService的频率(以前在崩溃时实现了此功能,以防止在服务通过onStartCommand的回调启动之前停止服务,但现在如果服务已经启动,它还会过滤调用)。-使用START_REDELIVER_INTENT(应该不会影响任何内容)
这个问题一直发生在提到的手机只是一些用户,所以我怀疑这是有关一些新的更新,从三星,并最终将得到修复

acruukt9

acruukt96#

我几乎消除了MediaSessionCompat.回调方法(如onPlay()、onPause())中startForeground()的问题。

nhjlsmyf

nhjlsmyf7#

根据这个错误消息,这说明当你调用Context.startForegroundService()时,你必须使用Service.startForeground()方法发出通知。

oxcyiej7

oxcyiej78#

2022年12月13日更新

我在音乐播放器应用程序中遇到此问题
然后,上下文.startForegroundService()没有调用服务.startForeground()
经过大量的研究,我找到了一些解决办法,也许这会帮助其他人

解决方案1:

如果您的应用面向API级别26或更高级别,则当应用本身不在前台时,系统会对后台服务的运行施加限制。如果应用需要创建前台服务,则应用应调用startForegroundService()。即使应用在后台,系统也允许应用调用Context.startForegroundService()。但是,应用程序必须在创建服务后5秒内调用该服务的startForeground()方法。

**注意:**在onCreate()中调用startForeground(),以获取您使用Context.startForegroundService()的服务

为什么会发生这个问题,因为Android框架不能保证您的服务在5秒内启动,但另一方面,框架确实有一个严格的限制前台通知必须在5秒内发射,如果不检查框架是否已尝试启动服务startForeground(),则通知必须同时位于onCreate()onStartCommand()中,因为如果您的服务已创建并且活动正在尝试重新启动它,将不会调用onCreate()。通知ID不能为0,否则即使原因不同,也会发生相同的崩溃。不能在startForeground之前调用stopSelf

后台服务限制:

app服务有5秒时间调用startForeground(),app未在时限内调用startForegroudn(),系统停止服务并声明app为ANR。

可能性:

1.在调用startForeground()方法之前,前台服务被破坏/完成。
1.或者前台服务已经示例化,再次被调用,则不调用onCreate()方法,调用onStartCommand(),然后将逻辑移到onStartCommand()调用startForeground()方法。

  1. startForeground()中的通知ID不能为0,否则也会导致崩溃。

解决方案2:

1.在onCreate()onStartCommand()中调用startForeground()(可以多次调用startForeground()
1.使用context.stopService()停止服务,无需致电stopForeground()stopSelf()
1.使用ContextCompat.startForegroundService()启动您的服务。它将处理不同的API。
1.如果你的服务有动作(需要待定Intent)用广播接收器而不是你当前的服务来处理你的待定Intent(它将在onCreate()中调用你的服务并且可能是危险的,或者使用PendingIntent.FLAG_NO_CREATE),这是好的)。练习用一个特定的广播接收器来处理你的服务通知动作,我的意思是使用PendingIntent.getBroadcast()创建所有你的待定Intent。
1.一些崩溃报告来自与在Oreo中处理BOOT_COMPLETED相关的问题
1.基本上,一旦你执行startForegroundService(),你就必须执行startForeground()。如果你不能在你的服务中执行startForeground(),那么你最好在你的广播接收器中执行这种类型的检查,等等--这样你就只有在确定要执行startForeground()的时候才启动服务
1.按如下方式添加Handler()
Handler().postDelayed(()>ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);

解决方案3:

使用什么来代替前台服务?
现在有JobSchedulerJobService,它们是前台服务的更好的替代品。这是一个更好的选择,因为:作业运行时,系统会代表您的应用保留唤醒锁。因此,您无需采取任何操作来保证设备在作业期间保持唤醒状态。
这意味着你不再需要关心唤醒锁的处理,这就是为什么它与前台服务没有什么不同。从实现的Angular 来看,JobScheduler不是你的服务,它是一个系统的服务,大概它会正确地处理队列,谷歌永远不会终止自己的孩子。
Samsung已在其Samsung附件协议中从startForegroundService切换为JobSchedulerJobService当智能手表等设备需要与手机等主机通信时,这一功能非常有用,因为任务需要通过应用程序的主线程与用户交互。由于任务是由调度程序发布到主线程的,不过你应该记住,作业是在主线程上运行的,把所有繁重的工作都卸载到其他线程和异步任务上。

解决方案4:

问题是从服务本身调用Service.startForeground(id, notification),对吗?不幸的是,Android Framework不能保证在5秒内调用Service.onCreate(),但无论如何都会抛出异常,所以我想出了这种方法。在调用Context.startForegroundService()之前,使用绑定器将服务绑定到上下文
如果绑定成功,则从服务连接调用Context.startForegroundService(),并立即在服务连接内部调用Service.startForeground()

**重要提示:**在try-catch中调用Context.bindService()方法,因为在某些情况下,调用可能会引发异常,在这种情况下,您需要依赖于直接调用Context.startForegroundService(),并希望它不会失败。例如,广播接收器上下文,但是在这种情况下,获取应用程序上下文不会引发异常,但直接使用上下文会引发异常。
解决方案5:

如果您调用Context.startForegroundService(...),然后在调用Service.startForeground(...)之前调用Context.stopService(...),您的应用将崩溃。
信息由google团队提供
这不是一个框架错误;如果应用使用startForegroundService()启动服务示例,它必须将该服务示例转换到前台状态并显示通知。如果服务示例在调用startForeground()之前停止,则该承诺无法实现:这是应用程序中的漏洞。
发布一个其他应用可以直接启动的服务从根本上说是不安全的。2你可以通过将该服务的所有启动操作都视为需要startForeground()来减轻这一点,尽管很明显这可能不是你所想的。

解决方案6:

我能够消除所有的崩溃。我所做的是删除对stopSelf()的调用。(我考虑延迟停止,直到我非常确定通知显示,但我不希望用户看到通知,如果它是不必要的。)当服务已经空闲了一分钟或系统正常销毁它,没有抛出任何异常。

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

解决方案7:

与前台服务使用相关的常见崩溃是:
然后,上下文.startForegroundService()没有调用服务.startForeground()
要摆脱这种情况,请使用以下策略:
不要将服务作为前台服务启动,而是将服务作为后台服务启动并绑定到它,然后当您的Activity/UI组件中有可用的服务示例时,您可以直接调用服务内部的方法,该方法调用Service.startForeground()并添加通知。
这听起来像是一个黑客攻击,但想想像Spotify这样的音乐应用程序是如何启动服务的。这是一个类似的方法。结果非常出色。使用这种方法,问题数量从一个显著的无法揭示的数字减少到0。

相关问题