Deep understanding of Lifecycle, the cornerstone of Android architecture components

0. Preface

This is the second article in the Android Architecture Components series.
Source code is based on Version 1.1.1

In the previous article, I mentioned that Android Architecture Components (AAC) is a collection of libraries that help developers design robust, testable and maintainable libraries.

Lifecycle is a member of AAC, which can help us manage the life cycle of Activity and Fragment conveniently.

This article will give you an in-depth understanding of Lifecycle.

Note: This article is based on Lifecycle 1.1.1, Android API 26, depending on the following figure.




Assuming that the reader has a basic understanding of Lifecycle, I have drawn a basic class diagram. If you know all the classes involved in the following class diagram, you can continue reading. If you don't know at all, I suggest reading some tutorials first.

1. Lifecycle usage base

In AppCompat Activity, we can get Lifecycle by getLifecycle() method and add Observer to monitor the activity life cycle.

A simple example is as follows:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testLifecycle();
    }

    private void testLifecycle() {
        getLifecycle().addObserver(new LifecycleObserver() {

            @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
            void onResume(){
                Log.d(TAG, "LifecycleObserver onResume() called");
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }
}

Start MainActivity to see the following logs:

D/MainActivity: onResume: 
D/MainActivity: LifecycleObserver onResume() called

The log shows that we have actually implemented the function of monitoring the life cycle through the above code.

So the question arises. How does this work?

I divide the problem into two parts:

  1. Life Cycle Perception Question: What Perceptions of Activity's Life Cycle?
  2. Annotation method invocation problem: What invokes the method we use annotation modification?

2. Principles of Perceptual Life Cycle

2.1 Initial ReportFragment

I found a class called ReportFragment on the debugging stack, which I tracked suspiciously.

Note: Debug view stack is the easiest way to read source code, the best way to use the most people-friendly method, no one, everyone should be skilled.

Let's see what this class says:

public class ReportFragment extends Fragment {

    private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
            + ".LifecycleDispatcher.report_fragment_tag";
        //Method of Injecting Fragment
    public static void injectIfNeededIn(Activity activity) {
        // ProcessLifecycleOwner should always correctly work and some activities may not extend
        // FragmentActivity from support lib, so we use framework fragments for activities
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            // Hopefully, we are the first to make a transaction.
            manager.executePendingTransactions();
        }
    }
    //...
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        dispatchCreate(mProcessListener);
        dispatch(Lifecycle.Event.ON_CREATE);
    }

    @Override
    public void onStart() {
        super.onStart();
        dispatchStart(mProcessListener);
        dispatch(Lifecycle.Event.ON_START);
    }

    @Override
    public void onResume() {
        super.onResume();
        dispatchResume(mProcessListener);
        dispatch(Lifecycle.Event.ON_RESUME);
    }

    @Override
    public void onPause() {
        super.onPause();
        dispatch(Lifecycle.Event.ON_PAUSE);
    }

    @Override
    public void onStop() {
        super.onStop();
        dispatch(Lifecycle.Event.ON_STOP);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        dispatch(Lifecycle.Event.ON_DESTROY);
        // just want to be sure that we won't leak reference to an activity
        mProcessListener = null;
    }
        //Distribution of life cycle events to Lifecycle Registry Owner's Lifecycle or Lifecycle Registry
    private void dispatch(Lifecycle.Event event) {
        Activity activity = getActivity();
        if (activity instanceof LifecycleRegistryOwner) {
            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
            return;
        }

        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }
   //...
}

As we can see from the code, it rewrites the method of life cycle callback. It's really the report Fragment that works. Lifecycle uses Fragment to monitor the life cycle and calls the internal dispatch method in the life cycle callback to distribute life cycle events. (How to distribute it later)

2.2 Backstage SupportActivity

The way to inject Fragment is to call injectIfNeededIn(Activity).

This method was invoked when SupportActivity was found by search. (The API 28 version is ComponentActivity, and the code implementation is no different.)

public class SupportActivity extends Activity implements LifecycleOwner, Component {

    //Have a Lifecycle Registry
    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
          //ReportFragment is injected into onCreate
        ReportFragment.injectIfNeededIn(this);
    }

    @CallSuper
    protected void onSaveInstanceState(Bundle outState) {
        this.mLifecycleRegistry.markState(State.CREATED);
        super.onSaveInstanceState(outState);
    }

    public Lifecycle getLifecycle() {
        return this.mLifecycleRegistry;
    }
}

You can see that SupportActivity contains a Lifecycle Registry, implements Lifecycle Owner, and calls ReportFragment.injectIfNeededIn(this) in the onCreate method; injects ReportFragment.

Lifecycle Registry is an implementation of Lifecycle and is responsible for managing Observer. This class has been seen in the dispatch method in Chapter 2 above. Its handleLifecycle cEvent accepts callbacks from the lifecycle.

