preface

In App development, for the purpose of reducing memory leaks, releasing resources reasonably, and reducing memory footprint, we often need to execute the corresponding code in the component lifecycle callback function, such as this:

class xxActivity extend Activity{
    public void onStart(){
        xxx.init()
    }
    public void onStop(){
        xxx.stop();
    }
    public void onDestoryt(){ xxx.clean(); }}Copy the code

It’s a problem solver but not elegant enough, it’s too coupler, and let’s look at how some of the best frameworks we use all the time deal with this.

1. Lifecycle 1

Using Glide.With (), a series of overloading methods, the component lifecycle is finally sensed and responded to through the organization’s image requests. How did he do it?

1.1. RequsetManagerRetriever Related codes

 public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
Copy the code
  • Glide. With (XXX) transfers are all handed over to a set of get() overloaded methods in the RequsetManagerRetriever; Finally, a RequestManager object is returned
  • The RequestManager is obtained in the RequestManagerRetriever through a series of get () overloaded methods, The arguments to the get () method can be Fragment, SupportFragment, Activity, SupportActivity, View, or Context. The ultimate goal is to extract the FragmentManager from these components as much as possible. Taking the parameter Activity as an example, the code is as follows:
public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else{ assertNotDestroyed(activity); . / / in the Activity to obtain FragmentManager android app. FragmentManager FM = Activity. GetFragmentManager ();returnfragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); }}Copy the code

1.2. Obtain the RequestManager

Get or create fragments using these methods

  @NonNull
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
Copy the code
  • Using getSupportRequestManagerFragment SupportRequestManagerFragment () method.
  • If this is the first time, create a RequestManager and set the Settings to SupportRequestManager. If it is not the first time, directly from SupportRequestManagerFragment.
  • Return the RequestManager object

1.3 Construction of SupportRequestManager

