Bey*_*zid 14 android android-service foreground-service
首先,我看了看这些;
我有将近一百万人使用的流应用程序。我正在为播放器使用前台服务。我还没有实现MediaSession。我有99.95%的无崩溃会话。因此,该应用程序适用于所有版本,但我开始使用Android 9获取崩溃报告(ANR)。此崩溃仅发生在Samsung手机上,尤其是s9, s9+, s10, s10+, note9型号。
我尝试过这些
startForeground()方法onCreate()Service.startForeground()之前致电Context.stopService()我阅读了Google开发人员的一些评论,他们说这仅仅是Intended Behavior。我想知道这是由三星的系统还是Android操作系统引起的。有人对此有意见吗?我怎样才能解决这个问题?
经过与这次崩溃的太多斗争,我完全修复了这个异常并找到了解决方案。
确保在您的服务中完成了这些工作,我将它们列出如下:(其中一些内容是重复的,如另一个答案中所述,我只是重新写了一遍)。
1- 打电话
开始前景()
在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);
}
Run Code Online (Sandbox Code Playgroud)
2-停止您的服务使用
上下文.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);
}
Run Code Online (Sandbox Code Playgroud)
3-使用启动您的服务
ContextCompat.startForegroundService()
它将处理不同的 API 版本。
ContextCompat.startForegroundService(
context,
NavigationService.getStartIntent(context)
);
Run Code Online (Sandbox Code Playgroud)
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()
)
)
Run Code Online (Sandbox Code Playgroud)
5-始终在停止服务之前确保您的服务已创建并启动(我创建了一个具有我的服务状态的全局类)
if (navigationServiceStatus == STARTED) {
serviceManager.stopNavigationService();
}
Run Code Online (Sandbox Code Playgroud)
6- 将您的notificationId设置为一个长数字,例如 121412。
7- 使用NotificationCompat.Builder将处理不同的 API 版本,您只需要为 Build 版本 >= Build.VERSION_CODES.O. 创建通知通道(这不是解决方案,只是让您的代码更具可读性)
8- 添加
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Run Code Online (Sandbox Code Playgroud)
对您的清单的许可。(android 文档中提到了这个) Android 前台服务
希望能帮助到你 :))
我正在等待我的崩溃报告来分享解决方案。近 20 天我没有遇到任何崩溃或 ANR。我想分享我的解决方案。它可以帮助那些遇到这个问题的人。
在onCreate()方法中
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)
}
}
Run Code Online (Sandbox Code Playgroud)
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)
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的onCreate()
override fun onCreate() {
super.onCreate()
createNotificationChannel()
prepareAndStartForeground()
}
Run Code Online (Sandbox Code Playgroud)
我的onStartCommand()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
return START_STICKY_COMPATIBILITY
} else {
//....
//...
}
return START_STICKY
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
当 onDestroy() 调用时我们需要清除一些东西
override fun onDestroy() {
super.onDestroy()
releaseService()
}
Run Code Online (Sandbox Code Playgroud)
private fun releaseService() {
stopMedia()
stopTimer()
// sth like these
player = null
mContext = null
afChangeListener = null
mAudioBecomingNoisy = null
handler = null
mNotificationBuilder = null
mNotificationManager = null
mInstance = null
}
Run Code Online (Sandbox Code Playgroud)
我希望这个解决方案适合您。
2023 年 1 月 6 日更新
我在我的音乐播放器应用程序中遇到了这个问题
Context.startForegroundService() 然后没有调用 Service.startForeground()
经过大量研究,我找到了一些解决方案。也许这会帮助其他人
解决方案一:
如果您的应用程序面向 API 级别 26 或更高级别,则当应用程序本身不在前台时,系统会对运行后台服务施加限制。如果应用程序需要创建前台服务,则应用程序应调用startForegroundService(). Context.startForegroundService()即使应用程序在后台,系统也允许应用程序调用。但是,应用程序必须startForeground()在创建服务后五秒内调用该服务的方法。
注意:致电您所使用startForeground()的onCreate()服务Context.startForegroundService()
为什么会发生这个问题,因为android框架不能保证你的服务在5秒内启动,但另一方面框架确实有一个严格的限制,前台通知必须在5秒内触发,而不检查框架是否已尝试启动服务startForeground()通知必须同时存在,onCreate()因为onStartCommand()如果您的服务已经创建并且您的活动正在尝试再次启动它,onCreate()则不会被调用。通知ID不能为0,否则即使原因不同也会发生相同的崩溃。
stopSelf之前不得调用startForeground。
后台服务限制:
应用服务有5秒的调用时间,应用在时限内startForeground()没有调用,系统停止服务并声明应用ANR。startForegroudn()
可能性:
startForeground()。onCreate()方法将不会被调用,而是onStartCommand()会被调用。然后将您的逻辑移至onStartCommand()调用startForeground()方法。startForeground()不能为0,否则也会导致崩溃。解决方案2:
startForeground()两者都调用onCreate()and (调用多次onStartCommand()也可以)startForeground()
使用停止您的服务context.stopService()无需致电stopForeground()或stopSelf()
使用 启动您的服务ContextCompat.startForegroundService()。它将处理不同的 API。
如果您的服务有操作(需要挂起的意图),请使用广播接收器而不是当前的服务来处理您的挂起意图(它将调用您的服务onCreate()并且可能很危险,或者使用PendingIntent.FLAG_NO_CREATE),这很好)。练习拥有一个特定的广播接收器来处理您的服务通知操作,我的意思是使用创建所有待处理的意图PendingIntent.getBroadcast()。
一些崩溃报告来自BOOT_COMPLETED与 Oreo 处理相关的问题
基本上一旦你做了startForegroundService(),你就必须做startForeground()。如果您不能startForeground()在服务中执行此操作,那么您最好在广播接收器等中执行此类检查 - 这样您只有在确定会执行 startForeground() 时才启动服务
Handler()像这样添加:
Handler().postDelayed(()>ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);
解决方案3:
使用什么代替前台服务?
JobScheduler现在有JobService,这是前台服务的更好替代方案。这是一个更好的选择,因为:当作业运行时,系统代表您的应用程序持有唤醒锁。因此,您无需采取任何操作来保证设备在作业期间保持唤醒状态。
这意味着您不再需要关心处理唤醒锁,这就是为什么它与前台服务没有什么不同。从实现的角度来看JobScheduler,这不是您的服务,而是系统的服务,大概它将正确处理队列,并且 Google 永远不会终止自己的子服务。
三星已在其三星配件协议 (SAP) 中切换为startForegroundService和JobScheduler。JobService当智能手表等设备需要与手机等主机通信时,这非常有帮助,而该工作确实需要通过应用程序的主线程与用户交互。由于作业由调度程序发布到主线程,因此这成为可能。您应该记住,该作业正在主线程上运行,并将所有繁重的工作卸载到其他线程和异步任务。
解决方案4:
问题是Service.startForeground(id, notification)从服务本身调用,对吧?不幸的是,Android Framework 不保证在 5 秒Service.startForeground(id, notification)内调用Service.onCreate(),但无论如何都会抛出异常,所以我想出了这种方法。在调用之前使用服务中的绑定器将服务绑定到上下文Context.startForegroundService()
如果绑定成功,Context.startForegroundService()则从服务连接调用并立即Service.startForeground()在服务连接内部调用。
重要提示:在 try-catch 中调用该Context.bindService()方法,因为在某些情况下该调用可能会引发异常,在这种情况下,您需要依赖于Context.startForegroundService()直接调用并希望它不会失败。一个示例可以是广播接收器上下文,但是在这种情况下获取应用程序上下文不会引发异常,但直接使用上下文会引发异常。
解决方案5:
如果您先调用Context.startForegroundService(...)然后再调用,您的应用程序将会崩溃。Context.stopService(...)Service.startForeground(...)
信息由 Google 团队提供
这不是一个框架错误;而是一个错误。这是故意的。如果应用程序使用 启动服务实例
startForegroundService(),则它必须将该服务实例转换到前台状态并显示通知。startForeground()如果服务实例在调用之前停止 ,那么该承诺就无法实现:这是应用程序中的一个错误。发布其他应用程序可以直接启动的服务从根本上来说是不安全的。您可以通过将该服务的所有启动操作视为 require 来减轻这种情况
startForeground(),尽管显然这可能不是您想要的。
解决方案6:
我能够摆脱所有崩溃。我所做的就是删除对 的调用stopSelf()。(我正在考虑延迟停止,直到我非常确定通知已显示,但如果没有必要,我不希望用户看到通知。)当服务空闲一分钟或系统正常销毁它而不抛出任何异常。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
} else {
stopSelf();
}
Run Code Online (Sandbox Code Playgroud)
解决方案7:
与前台服务的使用相关的常见崩溃是:
Context.startForegroundService() 然后没有调用 Service.startForeground()
要摆脱这种情况,请使用以下策略:
不要将服务作为前台服务启动,而是将服务作为后台服务启动,绑定到它,然后当您的 Activity/UI 组件中有可用的服务实例时,您可以直接调用服务内的方法,该方法会调用并Service.startForeground()添加通知。
这听起来可能像是一种黑客行为,但想想 Spotify 这样的音乐应用程序是如何启动他们的服务的。这是类似的方法。结果非常出色。使用这种方法,问题计数从一个不可揭示的重要数字减少到 0。
| 归档时间: |
|
| 查看次数: |
2805 次 |
| 最近记录: |