2.3 Definition of Lifecycle Life Cycle Events and States

This section supplements Lifecycle's callback and life cycle matching knowledge of Activity and Fragment, which will appear in the following analysis.

Event is defined in Lifecycle to represent life cycle events and State to represent the current state.

2.3.1 Lifecycle.Event 

Lifecycle defines life cycle events similar to Activity life cycle.

    public enum Event {
        ON_CREATE,
        ON_START,
        ON_RESUME,
        ON_PAUSE,
        ON_STOP,
        ON_DESTROY,
        ON_ANY
    }

2.3.2 Lifecycle.State 

State represents the lifecycle state of the current component.

    /**
         * Lifecycle states. You can consider the states as the nodes in a graph and
         * {@link Event}s as the edges between these nodes.
         */
        public enum State {
        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;
        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }

2.3.3 The relationship between Event and State:

(Fig. 1. See [8.2] for the source of the figure.

2.4 Summary

Through research, we find that SupportActivity injects report Fragment into onCreate method, and implements lifecycle monitoring through Fragment mechanism.

In fact, the use of Fragment s to monitor the Activity life cycle has a long history in the open source community. Lifecycle is not original. The emergence of Lifecycle has officialized this.

Compared with third-party implementations, the implementations embedded in Android source code are very beneficial to developers, which shield the details and reduce the difficulty of use.
**

3. Principle of Annotation Method Called

OnLifecycle Event annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnLifecycleEvent {
    Lifecycle.Event value();
}

Seeing the RetentionPolicy.RUNTIME decoration, I guess it is achieved by reflection, but let's see the specific implementation to verify it.

We have seen Lifecycle Registry, the receiver of life cycle events, as well as the handleLifecycle Event, which receives events, and we continue to track them.

    /**
     * Sets the current state and notifies the observers.
     * Note that if the {@code currentState} is the same state as the last call to this method,
     * calling this method has no effect.
     */
    public void handleLifecycleEvent(Lifecycle.Event event) {
        mState = getStateAfter(event);
        if (mHandlingEvent || mAddingObserverCounter != 0) {
            mNewEventOccurred = true;
            // we will figure out what to do on upper level.
            return;
        }
        mHandlingEvent = true;
        sync();
        mHandlingEvent = false;
    }

As you can see from the method annotation, it handles the state and notifies observer.

Look at the getStateAfter() method:

   static State getStateAfter(Event event) {
        switch (event) {
            case ON_CREATE:
            case ON_STOP:
                return CREATED;
            case ON_START:
            case ON_PAUSE:
                return STARTED;
            case ON_RESUME:
                return RESUMED;
            case ON_DESTROY:
                return DESTROYED;
            case ON_ANY:
                break;
        }
        throw new IllegalArgumentException("Unexpected event value " + event);
    }

The getStateAfter() method obtains the corresponding State according to the current Event, which is actually the code implementation of the graph in [2.3.3].

Next, look at the sync() method:

    private void sync() {
        while (!isSynced()) {
            mNewEventOccurred = false;
            // no need to check eldest for nullability, because isSynced does it for us.
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass();
            }
            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass();
            }
        }
        mNewEventOccurred = false;
    }

The sync method compares the current mState with the previous State to see whether it should move forward or backward, which corresponds to the advance and backward of the life cycle. For example, from onResume - > onPause (forward Pass), onPause - > onResume (backward Pass), take backwardPass (backward Pass) for example. (Forward Pass method handles similar)

    private void backwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
                mObserverMap.descendingIterator();
        while (descendingIterator.hasNext() && !mNewEventOccurred) {
            Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
                    && mObserverMap.contains(entry.getKey()))) {
                //Call downEvent to get the earlier Event
                Event event = downEvent(observer.mState);
                pushParentState(getStateAfter(event));
                //Distribution of Event 
                observer.dispatchEvent(lifecycleOwner, event);
                popParentState();
            }
        }
    }
        
    private static Event downEvent(State state) {
        switch (state) {
            case INITIALIZED:
                throw new IllegalArgumentException();
            case CREATED:
                return ON_DESTROY;
            case STARTED:
                return ON_STOP;
            case RESUMED:
                return ON_PAUSE;
            case DESTROYED:
                throw new IllegalArgumentException();
        }
        throw new IllegalArgumentException("Unexpected state value " + state);
    }

As you can see from the source code, the backwardPass() method calls downEvent to get the target Event back.

Maybe more abstract. For example, in the state of onResume, we press home. This is when the state of RESUMED changes to the state of STARTED. The corresponding Event to be sent is ON_PAUSE, which is the logic of backwardPass().

