Design 1: Mantis shrimp, let’s go

The introduction

Mantis shrimp, also known as shrimp Gook, are strong in fresh water. The two hammer segments on its head can easily break the shell of the shellfish, and the legs on its body allow for fast movement. These excellent qualities have made it a target for memes.

Glide, as one of the best picture loading frameworks in Android, can bind with Activity and Fragment life cycle, which is the core feature that distinguishes it from other network frameworks and is also the focus of this paper.

Let’s make an analogy with the mantis shrimp meme:

Leathery shrimp and Glide analogy


The framework design

Simple to use

Glide.with(Context).load(String).into(ImageView) Can be used to retrieve images from the network and display them in the ImageView. The main entry for lifecycle binding to a page is Glide. With (Context). According to the general analysis logic should be the first analysis of the source code, before the conclusion, but because of a deep source like the sea, is not conducive to the overall grasp, so the first conclusion. Glide. With (Context) returns a RequestManager, so let’s look at the description of the RequestManager class.

A class for managing and starting requests for Glide. Can use activity, fragment and connectivity lifecycle events to intelligently stop, start, and restart requests. Retrieve either by instantiating a new object, or to take advantage built in Activity and Fragment lifecycle handling, use the static Glide.load methods with your Fragment or Activity.

Thus, this class is used to bind requests to the Activity or Framgent lifecycle.

The class diagram

Diagram the classes associated with the life cycle as follows (omitting most of the classified variables and methods) :

Declare a periodic binding class diagram

A brief introduction to class

  1. RequestManagerRetriever: This class is used to create a RequestManager or locate an existing RequestManager in your Activity or Fragment. RequestManagerRetriever is a singleton.
  2. RequestManagerFragment: inheritance fragments, invisible, is only used to save RequestManager, there will be a SupportRequestManagerFragment inherit the v4 package fragments, similar functions.
  3. LifecycleListener: Used to listen for life cycle events of activities and fragments.
  4. Lifecycle: Used to add a LifecycleListener.
  5. ActivityFragmentLifecycle: Livecycle interface, used to notify the Activity, fragments of life cycle events.
  6. RequestTracker: This class is used to track, cancel, and restart executing, completed, and failed requests.
  7. ConnectivityMonitor: An interface that listens for network events and affects the network request state when the network state changes, inheriting LifecycleListener.
  8. DefaultConnectivityMonitor: ConnectivityMonitor implementation class, realize to monitor the network status of logic.
  9. RequestManagerConnectivityListener: ConnectivityListener interface, pass network events RequestTracker.

Class contact

The above has a simple understanding of various types, and the following will focus on the relationship between various types. The binding throughout the life cycle is divided into four parts.

  1. Call Glide. With (Context) to create a RequestManager based on the Context type passed in. Context can be an Activity, Fragment, or Application.
  2. Add an invisible Fragment to an Activity or Fragment, listen for the life cycle of the invisible Fragment, and pass the event to the RequestManager bound to the Fragment.
  3. The RequestManager listens for life events and responds to manage image requests.
  4. Listen When the network starts up, the RequestManager restarts the image request.

Code reading

According to the above content can be directly with the code can skip the following content, more impressive.

Glide. With (Context)

