final Context applicationContext = context.getApplicationContext();
Intent intent = new Intent(context, MusicService.class);
applicationContext.bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
if (binder instanceof MusicBinder) {
MusicBinder musicBinder = (MusicBinder) binder;
MusicService service = musicBinder.getService();
if (service != null) {
// start a command such as music play or pause.
service.startCommand(command);
// force the service to run in foreground here.
// the service is already initialized when bind and auto_create.
service.forceForeground();
}
}
applicationContext.unbindService(this);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
然后这是MusicBinder实现:
/**
* Use weak reference to avoid binder service leak.
*/
public class MusicBinder extends Binder {
private WeakReference<MusicService> weakService;
/**
* Inject service instance to weak reference.
*/
public void onBind(MusicService service) {
this.weakService = new WeakReference<>(service);
}
public MusicService getService() {
return weakService == null ? null : weakService.get();
}
}
public class MusicService extends MediaBrowserServiceCompat {
...
private final MusicBinder musicBind = new MusicBinder();
...
@Override
public IBinder onBind(Intent intent) {
musicBind.onBind(this);
return musicBind;
}
...
public void forceForeground() {
// API lower than 26 do not need this work around.
if (Build.VERSION.SDK_INT >= 26) {
Intent intent = new Intent(this, MusicService.class);
// service has already been initialized.
// startForeground method should be called within 5 seconds.
ContextCompat.startForegroundService(this, intent);
Notification notification = mNotificationHandler.createNotification(this);
// call startForeground just after startForegroundService.
startForeground(Constants.NOTIFICATION_ID, notification);
}
}
}
id int:根据NotificationManager.notify(int,Notification)的此通知的标识符;一定不能是0 .
0
我已经研究了几天并得到了解决方案 . 现在在Android O中,您可以设置背景限制,如下所示
正在调用服务类的服务
Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){
SettingActivity.this.startForegroundService(serviceIntent);
}else{
startService(serviceIntent);
}
而服务类应该是这样的
public class DetectedService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
}
// Do whatever you want to do here
}
}
Intent intent = new Intent(context,
YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);
并在onStartCommand中添加了一个检查,以查看它是否实际上已停止:
@Override
public int onStartCommand(Intent intent,
int flags, int startId) {
//call startForeground first
boolean stopService = false;
if (intent != null) {
stopService = intent.getBooleanExtra("request_stop", false);
}
if (stopService) {
stopSelf();
return START_STICKY;
}
//Continue with the background task
return START_STICKY;
}
我甚至在我的Pixel 3 XL上都注意到了这个问题,当时我根本不认为该设备有很多负载 . startForeground() 涵盖了所有代码路径 . 但后来我意识到,在很多情况下,我的服务很快就完成了工作 . I believe the trigger for my app was that the service was finishing before the system actually got around to showing a notification.
变通方法/解决方案
I was able to get rid of all crashes. What I did was to remove the call to stopSelf(). (我正在考虑延迟停止,直到我非常确定通知已显示,但我没有必要 . )当服务闲置一分钟或系统正常销毁它而不会抛出任何异常 .
19 回答
我知道这是一个迟到的答案但是,我认为它将来会有所帮助,我只是使用
JobIntentService
而不是IntentService
,包括它的JobScheduler并为我处理一切 . 看看这个example我有一个解决这个问题的方法 . 我已经在我自己的应用程序(300K DAU)中验证了此修复程序,这可以减少至少95%的此类崩溃,但仍然无法100%避免此问题 .
即使您确保在Google记录的服务启动后立即调用startForeground(),也会出现此问题 . 可能是因为在许多情况下服务创建和初始化过程已花费超过5秒,然后无论何时何地调用startForeground()方法,此崩溃都是不可避免的 .
我的解决方案是确保startForeground()将在startForegroundService()方法之后的5秒内执行,无论您的服务需要多长时间才能创建和初始化 . 这是详细的解决方案 .
就这些 . 希望能帮助到你 . 祝好运 .
来自Google的文档Android 8.0 behavior changes:
解决方案:在
onCreate()
中为Service
调用startForeground()
,使用Context.startForegroundService()
另请参阅:Background Execution Limits for Android 8.0(Oreo)
因为我浪费了太多时间,所以只是抬头 . 即使我在
onCreate(..)
中首先调用startForeground(..)
,我也一直得到这个例外 . 最后我发现问题是由使用NOTIFICATION_ID = 0
引起的 . 使用任何其他值似乎解决了这个问题 .确保所有代码路径都调用startForeground方法,例如代码可能会在服务的onCreate方法上生成一个异常,阻止调用startForeground
Android O API问题26
如果您立即停止服务(因此您的服务实际上并未真正运行(措辞/理解)并且您处于ANR间隔之下,您仍需要在stopSelf之前调用startForeground
https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5
试过这种方法但它仍然造成错误: -
我正在使用它直到错误得到解决
当Service.startForeground(int id, Notification notification)被调用而 id 被设置为0时,在Android 8上也会发生此错误 .
我已经研究了几天并得到了解决方案 . 现在在Android O中,您可以设置背景限制,如下所示
正在调用服务类的服务
而服务类应该是这样的
为什么会出现这个问题是因为Android框架无法保证您的服务在5秒内开始运行,但另一方面框架确实对前台通知有严格限制必须在5秒内触发,而不检查框架是否曾尝试启动该服务 .
这绝对是一个框架问题,但并非所有面临此问题的开发人员都在尽力:
startForeground通知必须同时在onCreate和onStartCommand中,因为如果您的服务已经创建,并且您的活动试图以某种方式再次启动它,则不会调用onCreate .
通知ID不能为0,否则会发生同样的崩溃,即使原因不一样 .
不得在startForeground之前调用
有了以上所有3这个问题可以减少一点但仍然没有修复,真正的修复或让我们说解决方法是将目标sdk版本降级到25 .
请注意,很可能Android P仍会出现这个问题,因为谷歌甚至不知道发生了什么,也不相信这是他们的错,请在这里阅读#36:https://issuetracker.google.com/issues/76112072 .
https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
确保你在onCreate()上调用
Service.startForeground(int, android.app.Notification)
,这样你就可以确保它被调用..如果你有任何条件可能会阻止你这样做,那么你最好使用正常的Context.startService(Intent)
并自己调用Service.startForeground(int, android.app.Notification)
.似乎
Context.startForegroundService()
增加了一个监视器,以确保在它被销毁之前调用Service.startForeground(int, android.app.Notification)
...我打电话给
ContextCompat.startForegroundService(this, intent)
然后开始服务在服务
onCreate
我面临同样的问题,花了很长时间找到解决方案,你可以尝试下面的代码 . 如果您使用
Service
然后将此代码放入onCreate,则使用Intent Service
然后将此代码放入onHandleIntent .我只是在调用
context.startForegroundService(service_intent)
函数之前检查PendingIntent
是否为空 .这对我有用
我一直在研究这个问题,这是我到目前为止所发现的 . 如果我们有类似于此的代码,则可能发生此崩溃:
MyForegroundService.java
MainActivity.java
以下代码块中抛出异常:
ActiveServices.java
此方法在
onCreate()
MyForegroundService
之前执行,因为Android在主线程处理程序上调度服务的创建,但在BinderThread
上调用bringDownServiceLocked
,这是竞争条件 . 这意味着MyForegroundService
没有机会调用startForeground
,这将导致崩溃 .要解决这个问题,我们必须确保在
MyForegroundService
MyForegroundService
之前没有调用bringDownServiceLocked
.通过使用粘性广播,我们确保广播不会丢失,并且
stopReceiver
在onCreate()
MyForegroundService
中注册后立即收到停止意图 . 到这时我们已经打电话给startForeground(...)
. 我们还必须删除该粘性广播,以防止下次通知stopReceiver .Please note 方法
sendStickyBroadcast
已弃用,我仅将其用作临时解决方法来解决此问题 .即使在
Service
中调用startForeground
之后,如果在调用onCreate
之前调用stopService
,它会在某些设备上崩溃 . 所以,我通过使用另一个标志启动服务来修复此问题:并在onStartCommand中添加了一个检查,以查看它是否实际上已停止:
附:如果服务实际上没有运行,它将首先启动服务,这是一个开销 .
如果您调用
Context.startForegroundService(...)
然后在调用Service.startForeground(...)
之前调用Context.stopService(...)
,您的应用程序将崩溃 .我有一个明确的责备:https://github.com/paulpv/ForegroundServiceAPI26/blob/repro/app/src/main/java/com/github/paulpv/foregroundserviceapi26/MainService.kt#L28
我在这上面打开了一个错误:https://issuetracker.google.com/issues/76112072
关于此的几个错误已经打开并关闭将不会修复 .
希望采用清晰的复制步骤进行复制 .
我有一个widget,它在设备清醒时进行相对频繁的更新,我在短短几天内就看到了成千上万的崩溃事件 .
问题触发器
我甚至在我的Pixel 3 XL上都注意到了这个问题,当时我根本不认为该设备有很多负载 .
startForeground()
涵盖了所有代码路径 . 但后来我意识到,在很多情况下,我的服务很快就完成了工作 . I believe the trigger for my app was that the service was finishing before the system actually got around to showing a notification.变通方法/解决方案
I was able to get rid of all crashes. What I did was to remove the call to stopSelf(). (我正在考虑延迟停止,直到我非常确定通知已显示,但我没有必要 . )当服务闲置一分钟或系统正常销毁它而不会抛出任何异常 .
如果您重写要启动的服务w / bindService,这一切都会消失 .
有关如何执行此操作的示例,请参见:https://github.com/paulpv/ForegroundServiceAPI26/tree/bound
我的"repro"分支的差异可见于:https://github.com/paulpv/ForegroundServiceAPI26/compare/repro...bound?expand=1
这么多回答,但没有一个在我的情况下有效 .
我已经开始这样的服务了 .
在我的onStartCommand服务中
并且不要忘记将NOTIFICATION_ID设置为非零
所以一切都很完美,但仍然在8.1崩溃,原因如下 .
我已通过删除通知调用停止前景,但一旦通知删除服务成为后台,后台服务无法从后台运行在android O中 . 推送后开始 .
太神奇了
到目前为止,您的服务崩溃的任何原因都遵循上述所有步骤并享受 .