Android 9 (Pie), Context.startForegroundService() 没有再调用 Service.startForeground(): ServiceRecord
Android 9 (Pie), Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord
首先,
我看了这些;
- Context.startForegroundService()没再调用
Service.startForeground()
- Context.startForegroundService() did not then call Service.startForeground
- Android 9 (Pie) Only: Context.startForegroundService() did not then call Service.startForeground() - Works fine on Oreo
- https://issuetracker.google.com/issues/76112072
我有一个近一百万人使用的流媒体应用程序。我正在为播放器使用前台服务。我还没有实现 MediaSession
。我有 99.95% 的无崩溃会话。
所以这个应用程序适用于所有版本,但我开始收到 android 9 的崩溃报告(ANR)。此崩溃仅发生在 Samsung
手机,尤其是 s9, s9+, s10, s10+, note9
型号。
我试过这些,
- 正在
onCreate()
中调用 startForeground()
方法
- 在
Context.stopService()
之前调用 Service.startForeground()
- 类似问题的其他 Whosebug 答案
我读了 Google 开发人员的一些评论,他们说它只是 Intended Behavior
。不知是三星的系统还是AndroidOS出现的。有人对此有意见吗?我该如何解决这个问题?
在用相同的手机遇到同样的问题后,我做了一些改动,崩溃现象消失了。我不确定是什么导致了这个问题,但我猜是调用
onCreate 和 onStartCommand 中的 startForeground。如果服务已经启动并且在 onCreate 中正确调用了所有内容,我不确定为什么需要这样做。
其他变化:
- 将 serviceId 更改为一些较小的数字 (1-10)
- 通过单例同步 class 较少调用 startFororegroundService(这是在崩溃之前实现的,以防止在它从 onStartCommand 回调开始之前停止服务,但现在如果服务已经启动,它也会过滤调用)。
- 使用 START_REDELIVER_INTENT(应该不会影响任何东西)
仅部分用户在上述手机上出现此问题,因此我怀疑它与三星的一些新更新有关,最终将得到修复
我正在等待我的崩溃报告来分享解决方案。我将近 20 天没有遇到任何崩溃或 ANR。我想分享我的解决方案。可以帮到遇到这个问题的人。
在onCreate()
方法中
- 首先,我的应用程序是一个媒体应用程序。我还没有实施 mediasession。我正在
onCreate()
的顶部创建一个 通知渠道 。 Official doc
- 我在
Context.startForegroundService()
方法之后调用 Service.startForeground()
方法。在我的 prepareAndStartForeground()
方法中。
Note: I don't know why but ContextCompat.startForegroundService() doesn't work properly.
出于这个原因,我手动将相同的功能添加到我的服务 class 而不是调用 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
}
希望此解决方案适合您。
Android Service
组件要正常工作有点棘手,尤其是在 Android 之后的版本中 OS 添加了额外的限制。正如其他答案中提到的,在启动 Service
时,请使用 ContextCompat.startForegroundService()
。接下来,在Service.onStartCommand()
中,立即调用startForeground()
。将要显示的 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
}
总是 return START_STICKY
在你的 Service
中。其他任何东西都可能是错误的,特别是如果你正在做任何类型的音频播放器。事实上,如果你正在做一个音频播放器,你不应该实现你自己的服务,而是使用 MediaBrowserServiceCompat
(来自 AndroidX)。
我也推荐一下我在这上面写的博文:https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd
我几乎已经解决了 MediaSessionCompat.Callback 方法中 startForeground() 的问题,例如 onPlay()、onPause()。
经过与这次崩溃的斗争,我终于完全修复了这个异常并找到了解决方案。
确保在您的服务中完成了这些工作,我将它们列在下面:
(其中一些内容是重复的,正如另一个答案中提到的,我只是重新写了一遍)。
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- 如果您的服务有操作(需要待处理的 Intents),请使用 Broadcast receiver 而不是当前的处理您的 pending intent服务(它会在 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- 始终在 停止您的服务 之前确保您的服务已创建并启动(我创建了一个具有我的服务状态的全局 class)
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
希望对您有所帮助:))
根据此错误消息,当您调用 Context.startForegroundService() 时,您必须使用 Service.startForeground() 方法发出通知。我是这么理解的。
我浪费了很多时间来弄清楚它崩溃的原因,却不明白为什么。问题是 startForeground()
的通知 ID。将其更改为 0 以外的值。这是 运行 Android 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;
}
首先, 我看了这些;
- Context.startForegroundService()没再调用 Service.startForeground()
- Context.startForegroundService() did not then call Service.startForeground
- Android 9 (Pie) Only: Context.startForegroundService() did not then call Service.startForeground() - Works fine on Oreo
- https://issuetracker.google.com/issues/76112072
我有一个近一百万人使用的流媒体应用程序。我正在为播放器使用前台服务。我还没有实现 MediaSession
。我有 99.95% 的无崩溃会话。
所以这个应用程序适用于所有版本,但我开始收到 android 9 的崩溃报告(ANR)。此崩溃仅发生在 Samsung
手机,尤其是 s9, s9+, s10, s10+, note9
型号。
我试过这些,
- 正在
onCreate()
中调用 - 在
Context.stopService()
之前调用 - 类似问题的其他 Whosebug 答案
startForeground()
方法
Service.startForeground()
我读了 Google 开发人员的一些评论,他们说它只是 Intended Behavior
。不知是三星的系统还是AndroidOS出现的。有人对此有意见吗?我该如何解决这个问题?
在用相同的手机遇到同样的问题后,我做了一些改动,崩溃现象消失了。我不确定是什么导致了这个问题,但我猜是调用 onCreate 和 onStartCommand 中的 startForeground。如果服务已经启动并且在 onCreate 中正确调用了所有内容,我不确定为什么需要这样做。
其他变化: - 将 serviceId 更改为一些较小的数字 (1-10) - 通过单例同步 class 较少调用 startFororegroundService(这是在崩溃之前实现的,以防止在它从 onStartCommand 回调开始之前停止服务,但现在如果服务已经启动,它也会过滤调用)。 - 使用 START_REDELIVER_INTENT(应该不会影响任何东西)
仅部分用户在上述手机上出现此问题,因此我怀疑它与三星的一些新更新有关,最终将得到修复
我正在等待我的崩溃报告来分享解决方案。我将近 20 天没有遇到任何崩溃或 ANR。我想分享我的解决方案。可以帮到遇到这个问题的人。
在onCreate()
方法中
- 首先,我的应用程序是一个媒体应用程序。我还没有实施 mediasession。我正在
onCreate()
的顶部创建一个 通知渠道 。 Official doc - 我在
Context.startForegroundService()
方法之后调用Service.startForeground()
方法。在我的prepareAndStartForeground()
方法中。Note: I don't know why but ContextCompat.startForegroundService() doesn't work properly.
出于这个原因,我手动将相同的功能添加到我的服务 class 而不是调用 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
}
希望此解决方案适合您。
Android Service
组件要正常工作有点棘手,尤其是在 Android 之后的版本中 OS 添加了额外的限制。正如其他答案中提到的,在启动 Service
时,请使用 ContextCompat.startForegroundService()
。接下来,在Service.onStartCommand()
中,立即调用startForeground()
。将要显示的 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
}
总是 return START_STICKY
在你的 Service
中。其他任何东西都可能是错误的,特别是如果你正在做任何类型的音频播放器。事实上,如果你正在做一个音频播放器,你不应该实现你自己的服务,而是使用 MediaBrowserServiceCompat
(来自 AndroidX)。
我也推荐一下我在这上面写的博文:https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd
我几乎已经解决了 MediaSessionCompat.Callback 方法中 startForeground() 的问题,例如 onPlay()、onPause()。
经过与这次崩溃的斗争,我终于完全修复了这个异常并找到了解决方案。
确保在您的服务中完成了这些工作,我将它们列在下面: (其中一些内容是重复的,正如另一个答案中提到的,我只是重新写了一遍)。
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- 如果您的服务有操作(需要待处理的 Intents),请使用 Broadcast receiver 而不是当前的处理您的 pending intent服务(它会在 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- 始终在 停止您的服务 之前确保您的服务已创建并启动(我创建了一个具有我的服务状态的全局 class)
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
希望对您有所帮助:))
根据此错误消息,当您调用 Context.startForegroundService() 时,您必须使用 Service.startForeground() 方法发出通知。我是这么理解的。
我浪费了很多时间来弄清楚它崩溃的原因,却不明白为什么。问题是 startForeground()
的通知 ID。将其更改为 0 以外的值。这是 运行 Android 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;
}