. final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>(); . @NonNull private SupportRequestManagerFragment getSupportRequestManagerFragment( @NonNull final FragmentManager fm, @Nullable Fragment parentHint, Boolean isParentVisible) {/ / find ManagerFragment in fragments SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if(current = = null) {/ / if not found, in the storage of the map attempts to acquire the current = pendingSupportRequestManagerFragments. Get (FM);if(current == null) {// Create a new one if no. current = new SupportRequestManagerFragment(); current.setParentFragmentHint(parentHint);if(isParentVisible) { current.getGlideLifecycle().onStart(); } pendingSupportRequestManagerFragments.put(fm, current); // Remove handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, FM).sendtotarget (); }}return current;
  }
Copy the code

As noted in the notes,

  • If no tag exists in the FragmentManager, return FRAGMENT_TAG
  • In short, RequestManagerTreeNode Is used to obtain the RequestManager bound to all child fragments of the RequestManagerFragment bound to the RequestManagerFragment
  • Note that if the TAG is not found in FragmentManager, it will be fetched from pendingSupportRMF. If not, create a new RMF, place the newly created RMF in pendingSupportRM, and add Transact Ion then removes RMF from pendingSupportRM via handler. Why? because
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
Copy the code

This operation is asynchronous! What is the concept of thinking: this situation

public test(){
    Glide.with(activity).load("xxx1").into(target1); //1 Glide.with(activity).load("xxx1").into(target2); / / 2}Copy the code

Codes 1 and 2 are called assuming that the current activity does not yet have RequestManagerFragemnt, which must execute the process to be created, Code 1 followed to pendingSupportRequestManagerFragments. Put (FM, current); Asynchronously add, that is, use handler to wrap the entire add operation as a Message and send it to the main thread’s messageQueue in Looper rather than immediately wait for looper.looper () to iterate over it and hand it to its handler to handle it. Asynchronous adding occurs when RFM is not added to the Activity, and code 2 is executed. It also creates requesManagerFragments without RFM in the Activity, so there are two requesManagerFragments in the same Activity. So introduced pendingSupportRequestManagerFragments, guaranteed not to have the above situation. RFM is added to the Activity after the asynchronous task, send Handler removed from pendingSupportRequestManagerFragments removed, so as to ensure, before adding success, Can get in pendingSupportRequestManagerFraments RFM.

1.4, SupportRequestManagerFragment

public class SupportRequestManagerFragment extends Fragment { private final ActivityFragmentLifecycle lifecycle; private final RequestManagerTreeNode requestManagerTreeNode = ... @Nullable private SupportRequestManagerFragment rootRequestManagerFragment; @Nullable private RequestManager requestManager; . publicSupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }
  ActivityFragmentLifecycle getGlideLifecycle() {
    returnlifecycle; }... private void addChildRequestManagerFragment(SupportRequestManagerFragment child) { childRequestManagerFragments.add(child); } private void removeChildRequestManagerFragment(SupportRequestManagerFragment child) { childRequestManagerFragments.remove(child); }... . @Override public void onAttach(Context context) { super.onAttach(context); registerFragmentWithRoot(getActivity()); } @Override public voidonDetach() { super.onDetach(); unregisterFragmentWithRoot(); }...Copy the code
  • RequestManagerFragment default in the constructor to create a ActivityFragmentLifecycle types of member variables.
  • In the attach registerFragmentWithRoot (), in onDetach () unregisterFragmentWithRoot ();
  • Lifecycle will be called in onStart (), onStop, in the RequestManagerFragment object
  • Among RequestManager to ActivityFragmentLifecycle registered monitoring object, then RequestManager will react accordingly on component life cycle.
  • In addition, when the Fragment is added to the Fragment as a sub-fragment, you can refer to sub-fragment management

1.5. The RequestManager is aware of the Activity/Fragment lifecycle

public class RequestManager implements LifecycleListener protected final Glide glide; protected final Context context; final Lifecycle lifecycle; private final RequestTracker requestTracker; . RequestManager(...) {... // The RequestManager registers itself into Lifecycle. AddListener (this) in the RequestManagerFragment; lifecycle.addListener(connectivityMonitor); } public synchronized booleanisPaused() {
    return requestTracker.isPaused();
  }
  public synchronized void pauseRequests() {
    requestTracker.pauseRequests();
  }
  public synchronized void pauseAllRequests() {
    requestTracker.pauseAllRequests();
  }
  public synchronized void resumeRequests() { requestTracker.resumeRequests(); } public synchronized void Override public synchronized void Override public synchronized void Override public synchronized void OverrideonStart() {
    resumeRequests();
    targetTracker.onStart();
  }
  @Override
  public synchronized void onStop() { pauseRequests(); targetTracker.onStop(); Override public synchronized void Override public synchronized void Override public synchronized void Override public synchronized voidonDestroy() { targetTracker.onDestroy(); targetTracker.clear(); requestTracker.clearRequests(); lifecycle.removeListener(this); . glide.unregisterRequestManager(this); }}Copy the code
  • The RequestManager implements LifecycleListener and is created with the Lifecycle parameter assigned to the member variable.
  • Requests managed by the RequestManager are stored in the member variable property of the RequestTracker object.
  • The RequestManager senses component Lifecycle changes through Lifecycle. LifecyleListener callback functions implemented in the LifecyleListener state will be executed and the RequestTracker will manage its associated Request objects.

Lifecycle summary in Glide


2. Create a Fragment without interface (SupportRequestManagerFragment/RequestManagerFragment), the Frament without interface has a Lifecycle member variables, Is initialized when a Fragment is created to listen for its life cycle.

3. Add the Frament without an interface to the specified component.

LifecycleListener will be passed into Lifecyle with no interface fragment. The RequestManager will register itself into Lifecycle. This gives the RequestManager the ability to be aware of the component’s lifecycle.

2, RxLifecycle

Analysis of the RxLifecycle principle is based primarily on Trello’s RxLifecycle, github portal point here

2.1. Simple use

In the Activity, for example, the following two methods are used:

bindUntilEvent(@NonNull ActivityEvent event)

bindToLifecycle()
Copy the code

The posture used is like this: When the Activity calls back to the onDestory lifecycle function, it unsubscribes.

 Observable.interval(1, TimeUnit.SECONDS)
                .compose(bindUntilEvent(ActivityEvent.DESTROY))
                .subscribe();
Copy the code

2.2 RxActivity composition

The compose() operator converts the current Observable into another One without breaking the RxJava stream, which feeds in a Transformer object to use. A more detailed description of this operator can be found here

public abstract class RxActivity extends Activity implements LifecycleProvider<ActivityEvent> {

    private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
    @CheckResult
    public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
        return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
    }

    public final <T> LifecycleTransformer<T> bindToLifecycle() {
        returnRxLifecycleAndroid.bindActivity(lifecycleSubject); } protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); lifecycleSubject.onNext(ActivityEvent.CREATE); }... . protected voidonDestroy() { lifecycleSubject.onNext(ActivityEvent.DESTROY); super.onDestroy(); }}Copy the code
  • The Activity has a BehaviorSubject member variable: lifecycleSubject. The BehaviorSubject sends the last value closest to the subscription, and the default value if there is no recent value.
  • BindUntilEvent (ActivityEvent avt) will eventually return a Transformer as an argument to the compose() method.
  • BindUtliEvent method is realized RxActivity LifecycleProvider interface method, the realization of his transfer to the RxLifecycle. BindUntilEvent ()

