简体   繁体   中英

onStop being called after onDestroy?

I've encountered a rather strange issue with Activity lifecycle.

Short prequel:

The first symptom that I've found is that it crashed with IllegalArgumentException when I was trying to unregister receiver in onStop after registering it in onStart .

After dumping the full list of active receivers (some reflection magic), I've found that my receiver is not the list. It was either being removed somewhere else by mistake, or it was removed during onDestroy call of Activity where Context is being cleaned (during onDestroy ActivityThread calls ContextImpl#performFinalCleanup , which then calls LoadedApk#removeContextRegistrations ).

What is happening?

After adding some more analytics info to the crash, I found out that while the crash is happening in onStop , the Activity is in a pretty weird state - it's isDestroyed() call returns true , it's isFinishing() returns false , and it's getLifecycle().getCurrentState() returns DESTROYED ...

Checking normal onStop() (without crash) call shows that the Activity is in this state:

isDestroyed() is false , and getLifecycle().getCurrentState() is CREATED in onStop .

So I drew the conclusion that onStop is being called after onDestroy , which I thought is impossible, but it seems to happen.

And onStop is definitely not being called manually by something else in the app, since this is the stack trace of where the onStop is called from.

com.myapp.TheActivity.onStop (TheActivity.java:217)
android.app.Instrumentation.callActivityOnStop (Instrumentation.java:1474)
android.app.Activity.performStop (Activity.java:8189)
android.app.ActivityThread.callActivityOnStop (ActivityThread.java:4994)
android.app.ActivityThread.performStopActivityInner (ActivityThread.java:4967)
android.app.ActivityThread.handleStopActivity (ActivityThread.java:5047)
android.app.servertransaction.TransactionExecutor.performLifecycleSequence (TransactionExecutor.java:233)
android.app.servertransaction.TransactionExecutor.cycleToPath (TransactionExecutor.java:201)
android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:173)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:97)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2220)
android.os.Handler.dispatchMessage (Handler.java:107)
android.os.Looper.loop (Looper.java:237)
android.app.ActivityThread.main (ActivityThread.java:8016)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1076)

The reason is unclear, but, it is getting more and more attention recently. And it happens in both onResume/onPause and onStart/onStop pairs to call register/unregister. About those lifeCycle checks, make sure you have on Instance of the activity with hashCode() or something. Anyway, to fix the issue the best practice is to wrap register/unregister calls in try/catch block:

private void registerBroadcastReceiver() {
    try {
        appUpdateReceiver = new AppUpdateReceiver();
        registerReceiver(appUpdateReceiver, appUpdateIntentFilter);
    } catch (IllegalArgumentException e) {
        // already registered
    }

}

private void unregisterBroadcastReceiver() {
    try {
        unregisterReceiver(appUpdateReceiver);
    } catch (IllegalArgumentException e) {
        // already unregistered
    }
}

I am not entirely sure why that happens in the first place, but I've encountered something similar on one of the Xiaomi devices that did not happen on any of the emulators (or my device), but happened on somebody else's phone. onStop() was called 5 seconds after the app was moved to background, but onPause() was called immediately. More weird issue, if in between these 5 seconds, I open the app from task manager, onStart() was called. (Notice the 2 onStart() calls but not a singular onStop() call.) I think it would have been possible if the activity was destroyed, onDestroy() would be called before onStop() . As an alternative, you might try to move the receiver registration calls to onResume() and onPause() instead of onStart() and onStop() .

Second: I don't exactly use lifecycle of the activity when it comes to checking resumed or paused state, but I'd suggest overriding all of those methods (onPause, onResume etc...), storing the state on boolean variables and logging the state while checking the actual calls. Maybe that will result in a different state since this is a pretty unusual check.

If it does not happen on other activities, in that activity, maybe something is posting so many callbacks to the main message looper that prevents onStop() from getting called in the first place because of the overflow, causing this exception to happen. These are only assumptions, of course, but I'd be grateful if they helped in a way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM