Android 9 (Pie), Context.startForegroundService() 没有调用 Service.startForeground(): ServiceRecord

新手上路,请多包涵

首先,我看了这些;

在此处输入图像描述

我有一个近一百万人使用的流媒体应用程序。我正在为播放器使用前台服务。我还没有实施 MediaSession 。我有 99.95% 的无崩溃会话。所以这个应用程序适用于所有版本,但我开始使用 android 9 获取崩溃报告 (ANR)。此崩溃仅发生 Samsung 手机,尤其是 s9, s9+, s10, s10+, note9 型号。

我试过这些,

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

我读了谷歌开发人员的一些评论,他们说它只是 Intended Behavior 。请问是三星的系统还是安卓系统出现的。有人对此有意见吗?我怎样才能解决这个问题?

原文由 Beyazid 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 614
2 个回答

我正在等待我的崩溃报告来分享解决方案。我将近 20 天没有遇到任何崩溃或 ANR。我想分享我的解决方案。它可以帮助那些遇到这个问题的人。

onCreate() 方法中

  • 首先,我的应用程序是一个媒体应用程序。我还没有实施 mediasession。我在 onCreate() 的顶部创建了一个 通知通道官方文档
  • 我在 Service.startForeground() Context.startForegroundService() 方法。在我的 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
}


onRebind , onBind , onUnbind 这些方法

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
}

我希望此解决方案适合您。

原文由 Beyazid 发布,翻译遵循 CC BY-SA 4.0 许可协议

在与这次崩溃进行了太多的斗争之后,我终于完全修复了这个异常并找到了解决方案。

确保在您的服务中完成了这些工作,我将它们列在下面:(其中一些是重复的,正如另一个答案中提到的,我只是再次写了一遍)。

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- 停止 使用您的服务

context.stopService()

,无需调用 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- 使用 开始 您的服务

ContextCompat.startForegroundService()

它将处理不同的 API 版本。

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

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

     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- 始终在 停止服务 之前确保您的服务已创建并启动(我创建了一个具有我的服务状态的全局类)

   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 前台服务

希望能帮助到你 :))

原文由 Sepehr 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题