Android 9.0: не разрешено запускать сервис: приложение находится в фоновом режиме.. после onResume()
У меня есть музыкальный проигрыватель, который пытается запустить Service
в onResume()
Activity
. Я удалил несколько строк для ясности, но код эффективен:
@Override
protected void onResume() {
super.onResume();
startService(new Intent(this, MusicService.class));
}
Согласно журналам сбоев, это исключение на некоторых устройствах под управлением Android P:
Caused by java.lang.IllegalStateException: Not allowed to start service Intent { cmp=another.music.player/com.simplecity.amp_library.playback.MusicService }: app is in background uid UidRecord{6a4a9c6 u0a143 TPSL bg:+3m25s199ms idle change:cached procs:1 seq(1283,1283,1283)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1577)
at android.app.ContextImpl.startService(ContextImpl.java:1532)
at android.content.ContextWrapper.startService(ContextWrapper.java:664)
at android.content.ContextWrapper.startService(ContextWrapper.java:664)
at com.simplecity.amp_library.utils.MusicServiceConnectionUtils.bindToService(SourceFile:36)
at com.simplecity.amp_library.ui.activities.BaseActivity.bindService(SourceFile:129)
at com.simplecity.amp_library.ui.activities.BaseActivity.onResume(SourceFile:96)
Как это возможно, что мое приложение находится в фоновом режиме, сразу после onResume()
(и super.onResume()
)?
Это не имеет никакого смысла для меня. Может ли это быть ошибкой платформы? Все пользователи 3500+, затронутые этой аварией, работают на Android P.
Ответы
Ответ 1
ОБНОВЛЕНИЕ: Это работает для нас в Prod, но это не 100%. Я получил один отчет о сбое за последние полтора месяца, в противном случае было бы более сотни. Пока это не исправлено, это кажется нашим лучшим вариантом на данный момент. Может быть, если бы я поднял время больше 300, то такого падения никогда бы не произошло?
Мы сейчас тестируем это, и похоже, что оно работает. Будет обновляться, как мы видим больше результатов
class ResumingServiceManager(val lifecycle: Lifecycle) : LifecycleObserver {
init {
lifecycle.addObserver(this)
}
val disposable: CompositeDisposable = CompositeDisposable()
fun startService(context: Context, intent: Intent) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent)
} else {
Single.just(true)
.delaySubscription(300, TimeUnit.MILLISECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
context.startService(intent)
}
).addTo(disposable)
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopped() {
disposable.clear()
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
lifecycle.removeObserver(this)
}
}
В onCreate()
инициализируйте его, а затем в любое время, когда вы захотите запустить службу в onResume, просто вызовите resumingServiceManager.startService resumingServiceManager.startService(this, intent)
Он осведомлен о жизненном цикле, поэтому он будет очищать одноразовое устройство, если приостановит отмену запуска onSuccess, когда он может быть на пути к фону с немедленным открытием/закрытием.
Ответ 2
Они исправили сбой (в некоторых будущих выпусках, не уверен, что именно) и предоставили обходной путь на данный момент: https://issuetracker.google.com/issues/110237673#comment9
Ответ 3
Это было помечено как "исправлено" в трекере проблем Android:
Предположительно исправление будет выпущено в одном из выпусков Android Q.
По словам Гуглера, который закрыл вопрос,
Существует обходной путь, чтобы избежать сбоя приложения. Приложения могут получить состояние процесса в Activity.onResume()
, вызвав ActivityManager.getRunningAppProcesses()
и избежать запуска Service, если уровень важности ниже, чем ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
. Если устройство не полностью проснулось, действия будут немедленно приостановлены и, в конце концов, возобновлены после его полного пробуждения.
Ответ 4
Существует обходной путь от Google:
Проблема была решена в будущем выпуске Android.
Существует обходной путь, чтобы избежать сбоя приложения. Приложения могут получить состояние процесса в Activity.onResume(), вызвав ActivityManager.getRunningAppProcesses() и избежать запуска Service, если уровень важности ниже, чем ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND. Если устройство не полностью проснулось, действия будут немедленно приостановлены и, в конце концов, возобновлены после его полного пробуждения.
Поэтому я думаю, что так должно быть:
// hack for https://issuetracker.google.com/issues/113122354
ActivityManager activityManager = ContextCompat.getSystemService(this, ActivityManager.class);
int importance = activityManager.getRunningAppProcesses().get(0).importance;
// higher importance has lower number (?)
if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
URLPlayerService.startActionBroadcastServiceData(PlayerActivity.this);
Я использовал обработчик как обходной путь, и он работает довольно хорошо, но не на 100%:
// hack for https://issuetracker.google.com/issues/113122354
handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200);
Ответ 5
Вы должны принять разрешение переднего плана в android 9 pie, и rest u может создать службу переднего плана с помощью уведомления, и это нужно сделать в течение 5 секунд после запуска службы для Oreo и выше.
@Override
public void onCreate() {
super.onCreate();
this.context = this;
showNotification();
Log.i("onCreate", "completed");
}
private void showNotification() {
Notification notification;
Intent notificationIntent = new Intent(this, BackupsActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
int icon = R.mipmap.ic_launcher;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
icon = android.R.drawable.stat_notify_sync;
if (android.os.Build.VERSION.SDK_INT >= 26) {
//This only needs to be run on Devices on Android O and above
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String id = "id";
CharSequence name = Constants.CHANNEL_BACKUP_RESTORE; //user visible
String description = Constants.CHANNEL_BACKUP_RESTORE_DESCRIPTION; //user visible
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel mChannel = new NotificationChannel(id, name, importance);
mChannel.setDescription(description);
mChannel.enableLights(true);
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[]{0, 1000});
mNotificationManager.createNotificationChannel(mChannel);
notification = new Notification.Builder(getApplicationContext(), id)
.setContentTitle("your title")
.setTicker("process details")
.setContentText("Your text")
.setSmallIcon(icon) .setLargeIcon(Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.app_logo), 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true).build();
} else {
notification = new Notification.Builder(getApplicationContext())
.setContentTitle("TITLE")
.setTicker("ypur sticker")
.setContentText("Text")
.setSmallIcon(icon)
.setLargeIcon(Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.app_logo), 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true).build();
}
startForeground(Constants.CATEGORY, notification);
Log.i("notification", "show");
}
Если таргетинг на пирог, в манифесте добавить:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Вы можете начать обслуживание:
public static void startBackupService(Context context) {
Intent service = new Intent(context, UploadService.class);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
context.startForegroundService(service);
} else {
context.startService(new Intent(service));
}
}
Ответ 6
Может быть, android.arch.lifecycle можно использовать в качестве обходного пути для этой ошибки Android 9?
public class MyActivity extends Activity implements LifecycleObserver {
protected void onResume() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
startService(intent);
} else {
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
} else {
startService(intent);
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onEnterForeground() {
startService(intent);
ProcessLifecycleOwner.get().getLifecycle().removeObserver(this);
}
}
Я нашел его здесь.