If all the preceding codes are introductory, we finally see a trace of distribution -- observer. dispatchEvent (lifecycle Owner, event).

    static class ObserverWithState {
        State mState;
        GenericLifecycleObserver mLifecycleObserver;

        ObserverWithState(LifecycleObserver observer, State initialState) {
            mLifecycleObserver = Lifecycling.getCallback(observer);
            mState = initialState;
        }

        void dispatchEvent(LifecycleOwner owner, Event event) {
            State newState = getStateAfter(event);
            mState = min(mState, newState);
            //Here
            mLifecycleObserver.onStateChanged(owner, event);
            mState = newState;
        }
    }

You can see that the GenericLifecycle Observer. onStateChanged () method is finally called and followed.

class ReflectiveGenericLifecycleObserver implements GenericLifecycleObserver {
    //mWrapped is our Observer
    private final Object mWrapped;
    //Reflecting mWrapped to get annotated methods
    private final CallbackInfo mInfo;
    @SuppressWarnings("WeakerAccess")
    static final Map<Class, CallbackInfo> sInfoCache = new HashMap<>();

    ReflectiveGenericLifecycleObserver(Object wrapped) {
        mWrapped = wrapped;
        mInfo = getInfo(mWrapped.getClass());
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Event event) {
        invokeCallbacks(mInfo, source, event);
    }
    
    private void invokeCallbacks(CallbackInfo info, LifecycleOwner source, Event event) {
        invokeMethodsForEvent(info.mEventToHandlers.get(event), source, event);
        invokeMethodsForEvent(info.mEventToHandlers.get(Event.ON_ANY), source, event);
    }
  
    private void invokeMethodsForEvent(List<MethodReference> handlers, LifecycleOwner source,
            Event event) {
        if (handlers != null) {
            for (int i = handlers.size() - 1; i >= 0; i--) {
                MethodReference reference = handlers.get(i);
                invokeCallback(reference, source, event);
            }
        }
    }
    //Finally, go to invokeCallback
    private void invokeCallback(MethodReference reference, LifecycleOwner source, Event event) {
        //noinspection TryWithIdenticalCatches
        try {
            switch (reference.mCallType) {
                case CALL_TYPE_NO_ARG:
                    reference.mMethod.invoke(mWrapped);
                    break;
                case CALL_TYPE_PROVIDER:
                    reference.mMethod.invoke(mWrapped, source);
                    break;
                case CALL_TYPE_PROVIDER_WITH_EVENT:
                    reference.mMethod.invoke(mWrapped, source, event);
                    break;
            }
        } catch (InvocationTargetException e) {
            throw new RuntimeException("Failed to call observer method", e.getCause());
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    
    private static CallbackInfo getInfo(Class klass) {
        CallbackInfo existing = sInfoCache.get(klass);
        if (existing != null) {
            return existing;
        }
        existing = createInfo(klass);
        return existing;
    }
    
    //Getting method Information by Reflection
    private static CallbackInfo createInfo(Class klass) {
        //...
        Method[] methods = klass.getDeclaredMethods();

        Class[] interfaces = klass.getInterfaces();
        for (Class intrfc : interfaces) {
            for (Entry<MethodReference, Event> entry : getInfo(intrfc).mHandlerToEvent.entrySet()) {
                verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass);
            }
        }

        for (Method method : methods) {
            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
            if (annotation == null) {
                continue;
            }
            Class<?>[] params = method.getParameterTypes();
            int callType = CALL_TYPE_NO_ARG;
            if (params.length > 0) {
                callType = CALL_TYPE_PROVIDER;
                if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
                    throw new IllegalArgumentException(
                            "invalid parameter type. Must be one and instanceof LifecycleOwner");
                }
            }
            Event event = annotation.value();
            //...
            MethodReference methodReference = new MethodReference(callType, method);
            verifyAndPutHandler(handlerToEvent, methodReference, event, klass);
        }
        CallbackInfo info = new CallbackInfo(handlerToEvent);
        sInfoCache.put(klass, info);
        return info;
    }

    @SuppressWarnings("WeakerAccess")
    static class CallbackInfo {
        final Map<Event, List<MethodReference>> mEventToHandlers;
        final Map<MethodReference, Event> mHandlerToEvent;

        CallbackInfo(Map<MethodReference, Event> handlerToEvent) {
            //...
        }
    }

    static class MethodReference {
        final int mCallType;
        final Method mMethod;

        MethodReference(int callType, Method method) {
            mCallType = callType;
            mMethod = method;
            mMethod.setAccessible(true);
        }
    }

    private static final int CALL_TYPE_NO_ARG = 0;
    private static final int CALL_TYPE_PROVIDER = 1;
    private static final int CALL_TYPE_PROVIDER_WITH_EVENT = 2;
}

This class has a lot of code, but it's not complicated. You can see that the final code goes to invokeCallback(), calling the method through reflection.

