Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android 9.0: Not allowed to start service: app is in background.. after onResume()

I've got a music player which attempts to start a Service in onResume() of an Activity. I've removed a few lines for clarity, but the code is effectively:

@Override protected void onResume() {     super.onResume();      startService(new Intent(this, MusicService.class)); } 

According to the crash logs, this is throwing an Exception on some devices running 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) 

How is it possible that my app is in the background, immediately after onResume() (and super.onResume()) is called?

This doesn't make any sense to me. Could this be a platform bug? All 3500+ users affected by this crash are on Android P.

like image 339
Tim Malseed Avatar asked Aug 25 '18 02:08

Tim Malseed


2 Answers

There is a workaround from Google:

The issue has been addressed in future Android release.

There is a workaround to avoid application crash. Applications can get the process state in Activity.onResume() by calling ActivityManager.getRunningAppProcesses() and avoid starting Service if the importance level is lower than ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND. If the device hasn’t fully awake, activities would be paused immediately and eventually be resumed again after its fully awake.

So I think it should like that:

// 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);     } 

I have used handler as a workaround and it works pretty good but not 100%:

// hack for https://issuetracker.google.com/issues/113122354    handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200); 
like image 98
Mateusz Kaflowski Avatar answered Sep 21 '22 22:09

Mateusz Kaflowski


UPDATE: This is working for us in Prod, but it's not 100%. I have received one crash report over the past month and a half when there would have been well over a hundred otherwise. Until this is properly fixed, this seems like our best option for now. Maybe if I raised the time beyond 300 that one crash would never have happened?

We're testing this out right now which so far seems to be working. Will update as we see more results

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 it and then anytime you want to start a service in onResume just call resumingServiceManager.startService(this, intent)

It's lifecycle aware so it will clear the disposable if it pauses cancelling the onSuccess from triggering when it might be on the way to the background with an immediate open/close.

like image 28
Ben987654 Avatar answered Sep 19 '22 22:09

Ben987654