Android 9(Pie),Context.startForegroundService()然后未调用Service.startForeground():ServiceRecord

时间:2019-04-28 21:15:21

标签: android android-service foreground-service

首先, 我看着这些;

enter image description here

我有将近一百万人使用的流应用程序。我正在为播放器使用前台服务。我还没有实现MediaSession。我有99.95%的无崩溃会话。 因此,此应用可以在所有版本上使用,但是我开始使用android 9收到崩溃报告(ANR)。此崩溃仅发生在Samsung手机,尤其是s9, s9+, s10, s10+, note9机型上。

我尝试过这些,

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

我阅读了Google开发人员的一些评论,他们说这仅仅是Intended Behavior。我想知道这是由三星的系统还是Android操作系统引起的。有人对此有意见吗?我该如何解决?

6 个答案:

答案 0 :(得分:5)

经过与这次崩溃的过多斗争之后,我彻底修复了此异常并找到了解决方案。

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

1-通话

  

startForeground()

onCreate onStartCommand 中。(可以多次调用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),这是一个好的做法,拥有一个特定的Broadcast接收器来处理您的服务通知操作,我的意思是使用 PendingIntent.getBroadcast()创建所有待处理的意图。 strong>。

    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版本> = Build.VERSION_CODES.O创建通知通道即可。(这不是一个解决方案,仅使您的代码更具可读性)

8-添加

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

权限到您的清单。 (android docs中提到了这一点) Android Foreground Service

希望它会有所帮助:))

答案 1 :(得分:1)

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

onCreate()方法中

  • 首先,我的应用是媒体应用。我尚未实现mediasession。我正在onCreate()的顶部创建一个通知频道Official doc
  • 我在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
}

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
}

我希望该解决方案对您来说正确。

答案 2 :(得分:0)

在使用同一部手机遇到同样的问题后,我进行了一些更改,然后崩溃消失了。我不确定是什么原因导致的,但我猜想 onCreate和onStartCommand中的startForeground。我不确定如果已经启动服务并且在onCreate中正确调用了所有内容,为什么需要这样做。

其他更改: -将serviceId更改为较低的数字(1-10) -通过单例同步类较少地调用startFororegroundService(这是在崩溃之前实现的,以防止在通过onStartCommand的回调启动服务之前停止服务,但是现在,如果服务已经启动,它也可以过滤调用)。 -使用START_REDELIVER_INTENT(不会有任何影响)

问题仅在某些用户的手机上出现,所以我怀疑这与三星的一些新更新有关,最终将得到解决

答案 3 :(得分:0)

Android {'user_name': 'xcv', 'age': 27, 'country': 'value', 'work_history': [{'name': 'CSC', 'location': 'chennai'}, {'name': 'crayon', 'location': 'chennai'}], 'marital_status': 'married} 组件很难正常工作,尤其是在OS附加了更多限制的更高版本的Android上。如其他答案所述,在启动Service时,请使用Service。接下来,在ContextCompat.startForegroundService()中,立即调用Service.onStartCommand()。将要显示的startForeground()存储为成员字段,并使用它,除非它为null。示例:

Notification

始终在您的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 } 中返回START_STICKY。其他任何事情都可能是错误的事情,尤其是当您在做任何类型的音频播放器时。实际上,如果您正在使用音频播放器,则不应实施自己的服务,而应使用MediaBrowserServiceCompat(来自AndroidX)。

我还推荐我在此撰写的博客文章:https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd

答案 4 :(得分:0)

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

答案 5 :(得分:0)

根据此错误消息,这表示当您调用Context.startForegroundService()时,必须使用Service.startForeground()方法发出通知。这就是我的理解。