This method is a method found in the createInfo() method that reflects the method that traverses our registered Observer and is modified by the OnLifecycle Event annotation and stored in info.mEventToHandlers according to the Event type.

At this point, the whole link is clear. The annotation-modified method we use in Observer will be retrieved by reflection and saved. Then we can find the corresponding Event method when the life cycle changes, and invoke the method by reflection.

Note: There are some details in the source code, such as how to get the method, how to pack Observer, how to manage and store the State, etc., which are not expanded here. Interesting self-understanding.

4. Graphic Lifecycle

If you are stunned by the code, it doesn't matter. I drew class diagrams and sequence diagrams to help you understand. With class diagrams and sequence diagrams to see the code, it will be easy to understand a lot.

4.1 UML diagrams of Lifecycle related principle classes

The core class UML diagrams are arranged as follows:


(Figure 2. Lifecycle-UML diagram)

4.1 Lifecycle Principle Sequence Diagram

Starting from onCreate, the diagram depicts the whole process conveniently with onCreate. (Like other life cycle principles, do not repeat drawing)


(Figure 3. Lifecycle timing chart)

4.3 Relation Diagram between Lifecycle State and Event

The diagram shows the relationship between State and Event, and the changes that take place as the life cycle moves towards them.


(Figure 4. State-Event diagram)

5. The practical application of Lifecycle

Okay, we've finished the analysis of the key principles. If we don't understand it once, we'll see it several times more.

This section talks about Lifecycle's practical applications.

Lifecycle's application scenario is very extensive. We can use Lifecycle's mechanism to help us peel off all business logic related to the life cycle and decouple it completely, such as video pause and playback, Handler message removal, cancellation of network requests, attach & detach Vi of Presenter. EW and so on, and can be achieved in a more elegant way, but also we have a cleaner and readable Activity & Fragment.

Here is a simple example:

5.1 Automatic Removal of Handler Messages: Lifecycle Handler

We worry that Handler will lead to memory leaks. We usually remove messages from onDestroy and write too much. But combined with Lifecyc le, we can write a lifecycle-aware Handler, which automatically removes messages from onDestroy and no longer needs to write that line of boilerplate code.

The code is implemented as follows:

This code is already included in my open source library Pandora and can be accessed: https://github.com/AlanCheen/Pandora Depending directly on usage, welcome star.

5.2 Ability to add Lifecycle to ViewHolder

Some App s have long lists of pages filled with various non-styled Items, usually implemented with Recycler View. Sometimes some Items need to be aware of life cycle events, such as Items containing players need to be aware of life cycle to achieve pause/replay functions, which we can achieve with Lifecycle.

Specific implementation can refer to my open source library Flap: https://github.com/AlanCheen/Flap .

6. Combing and summarizing knowledge points

  1. Lifecycle library perceives the occurrence life cycle by injecting report fragment into onCreate of SupportActivity.
  2. Lifecycle Abstract class, one of the core classes of Lifecycle library, is an abstraction of life cycle, which defines life cycle events and states, through which we can get the current life cycle state, and it also sets the tone of the observer model; (I am a party member, do you see that:-D)
  3. Lifecycle Owner describes a component with a life cycle that can be defined by itself, but usually we don't need to use AppCompatActivity directly.
  4. Lifecycle Registry is the implementation class of Lifecycle, which takes over the life cycle events, and is also responsible for the registration and notification of Observer.
  5. ObserverWithState, an encapsulation class of Observer, finally invokes our annotation-modified method through Reflective Generic Lifecycle Observe.
  6. Lifecycle Observer, an observer of Lifecycle, allows us to enjoy the capabilities that Lifecycle brings.
  7. Reflective Generic Lifecycle Observer, which stores the methods we annotate in Observer and eventually invokes the corresponding methods by reflection when the life cycle changes.

7. Summary

Lifecycle is a library dedicated to life cycle processing, which can help us decouple Acitivity, Framgent's life cycle processing and business logic processing completely, so that we can focus more on business; through decoupling, Activity, Fragment's code is more readable and maintainable.

It can be said that the emergence of Lifecycle has thoroughly solved the life cycle processing problems encountered in Android development, and has also brought new architectural posture to developers, so that we can design a more reasonable architecture.

Mom doesn't have to worry about life cycle problems anymore!

Lifecycle, as the cornerstone of AAC, lays a solid foundation for the appearance of LiveData and ViewModel.

So what's behind LiveData and ViewModel?

Please look forward to the next article!

8. Reference and Recommendation

  1. https://developer.android.com/topic/libraries/architecture
  2. https://developer.android.com/topic/libraries/architecture/lifecycle

<img src="http://ww1.sinaimg.cn/large/9...; style="zoom:25%"/>

Keywords: Android Fragment github network

Added by SulleyMonstersInc on Fri, 23 Aug 2019 13:06:43 +0300