2.3 LifecycleTransformer build

There are three main methods about Transformer built into the RxlifeCycle class:

    @CheckReturnValue
    public static <T, R> LifecycleTransformer<T> bindUntilEvent(@Nonnull final Observable<R> lifecycle,@Nonnull final R event) { ... Sentenced to emptyreturn bind(takeUntilEvent(lifecycle, event));
    }
Copy the code

Using the filter() operator, get an Observable that filters out incoming specified events and does not distribute other events downstream to an Observer, that is, sends only specified events.

private static <R> Observable<R> takeUntilEvent(final Observable<R> lifecycle, final R event) {
        return lifecycle.filter(new Predicate<R>() {
            @Override
            public boolean test(R lifecycleEvent) throws Exception {
                returnlifecycleEvent.equals(event); }});Copy the code

The Observable obtained in the previous step is actually a BehaviorSubject, which is passed into LifecycleTransformer as a construction parameter

public static <T, R> LifecycleTransformer<T> bind(@Nonnull final Observable<R> lifecycle) {
        return new LifecycleTransformer<>(lifecycle);
    }
Copy the code

2.4 LifecycleTransformer principle

Check out what’s on sale at LifecyclerTranformer

public final class LifecycleTransformer<T> implements ObservableTransformer<T, T>,
                                                      FlowableTransformer<T, T>,
                                                      SingleTransformer<T, T>,
                                                      MaybeTransformer<T, T>,
    ...
   @Override
    public ObservableSource<T> apply(Observable<T> upstream) {
        returnupstream.takeUntil(observable); }... }Copy the code

Upstream: took util () from the Observerable we were using while xxx.compose(). The semantics of takeUtil are


Returns an Observable that emits the items emitted by the source Observable until a second ObservableSource emits an item. Returns an observer source that sends the event until the second observer sends the data.

In summary, when a Transformer object is added to the observed sequence compose, it terminates in the corresponding life cycle function.

2.5 summary

  • A BehaviorSubject within the Activity sends an event to it within the Activity’s lifecycle callback.
  • The main principle is to build LifecycleTransformr through the bindUtilEvent method of LifecycleProvider.
  • The Apply operation in LifecycleTransformer takes an Observable to TakeUtil. The effect of this operation is to interrupt the upstream Observerable send Observer cancel. For example, bindUtilEvent (DESTORY_EVENT) is called, which is used to unlisten the Observer when the original Observer receives the Event.

3, Jetpack Lifecycle

3.1 Lifecycle support is provided in SupportActivity

The SupportActivity class implements the LifecycleOwner structure

  • InjectIfNeedIn () of the ReportFragment is called in the Activity onCreate method
public class SupportActivity extends Activity implements LifecycleOwner { private LifecycleRegistry mLifecycleRegistry =  new LifecycleRegistry(this); protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ReportFragment.injectIfNeededIn(this); } public LifecyclegetLifecycle() {
        return this.mLifecycleRegistry;
    }
Copy the code
  • SupportActivity implements the LifecleOwner interface, which has only one method, geLifecycle();
  • SupportActivity implements this interface by returning its member variable, which is of type LifecycleRegistry.

3.2. ReportFragment Lifecycle Changes

The ability to be aware of the component and distribute events to LifecycleRegistry in the corresponding lifecycle callback function.

# ReportFragment
  public static void injectIfNeededIn(Activity activity) {
        android.app.FragmentManager manager = activity.getFragmentManager();
        if(manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) { manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit(); / / executed immediately rejected the asynchronous manager. ExecutePendingTransactions (); } } private void dispatch(Lifecycle.Event event) { Activity activity = getActivity();if (activity instanceof LifecycleRegistryOwner) {
            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
            return; } / 2 * * * * /if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if(lifecycle instanceof LifecycleRegistry) { ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event); }}}Copy the code

