Ich habe einen Musik-Player, der versucht, ein Service
In onResume()
eines zu starten Activity
. Ich habe aus Gründen der Übersichtlichkeit einige Zeilen entfernt, aber der Code ist effektiv:
@Override
protected void onResume() {
super.onResume();
startService(new Intent(this, MusicService.class));
}
Laut den Absturzprotokollen löst dies auf einigen Geräten mit Android P eine Ausnahme aus:
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)
Wie ist es möglich, dass sich meine App unmittelbar nach dem Aufruf von onResume()
(und super.onResume()
) im Hintergrund befindet?
Das ergibt für mich keinen Sinn. Könnte dies ein Plattformfehler sein? Alle über 3500 Benutzer, die von diesem Absturz betroffen sind, sind auf Android P.
android
android-service
android-9.0-pie
Tim Malseed
quelle
quelle
Antworten:
Es gibt eine Problemumgehung von Google:
Also ich denke es sollte so aussehen:
// hack for https://issuetracker.google.com/issues/113122354 List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses(); if (runningAppProcesses != null) { int importance = runningAppProcesses.get(0).importance; // higher importance has lower number (?) if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) URLPlayerService.startActionBroadcastServiceData(PlayerActivity.this); }
Ich habe den Handler als Problemumgehung verwendet und er funktioniert ziemlich gut, aber nicht zu 100%:
// hack for https://issuetracker.google.com/issues/113122354 handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200);
quelle
UPDATE: Dies funktioniert für uns in Prod, aber es ist nicht 100%. Ich habe in den letzten anderthalb Monaten einen Absturzbericht erhalten, in dem es sonst weit über hundert gegeben hätte. Bis dies richtig behoben ist, scheint dies unsere derzeit beste Option zu sein. Vielleicht wäre ein Absturz nie passiert, wenn ich die Zeit über 300 hinaus erhöht hätte?
Wir testen dies gerade, was bisher zu funktionieren scheint. Wird aktualisiert, sobald wir weitere Ergebnisse sehen
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) } }
In
onCreate()
initialize es und will dann , wann immer Sie einen Dienst in onResume nur Anruf startenresumingServiceManager.startService(this, intent)
Es ist lebenszyklusabhängig, sodass das Einwegprodukt gelöscht wird, wenn der onSuccess nicht mehr ausgelöst wird, wenn er möglicherweise mit einem sofortigen Öffnen / Schließen in den Hintergrund tritt.
quelle
handler.postDelayed({context.startService(intent)}, 300)
und in derstopped()
Berufunghandler.removeCallbacksAndMessages(null)
?Dies wurde im Android Issue Tracker als "behoben" markiert:
Vermutlich wird das Update in einer der Android Q-Versionen veröffentlicht.
Laut dem Googler, der die Ausgabe geschlossen hat,
quelle
Unser Team hatte das gleiche Problem. Mein Kalender zeigt den 5. April 2019 an, aber das Problem wird immer noch auf meinem Samsung Galaxy S9 +, Android 9.0 (One UI) reproduziert.
Wir starten den Service in onResume und lösen die Bindung von onPause.
Wie zu reproduzieren
Sperren Sie einfach Ihr Gerät, wenn Aktivitäten mit dieser Logik auf dem Bildschirm angezeigt werden, und berühren Sie 10-15 Minuten nicht. Nach dem Entsperren stürzt die App ab.
Wie repariert man
Wir haben eine Lösung gefunden, die tatsächlich funktioniert. Starten Sie seit Android 8+ Ihren Dienst in android.os.Handler.post (...)
Beispiel (Kotlin):
override fun onResume() { super.onResume() Handler().post { val serviceIntent = Intent(activity, SomeService::class.java) activity?.startService(serviceIntent) activity?.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE) } }
Viel Glück!
quelle
Es scheint, dass die Fehlermeldung nicht eindeutig ist, und ich habe festgestellt, dass sich der letzte Teil der Nachricht auf die Anwendung bezieht, in der sich der Dienst befindet, und NICHT auf die Anwendung, die versucht, den Dienst zu starten und an ihn zu binden.
Der Fehler, den ich erhalte, wenn die Anwendung des Dienstes nicht im Vordergrund steht (obwohl der Dienst selbst ein Hintergrunddienst ist):
W/System.err: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.mycompany.myserviceapp/.MyService }: app is in background uid null at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505) at android.app.ContextImpl.startService(ContextImpl.java:1461) at android.content.ContextWrapper.startService(ContextWrapper.java:644)
Wenn ich nun die Anwendung mit dem Dienst ausführe, sodass sie unmittelbar vor dem Start der "Client" -App im Vordergrund steht, die versucht, den Dienst zu starten, funktioniert alles gut für den Dienststart.
Wenn es sich bei dem Dienst um einen "Hintergrunddienst" handelt und die App für den Dienst nicht im Vordergrund steht, wird der Dienst möglicherweise beendet. Daher reicht es nicht aus, den Dienst zu starten, wenn Sie ihn benötigen, um in der Nähe zu bleiben.
Dies scheint ein Ergebnis der Änderungen von Android 7 zu Android 8 zu sein, bei denen in Oreo begonnen wurde, die Dinge ein wenig einzuschränken.
Alles wird hier in den Android-Dokumenten zu den Hintergrundausführungsgrenzen von Oreo erklärt
In den Dokumenten wird empfohlen, Ihre Logik auf geplante Jobs zu migrieren oder den Dienst zu einem Vordergrunddienst zu machen. Dort wird visuell angezeigt, dass der Dienst ausgeführt wird.
quelle
Vielleicht könnte android.arch.lifecycle als Workaround für diesen Android 9-Fehler verwendet werden?
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); } }
Ich habe es hier gefunden.
quelle
startService()
Anruf zu verschiebenonStart()
. Wie Sie jedoch sagen, könnte dies eine praktische Problemumgehung sein.Lifecycle.State.STARTED
. Ich werde es versuchenLifecycle.State.RESUMED
und@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
das wird danach aufgerufenonResume()
und sehen, ob das das Problem behebt.startService
Anrufs inonStart()
oderonResume()
oder wo auch immer, es sei denn, Sie berücksichtigen sehr winzige Zeitverzögerungen. Klingt wie der Anruf zu bewegenonStart()
ist nicht eine Abhilfe.