Glide is now Android development commonly used a picture loading tool, can be based on the resource file, network URL request picture, and set to the control. And a set of complete cache reuse mechanism, can greatly save memory.

Traditional Android image request

We first need to request the image through the network request tool, store the image in a Bitmap object in the form of Stream, and then set the image through setBitmap(Bitmap).

Thread(Runnable {val bitmap = getBitmap(sampleWallPaper)) RunOnUiThread {glide_out.setimageBitmap (Bitmap)}}).start()Copy the code

In this case, there are network requests, network connections, and so on scattered throughout the code, as well as the life cycle impact on loading.

The use of the Glide

Glide image request with no configuration is simple:

Glide.with(context)
     .load(url)
     .into(imageView)
Copy the code

Glide has three main steps, with is the binding life cycle, load is to load the picture, into is to set the picture. Next, we will analyze it from these three aspects.

With

With Context (Context) method

In Glide. Java, the With method has several overloaded methods, including Context, Activity, Fragment, and so on. Either the fragment.getContext() or the view.getContext () method inside the Fragment being called.

We open the basic with(context) method:

  @NonNull
  public static RequestManager with(@NonNull Context context) {
	    return getRetriever(context).get(context);
  }
Copy the code

This static method passes in a runtime Context object and returns a RequestManager, partially defined as follows:

//RequestManager.java public class RequestManager implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {// delete protected final Glide Glide; protected final Context context; @SuppressWarnings("WeakerAccess") @Synthetic final Lifecycle lifecycle; / / omit}Copy the code

RequestManagerc holds references to Glide, Context, and LifeCycle, which are familiar but LifeCycle may not be:

public interface Lifecycle {
  void addListener(@NonNull LifecycleListener listener);
  void removeListener(@NonNull LifecycleListener listener);
}
Copy the code

It is an interface, and there are only two interface methods: 1. Add listener, 2. Remove listeners. LifeCycleListener’s definition is more interesting:

//package com.bumptech.glide.manager;
public interface LifecycleListener {
  void onStart();
  void onStop();
  void onDestroy();
}
Copy the code

These three interface methods have the same name as the three lifecycle methods in activities or fragments. Let’s look at one of the onStart() method calls:

Back in RequestManager.java, it is clear that the RequestManager controls the lifecycle and the pause relationship between requests through these three methods.

@Override public synchronized void onStart() { resumeRequests(); targetTracker.onStart(); } @Override public synchronized void onStop() { pauseRequests(); targetTracker.onStop(); } @Override public synchronized void onDestroy() { targetTracker.onDestroy(); for (Target<? > target : targetTracker.getAll()) { clear(target); } targetTracker.clear(); requestTracker.clearRequests(); lifecycle.removeListener(this); lifecycle.removeListener(connectivityMonitor); Util.removeCallbacksOnUiThread(addSelfToLifecycle); glide.unregisterRequestManager(this); }Copy the code

There is a variable, targetTracker, which itself implements LifecycleListener.

private final TargetTracker targetTracker = new TargetTracker();
Copy the code

Back to the with method:

  @NonNull
  public static RequestManager with(@NonNull Context context) {
	    return getRetriever(context).get(context);
  }
Copy the code

Let’s look at the getRetriever() method, which defines:

  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }
Copy the code

This CheckNull means: You can’t call a View that hasn’t been attached or a Fragment that returns NULL when calling getActivity (usually before Fragement isAttached state or after destruction).

In other words, in a Fragment, we can only call between the onAttach() and onDetach() states.

LifeCycle will be used to create a RequestManager. However, when constructing RequestManager, different context will have different construction methods and ideas. Here is not to expand, the end of the article will do analysis and comparison. The following processes use the Activity as the Context, not the ApplicationContext.

  • What is the difference between ApplicationContext and normal Activity.context?

Context is the Context in which the current code is running. ApplicationContext is a Context within the entire Application, but there is no ApplicationContext class. In general, uI-related Context operations should be handled using the Activity Context; Some other operation, Service, Activity, Application instance can, of course, pay attention to the Context reference to hold, to prevent a memory leak.

See the difference between Context and ApplicationContext

In the get method, we branch based on the Context’s type. We select the Activity branch to go to:

@NonNull public RequestManager get(@NonNull Activity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else if (activity instanceof FragmentActivity) { return get((FragmentActivity) activity); } else { assertNotDestroyed(activity); frameWaiter.registerSelf(activity); android.app.FragmentManager fm = activity.getFragmentManager(); //**** return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); / / * * * *}}Copy the code

It starts out as a branch judgment, with different cases being handled by overloaded functions on different branches.

  • Glide -image-loading-with-application-context

If we use Glide on our Fragment and use the ApplicationContext, when our Fragment is removed. Glide will still load the image, or even set it into the ImageView, and it will eventually be recycled by the GC algorithm and not processed based on the Fragment lifecycle.

Note the annotated lines that are key to Glide’s binding to the host life cycle. The first line gets the FragmentManager for the Fragment of the Activity. In getRequestManagerFragment (fragments: fragments) the implementation of the method, these two lines are as follows:

android.app.FragmentManager fm = fragment.getChildFragmentManager();
return fragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
Copy the code

The second line of the fragmentGet method has the following method body:

  private RequestManager fragmentGet(
  @NonNull Context context,
  @NonNull android.app.FragmentManager fm,
  @Nullable android.app.Fragment parentHint,
  boolean isParentVisible) {
	    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
	    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);
	      // This is a bit of hack, we're going to start the RequestManager, but not the
	      // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
	      // Lifecycle might trigger memory leaks. See b/154405040
	      if (isParentVisible) {
	        requestManager.onStart();
	      }
	      current.setRequestManager(requestManager);
	    }
	    return requestManager;
  }
