Conductor lifecycle

Created on 28 Jun 2017  Â·  15Comments  Â·  Source: bluelinelabs/Conductor

Hi! I made a diagram of the lifecycle, tried putting all the methods in it.
Does it seem correct?

As you can see the diagram is quite large. Sometimes methods don't have a strict order. Probably it should be fixed?

untitled diagram_eng

Would appreciate any comments.

Most helpful comment

If the activity gets killed, the whole process gets killed and you don't
get any callbacks at all. Dont keep Activities is not simulating a process
death.

Kirill Akhmetov notifications@github.com schrieb am Do., 29. Juni 2017,
09:51:

@sockeqwe https://github.com/sockeqwe This happens when the system
kills an activity. I simulated it by enabled "Don't keep activities" in the
developer menu in a phone.

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/321#issuecomment-311890257,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAjnriRl5jgWS2Hlvl7tHOlF4Su4nXblks5sI1dsgaJpZM4OIJOu
.

All 15 comments

This is really cool !

Not sure what you mean with "Activity is going
to be killed" though...

Leonardo Ferrari notifications@github.com schrieb am Mi., 28. Juni 2017,
21:20:

This is really cool !

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/321#issuecomment-311760764,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAjnrvKMyj_oIitjoont-uKt5tGA5FDYks5sIqdfgaJpZM4OIJOu
.

@sockeqwe This happens when the system kills an activity. I simulated it by enabled "Don't keep activities" in the developer menu in a phone.

If the activity gets killed, the whole process gets killed and you don't
get any callbacks at all. Dont keep Activities is not simulating a process
death.

Kirill Akhmetov notifications@github.com schrieb am Do., 29. Juni 2017,
09:51:

@sockeqwe https://github.com/sockeqwe This happens when the system
kills an activity. I simulated it by enabled "Don't keep activities" in the
developer menu in a phone.

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/321#issuecomment-311890257,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAjnriRl5jgWS2Hlvl7tHOlF4Su4nXblks5sI1dsgaJpZM4OIJOu
.

@sockeqwe Are you sure?
If the whole process gets killed, activity gets killed too.
But there is another case when an activity is killed by the system to save resources, but the process is still alive.

https://developer.android.com/reference/android/app/Activity.html

  • If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations.

    • If an activity is completely obscured by another activity, it is stopped. It still retains all state and member information, however, it is no longer visible to the user so its window is hidden and it will often be killed by the system when memory is needed elsewhere.

    • If an activity is paused or stopped, the system can drop the activity from memory by either asking it to finish, or simply killing its process. When it is displayed again to the user, it must be completely restarted and restored to its previous state.

My bad, i thought I have seen the word process death somewhere in your diagram.

Process death is onPause() -> onSaveInstanceState() -> onStop(), and then boom everything is dead, but the activity stack restores the app from where it was, with savedInstanceState != null.

@JuzTosS what issues do you see here that you believe should be fixed?

One thing I'd like to clarify here is that onChangeStarted and onChangeEnded should probably not be included here, or at least not in a way that shows the ordering of these calls in comparison to onAttach and onDetach. There is no guaranteed ordering of these calls for either flow you have shown here. When views are attached and detached is dependent on many things, including whether or not the host Activity is visible and what kind of change handler is being used.

Given that caveat, the only things that are potentially in different call orders here would be onSaveViewState() coming before or after onDetach()

@EricKuck Yea I'm talking exactly about the moments you mentioned.

So I need to investigate more about onChangeStarted and onChangeEnded as you said they can't be compared in order with onAttach and onDetach.

The only one thing left then. It is changing the order of onSaveViewState and onDetach

@JuzTosS

But there is another case when an activity is killed by the system to save resources, but the process is still alive.

Activity documentation lies about this, see https://commonsware.com/blog/2011/10/03/activities-not-destroyed-to-free-heap-space.html

@bejibx Actually this link was in a @sockeqwe comment, for some reason he removed it from the comment.