Note 2 place

  • If the Activity implements LifecyclerOwener LifecycleOwner interface is () method returns a Lifecycle.
  • Lifecycle’s implementation class LifecycleRegistry is responsible for managing and distributing Lifecycle state.
  • manager.executePendingTransactions(); Transaction is asynchronous. FragmentManager transactions are performed asynchronously, and commitNow is committed if you want to execute them immediately. ExecutePendingTransactions () will all pending in the queue and your new submission of the transactions are carried out.

3.3. Handle the lifecycle event LifecycleRegistry

Let’s start with the official picture

Jetpack’s management of the component lifecycle has two core concepts, events and states. Their immediate relationship is event-driven state transitions.

There are five states, and how do they transition between them in an event-driven way, as shown in the figure above,

  • INITIALIZED: : This is an initial state.
  • DESTROYED: Death state, ON_DESTORY event driver enters this state.
  • CREATED: CREATED state, ON_CREATE event and STOP event drivers enter this state, ON_STOP event drivers leave this state.
  • STARTED: Starts the state. On_START and ON_PAUSE events drive in and out of the state. ON_RESUME and ON_STOP drivers leave the state.
  • RESUME: the ON_RESUME event driver enters the state, and the ON_PAUSE event driver leaves the state.

In fact, when we learn about an event we know exactly what state it is going to enter in the getStateAfter() method of LifecycleRegistry. This code does not need to be described in words.

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

Copy the code

LifecycleRegistry implements the Lifecyle abstract class, which is the actual manager and distributor of lifecycle listeners and distribution lifecycle events. The method

  • Public void addObserver(LifecycleObserver Observer) inherits Lifecycle from Lifecycle and adds Lifecycle listening.
  • LifecycleObserver Observer public void removeObserver(LifecycleObserver Observer) inherits from Lifecycle and removes a Lifecycle listener.
  • Public void handleLifecycleEvent(Lifecycle.Event Event) notifies listeners of the latest declaration cycle and stores it.

3.3.1 Notify handleLifecycleEvent changes

   public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        State next = getStateAfter(event);
        moveToState(next);
    }
Copy the code

The next state that the component enters can be obtained based on the event, and the listener is notified to change the state: moveToState(Next); In MoveToState (), modify the mState in LifecycleRegistry and then call sync().

   private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
            new FastSafeIterableMap<>();
Copy the code

LifecycleRegistry has a mObserverMap that stores the LifecycleObserver. FastSafeIterableMap is a Map data structure with a LifecycleObserver key and ObserverWithState value.

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); mLifecycleObserver.onStateChanged(owner, event); mState = newState; }}Copy the code

Glide vs RXJava vs Jetpack

  • Glide is a framework for loading images. When we use Glide. With (View/Fragment/View) we get the RequestManager that is aware of the component life cycle and start/pause/end the request in the corresponding life cycle. The core principle of component lifecycle awareness is to put a RequestManagerFragment without an interface into a Request/Activity, and call a method in the corresponding lifecycle callback to notify listeners outside of the registered listener.
  • The core idea of life cycle management in Jetpack is similar to Glide. It is also a ReportFragment that adds an unbounded ReportFragment to the component. The ReportFragment has a life cycle that is synchronized with the component, and thus informs the outside world of the life state, and the listener does the corresponding processing. Glide is concerned about onStart(),onStop() and onDestory(), and it is processed in the appropriate lifecycle callback function. Retry paused or failed requests in onStart(), cancel ongoing requests in onStop, Release resources in Destory, etc. Jetpack listens on onCreate, onStart, onResume, onPause, onStop, and onDestroy to provide more comprehensive status notification to listeners.
  • The use of the Compose and TakeUnitl operators in RxLifecycle, which tell an operation to terminate at a specified lifecycle state, is thin compared to the lifecycle management in Glide and Jetpack, Also, if you want to use it directly, you need to integrate RxActivity or RxFragment. Of course, you can also implement the BehaviorSubject itself, but generally it is very intrusive. The problem with the takeUtil operator is that an onComplete or onError event is still sent when the operation is terminated, which is not an elegant implementation for cases where there is business processing in either method. Therefore, WE can see that Zhihu team based on the concept of interface Fragment sensing component life cycle, and implemented a set of RxLifecycle interested can refer to Zhihu RxLifecycle

Give us some thoughts

  • How does the takeUntil() operator abort internally?
  • How does Glide sense network changes and process them in addition to sensing component life cycles?
  • Specific applications for component lifecycle listening in Jetpack are shown in both LiveData and ViewModule. How do they work?
  • In addition, LifecycleListener in Jetpack can also use annotations to call back execution. How does that work?
  • and so on

I’ll do that next time