Copy the code

RequestManagerFragment = RequestManagerFragment = RequestManagerFragment = RequestManagerFragment = RequestManagerFragment

Public class RequestManagerFragment extends Fragment {// omitted}Copy the code

Then create the RequestManager through the Builder mode and pass in the Fragment’s lifecycleFragments and RequestManagerI made a connection. RequestManagerFragment will hold a ActivityFragmentLifecycle type, called the LifeCycle of the variable, the variable can callback method in RequestManager. When a Fragment is attached to an Activity, the three life-cycle callback methods in the Fragment of the blank UI can be called back:

With Section Summary

Let’s summarize how Glide is tied to the life cycle. First, three basic questions should be clarified: 1. How does Glide sense the life cycle of the bound Activity? 2. How does Glide pass lifecycle hook functions? 3. What is the process of Glide binding?

  • The answer to the first question is that Glide builds a UI-less Fragment, attaches it to the Activity that it needs to listen on, and is aware of both the Activity and Fragment lifecycle.

  • Second question: no UI fragments of Glide, hold a lifecycle reference, ActivityFragmentLifecycle is Glide, internal contains a data structure:

    	private final Set<LifecycleListener> lifecycleListeners = 
    Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
    Copy the code

We do different things at different stages of our life cycle:

void onStart() { isStarted = true; for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStart(); } } void onStop() { isStarted = false; for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStop(); } } void onDestroy() { isDestroyed = true; for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onDestroy(); }}Copy the code

The observer mode is used here, and the three onXXX methods are events. Once an event occurs, all consumers who have subscribed will be notified to come out and consume the event. LifecycleListeners has a value added to the addListener method:

@Override public void addListener(@NonNull LifecycleListener listener) { lifecycleListeners.add(listener); if (isDestroyed) { listener.onDestroy(); } else if (isStarted) { listener.onStart(); } else { listener.onStop(); }}Copy the code

This addListener is called in the constructor of the RequestManager:

  • For the third problem, the binding process is shown as follows:

Added: Lifecycle creation issues.

If the Context we pass in the get method is ApplicationContext, we will create a new one like this: new ApplicationLifecycle() :

Factory. The build (glide, new ApplicationLifecycle (), / / a new ApplicationContext new EmptyRequestManagerTreeNode (), context.getApplicationContext());Copy the code

If we passed is the Context of the other, then will direct call. Current GetGlideLifeCycle () :

  factory.build(
          glide, 
          current.getGlideLifecycle(), 
          current.getRequestManagerTreeNode(), 
          context);
Copy the code

This kind of situation will return to the current LifeCycle, the current is in getSupportRequestManagerFragment () will be blank UI fragments on the host before, through the constructor to create:

public SupportRequestManagerFragment() { this(new ActivityFragmentLifecycle()); } @NonNull private SupportRequestManagerFragment getSupportRequestManagerFragment( @NonNull final FragmentManager fm, @Nullable Fragment parentHint) { SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { current = pendingSupportRequestManagerFragments.get(fm); if (current == null) { current = new SupportRequestManagerFragment(); / / if not here to create a current setParentFragmentHint (parentHint); pendingSupportRequestManagerFragments.put(fm, current); / / deposit pendingSupportRequestManagerFragments FM. BeginTransaction (). The add (current, FRAGMENT_TAG). CommitAllowingStateLoss (); handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; }Copy the code

LifeCycle will not be the same as StackOverflow. LifeCycle will not be the same as StackOverflow. ApplicationLifeCycle only processes add and remove events:

class ApplicationLifecycle implements Lifecycle { @Override public void addListener(@NonNull LifecycleListener listener)  { listener.onStart(); } @Override public void removeListener(@NonNull LifecycleListener listener) { // Do nothing. } }Copy the code

Since the life cycle of an Application is the App itself, there is naturally no need to deal with so many onStart, onStop and other methods. Once the App exits or is reclaimed, naturally those objects will also be reclaimed.So, if you pass in a different Context, you implement it differently,Running BackGroundThread or passing ApplicationContext does not generate a blank Fragment. (The main reason for the former is that if executed on a child thread, the applicationContext is passed in by default). Using ApplicationContext makes Glide’s life cycle as long as the App’s.

Reference source
  • How is Glide tied to the lifecycle? What’s the difference between different contexts?
  • Android [Hand-torn Glide] — How is Glide associated with the life cycle?