public static RequestManager with(Context context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }
`Copy the code

The get method to call the RequestManagerRetriever is as follows:

public RequestManager get(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if(Util.isOnMainThread() && ! (contextinstanceof Application)) {
            if (context instanceof FragmentActivity) {  // The Fragment is passed in
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {  // Incoming Acitivity
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) { // Application is passed in
                returnget(((ContextWrapper) context).getBaseContext()); }}return getApplicationManager(context);
    }Copy the code

Taking the Activity type as an example, the code is as follows:

  public RequestManager get(Activity activity) {
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            android.app.FragmentManager fm = activity.getFragmentManager();   / / get FragmentManager
            returnfragmentGet(activity, fm); }}Copy the code

The fragmentGet method is called as follows:

 RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        RequestManagerFragment current = getRequestManagerFragment(fm); 
        RequestManager requestManager = current.getRequestManager();  // Obtain a RequestManager based on a RequestManagerFragment. A RequestManagerFragment contains a RequestManager
        if (requestManager == null) {  // If the RequestManager is empty, create a new one and add it to the RequestManagerFragment
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }Copy the code

GetRequestManagerFragment (FM) functions is based largely on FragmentManager fragments, the code is as follows:

 RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);  // Find by Tag
        if (current == null) { / / is empty, if from the cache lookup pendingRequestManagerFragments
            current = pendingRequestManagerFragments.get(fm);
            if (current == null) { // Create a RequestManagerFragment and add it to the page.
                current = newRequestManagerFragment(); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); }}return current;
    }Copy the code

That’s the part of the code that creates the RequestManager based on the Context type passed in.

Part 2: Listen for the life cycle of the invisible Fragment and pass it on to the RequestManager

The purpose of adding an invisible Fragment is because the Fragment has the same life cycle as the parent Activity. You can monitor the life cycle of the original Activity without changing its code. The code for the RequestManagerFragment lifecycle is as follows:

  @Override
     public void onStart(a) {
         super.onStart();
         lifecycle.onStart(); // Execute onStart on lifecycle
     }

     @Override
     public void onStop(a) {
         super.onStop();
         lifecycle.onStop();// Execute onStop on Lifecycle
     }

     @Override
     public void onDestroy(a) {
         super.onDestroy();
         lifecycle.onDestroy();// Execute onDestroy on lifecycle
     }Copy the code

It can be seen that fragments statement cycle listening and transferring them to the type is ActivityFragmentLifecycle variables corresponding to the method in the lifecycle of the execution. Check ActivityFragmentLifecycle code:

  void onStart(a) {
        isStarted = true;
        // lifecycleListeners are all lifecycleListeners and execute the corresponding onStart
        for(LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStart(); }}// omit the onStop() and onDestroy() methods, similar to onStart().Copy the code

Set how collection LifecycleListener is added, see ActivityFragmentLifecycle the code:

 @Override
     public void addListener(LifecycleListener listener) {
         lifecycleListeners.add(listener);
         if (isDestroyed) {// If the current page is already destroyed, call onDestroy
             listener.onDestroy();
         } else if (isStarted) {// If the current page is already open, call the corresponding onStart
             listener.onStart();
         } else {  // Otherwise call onStoplistener.onStop(); }}Copy the code

The addListener(LifecycleListener Listener) method is a method of the interface Lifecycle. RequestManagerFragment provides a public method:

ActivityFragmentLifecycle getLifecycle(a) {
        return lifecycle;
    }Copy the code

Looking back to part 1 when creating a RequestManager:

  requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());Copy the code

Lifecycle in the RequestManagerFragment is passed to the RequestManager as an argument to the Constructor of the RequestManager. The RequestManager constructor looks like this:

 RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
            RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
        this.context = context.getApplicationContext();
        this.lifecycle = lifecycle;
        this.treeNode = treeNode;
        this.requestTracker = requestTracker;
        this.glide = Glide.get(context);
        this.optionsApplier = new OptionsApplier();
        // A class that listens for network changes
        ConnectivityMonitor connectivityMonitor = factory.build(context,
                new RequestManagerConnectivityListener(requestTracker));

        // If we're the application level request manager, we may be created on a background thread. In that case we
        // cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
        // ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
        if (Util.isOnBackgroundThread()) {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run(a) {
                    // Add the LifecycleListener implemented by the current class to lifecycle in the main thread.
                    lifecycle.addListener(RequestManager.this); }}); }else {
            // Add the LifecycleListener implemented by the current class to lifecycle in the main thread.
            lifecycle.addListener(this);
        }
        lifecycle.addListener(connectivityMonitor);
    }Copy the code

Lifecycle adds the LifecycleListener interface to the RequestManager implementation.

Part 3: RequestManager implementation LifecycleListener

Then look at the RequestManager implementation LifecycleListener method:

 @Override
    public void onStart(a) {
        // onStart might not be called because this object may be created after the fragment/activity's onStart method.
        resumeRequests();
    }

    @Override
    public void onStop(a) {
        pauseRequests();
    }

    @Override
    public void onDestroy(a) {
        requestTracker.clearRequests();
    }Copy the code

Continue to enter resumeRequests (), pauseRequests () and requestTracker clearRequests () method, is called requestTracker corresponding method, The RequestTracker class contains a collection of requests that an Activity obtains from a Fragment. The collection manages image requests based on the lifetime of the RequestManagerFragment.

Part four: Monitor network status and make corresponding

The RequestManager constructor has the following methods:

        // omit some code...
        // A class that listens for network changes
        ConnectivityMonitor connectivityMonitor = factory.build(context,
                new RequestManagerConnectivityListener(requestTracker));
        // omit some code...
       lifecycle.addListener(connectivityMonitor);         
        // omit some code...Copy the code

As you can see from the above code, The ConnectivityMonitor also implements a LifecycleListener. Continue to follow the code found that instance of the factory is ConnectivityMonitorFactory, will check the Internet access in the factory, at the same time create DefaultConnectivityMonitor ConnectivityMonitor examples. The LifecycleListener interface is implemented as follows:

 @Override
    public void onStart(a) {
        register(); // The register () method listens for network changes by registering broadcasts
    }

    @Override
    public void onStop(a) {
        unregister(); // Remove the listening broadcast
    }

    @Override
    public void onDestroy(a) {
        // Do nothing.
    }Copy the code

The broadcast receiver codes are as follows:

  private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            boolean wasConnected = isConnected;
            isConnected = isConnected(context);
            if(wasConnected ! = isConnected) {/ / when the network status changes, just call the listener. OnConnectivityChanged () methodlistener.onConnectivityChanged(isConnected); }}};Copy the code

An instance of ConnectivityListener is of type internal to RequestManager and the code is as follows:

 private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener {
        private final RequestTracker requestTracker;

        public RequestManagerConnectivityListener(RequestTracker requestTracker) {
            this.requestTracker = requestTracker;
        }

        @Override
        public void onConnectivityChanged(boolean isConnected) {
            if (isConnected) { // If the current state is a link state
                requestTracker.restartRequests(); // Restart the image request}}}Copy the code

summary

The above is Glide realization picture loading and Activity, Fragment life cycle binding analysis. You’ll find that the observer and factory modes are decoupled. Creating an invisible Fragment in the Activity or Fragment that needs to be monitored for its life cycle is the best. Next, we will analyze Glide core Design two: image caching. Stay tuned.