So where does onContextAvailable(...) come into this diagram? Right before the onCreateView(...)?

Yes, onContextAvailable() is called before onCreateView() (and before
onRestoreInstanceState() afaik)

Siebe Sysmans notifications@github.com schrieb am Do., 12. Okt. 2017 um
11:17 Uhr:

So where does onContextAvailable(...) come into this diagram? Right
before the onCreateView(...)?

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/321#issuecomment-336070881,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAjnrjZNOFRNy-5yHyfnaiyJPUnj81peks5srdksgaJpZM4OIJOu
.

But there is another case when an activity is killed by the system to save resources, but the process is still alive.

Android never kills a particular activity, only a process as a whole. Here is an answer from Dianne Hackbor, Android core team member involved in the "out of memory killer" implementation: stackoverflow.com/a/7576275/1290264

Hey I've noticed a weird behavior related to lifecycle. I'm testing it on a sample app. I've modified NavigationControllerDemo by adding this:

static boolean alreadyReplaced = false;
    @Override
    protected void onChangeEnded(@NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) {
        super.onChangeEnded(changeHandler, changeType);
        if(changeType.isEnter) {
            Log.e("TAG","On Enter Animation Ended" + " isBeingDestroyed: " + isBeingDestroyed() + " index " + index);

            if(!alreadyReplaced) {
                getRouter().replaceTopController(RouterTransaction.with(new NavigationDemoController(index + 1, displayUpMode.getDisplayUpModeForChild()))
                        .pushChangeHandler(new HorizontalChangeHandler())
                        .popChangeHandler(new HorizontalChangeHandler()));

                getRouter().replaceTopController(RouterTransaction.with(new NavigationDemoController(index + 2, displayUpMode.getDisplayUpModeForChild()))
                        .pushChangeHandler(new HorizontalChangeHandler())
                        .popChangeHandler(new HorizontalChangeHandler()));

                alreadyReplaced = true;
            }
        }
    }

    @Override
    public void onDestroy() {
        Log.e("onDestroy", "index "+index);
        super.onDestroy();
    }

Important things to note: I'm using replaceTopController instead of push, and I'm intentionally pushing 2 controllers one after another to force a change before previous animation ended.

Now when I run the app and go to Navigation Demos section I see the following logs:

onDestroy: index 0
On Enter Animation Ended isBeingDestroyed: true index 1
onDestroy: index 1
On Enter Animation Ended isBeingDestroyed: false index 2

And this is a correct behavior. You can see that Conductor first calls onChangeEnded with isBeingDestroyed=true for controller with index=1 and then it calls onDestroy for this controller.

Now we have Controller with index=2 on top. Let's get the app to the background by pressing home button. And when app is in background we can use Terminate Application button in LogCat window or adb kill command to kill app's process. When it's done we go to recents menu and we go back to our app.
Since the process was killed alreadyReplaced boolean will be reset to false and our 2 replace calls will be executed again. Here is what we get in the logs after going back to the app:

On Enter Animation Ended isBeingDestroyed: false index 2
onDestroy: index 2
onDestroy: index 3
On Enter Animation Ended isBeingDestroyed: true index 3
On Enter Animation Ended isBeingDestroyed: false index 4

Let's analyze what just happened. First enter animation of our Controller with index=2 was called, then it was destroyed because of 2 replace calls. Now weird thing happens. You can see that onDestroy of Controller with index=3 is called before it's onChangeEnded. I would expect it to behave the same as above when I first opened the screen. It seems that killing process affects somehow how Router changes the Controllers. Maybe some state is being lost? Afaik Conductor uses retained fragments which are not retained in case of process death.

Is it a bug or an expected behavior? I didn't try to find the root cause yet.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jpetitto picture jpetitto  Â·  4Comments

tomblenz picture tomblenz  Â·  4Comments

lenguyenthanh picture lenguyenthanh  Â·  5Comments

mradzinski picture mradzinski  Â·  5Comments

DJafari picture DJafari  Â·  4Comments