Like attention, no more lost, your support means a lot to me!

🔥 Hi, I’m Chouchou. GitHub · Android-Notebook has been included in this article. Welcome to grow up with Chouchou Peng. (Contact information at GitHub)

preface

  • Picture module is a very important component in App, and Glide, as a solution recognized by both the official and the industry, its learning value is needless to say;
  • In this article, I will analyze Glide life cycle management, which is divided into three levels of life cycle: Activity & Network & Memory. Please be sure to like and follow if you can help, it really means a lot to me.

Related articles

  • The Android | interview will ask Handler, you sure don’t look at it?”

Tip: This article source based on Glide 4.11

directory

1. An overview of the

Loading images using Glide is very simple, something like this:

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

Unloading, on the other hand, is simple, like this:

Glide.with(activity).clear(imageView)
Copy the code

It is generally accepted that unnecessary load requests should be cancelled in a timely manner, but this is not required. Glide automatically unloads or reloads when the page life cycle/network changes.

  • Page life cycle

Suspend requests when the page is not visible Resume the request when the page is visible; Destroy the request when the page is destroyed. In Section 2, I will analyze Glide’s life cycle module in detail, mainly including two main bodies of Activity/Fragment.

  • Network Connection Status

If an image is loaded from a URL, Glide listens for the connection status of the device and restarts the previously failed request when it reconnects to the network. In section 3, I will analyze Glide’s network connection status monitoring module in detail

  • Memory state

Glide listens to memory status and releases memory at different levels. In Section 4, I examine Glide’s memory status monitoring module in detail

2. Activity/Fragment lifecycle monitoring

2.1 Why listen for the page life cycle?

Mainly based on the following two purposes:

  • To ensure that visible activities/fragments in the foreground are processed first to improve resource utilization;

  • Free resources when necessary to avoid being killed while the application is in the background, improving stability.

Low Memory Killer will kill the process at the right time. The priority is: empty process -> background process -> service process -> visible process -> foreground process.

2.2 Three lifecycle scopes

First, let’s start with Glide’s entry method:

Glide.java

private final RequestManagerRetriever requestManagerRetriever; Public static RequestManager with(Context Context) {return getRetriever(Context).get(Context); Public static RequestManager with(Activity Activity) {return getRetriever(Activity).get(Activity); } omit similar methods whose parameters are FragmentActivity, Fragment, View... Private static RequestManagerRetriever getRetriever(Context Context) { Glide. Get (context) based on DCL singleton return Glide. Get (context). GetRequestManagerRetriever (); } public RequestManagerRetriever getRequestManagerRetriever() { return requestManagerRetriever; }Copy the code

Can be seen, with(…) The return value of the method is RequestManager, and the actual place to create it is in RequestManagerRetriever#get(…). In the.

< span style = “box-sizing: border-box; color: RGB (50, 50, 50); display: block; display: block; display: block; display: block;

thread parameter scope
The child thread / Application
Main thread (same below) ApplicationContext/

ServiceContext
Application
/ FragmentActivity Activity
/ Activity Activity
/ Fragment Fragment
/ View Activity / Fragment
  • Application scope

For Application scoped requests, the lifecycle is global and not tied to a specific page.

RequestManagerRetriever.java

Private Volatile RequestManager applicationManager; Private RequestManager getApplicationManager(@nonNULL Context Context) {source code based on DCL singleton return applicationManager; } public RequestManager get(@NonNull Context context) { if (Util.isOnMainThread() && ! (Context Instanceof Application)) {2, FragmentActivity if (Context Instanceof FragmentActivity) {return get((FragmentActivity) context); } 3, Activity else if (context instanceof Activity) {return get((Activity) context); }} 1, Application return getApplicationManager(context); } public RequestManager get(@NonNull FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else {see below... }}Copy the code

The above code has been very simplified, focusing on the following points:

1. The Application domain corresponds to the applicationManager, which is bundled with the RequestManagerRetriever object.

2. Call get(…) on child thread Or if the parameters passed in are ApplicationContext & ServiceContext, the corresponding request is the Application domain.

  • 2. Activity scope

RequestManagerRetriever.java

Has been simplified, Public RequestManager Get (FragmentActivity Activity) {FragmentManager FM = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm, null, isActivityVisible(activity)); } public RequestManager get(Activity activity) { android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm, null, isActivityVisible(activity)); }Copy the code

As you can see, you get the FragmentManager for the FragmentActivity and then call supportFragmentGet(…). Get RequestManager.

Tip: The Activity branch is similar to the FragmentActivity branch, so I won’t repeat the analysis.

  • Fragment scope

RequestManagerRetriever.java

Has been simplified, and omit the child thread branch public RequestManager get (fragments fragments) {FragmentManager FM = fragments. GetChildFragmentManager (); return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible()); }Copy the code

As you can see, you get the Fragment FragmentManager(getChildFragmentManager()) and then call supportFragmentGet(…). Get RequestManager.

2.3 Life cycle Binding

From the analysis in the previous section, both the Activity and Fragment domains call supportFragmentGet(…). To get the RequestManager, this section is devoted to this method:

RequestManagerRetriever.java

Simplified (Hint: This method will be in the main thread) for temporary record FragmentManager - SupportRequestManagerFragment mapping final Map < FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>(); private RequestManager supportFragmentGet( Context context, FragmentManager fm, Fragment parentHint, Boolean isParentVisible) {1, obtained from the FragmentManager SupportRequestManagerFragment SupportRequestManagerFragment current =  getSupportRequestManagerFragment(fm, parentHint, isParentVisible); 2, from the fragments acquisition RequestManager RequestManager RequestManager = current. GetRequestManager (); If (RequestManager == null) {3.1 Creating a Glide Glide = Glide. Get (context); requestManager = factory.build(...) ; 3.2 set up corresponding RequestMananger fragments. Current setRequestManager (requestManager); } return requestManager; } - > 1, from FragmentManager SupportRequestManagerFragment private SupportRequestManagerFragment getSupportRequestManagerFragment(FragmentManager fm, Fragment parentHint, Boolean isParentVisible) {1.1 attempts to acquire corresponding fragments FRAGMENT_TAG SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); If (current = = null) {1.2 try to obtain fragments from the temporary record current = pendingSupportRequestManagerFragments. Get (FM); 1.3 instantiation fragments the if (current = = null) {1.3.1 object creation current = new SupportRequestManagerFragment (); current.setParentFragmentHint(parentHint); 1.3.2 visible if the parent layer, call the onStart () life cycle if (isParentVisible) {current. GetGlideLifecycle (). The onStart (); } 1.3.3 temporary record mapping pendingSupportRequestManagerFragments. Put (FM, current); 1.3.4 Submitting a Fragment Transaction fm.beginTransaction().add(current, FRAGMENT_TAG).commitallowingStateloss (); 1.3.5 Post a Message handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, FM).sendtotarget (); } } return current; } -> 1.3.5 Post a message case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER: 1.3.6 Removing a Mapping from a Temporary Record FragmentManager supportFm = (FragmentManager) message.obj; key = supportFm; removed = pendingSupportRequestManagerFragments.remove(supportFm); break;Copy the code

The above code has been very simplified, focusing on the following points:

  • 1, from FragmentManager SupportRequestManagerFragment;
  • 2. Retrieve the RequestManager from the Fragment;
  • 3, for the first time to get, then instantiated RequestManager, follow-up is obtained from the same SupportRequestManagerFragment this RequestManager.

Among them, for SupportRequestManagerFragment method is even more crucial:

  • 1.1 Attempt to ObtainFRAGMENT_TAGThe corresponding fragments
  • 1.2 Attempting to Obtain fragments from temporary Records
  • 1.3 Instantiating the Fragment
    • 1.3.1 Creating Objects
    • 1.3.2 If the parent layer is visible, the onStart() lifecycle is called
    • 1.3.3 Temporarily Recording mapping Relationships
    • 1.3.4 Submitting a Fragment Transaction
    • 1.3.5 Post a Message
    • 1.3.6 Removing mappings from Temporary Records

In fact, step by step look down, the logic is not complex, only “temporary record” comparison test source framework understanding degree. Why should records be saved before committing a Fragment transaction?

This is to avoid SupportRequestManagerFragment in repeated to create a scope. Because commitAllowingStateLoss() posts transactions to a message queue, that is, transactions are processed asynchronously, not synchronously. Glide. With (…) is called once a transaction is asynchronously waiting to execute, assuming no temporary records are saved. , the Fragment is created repeatedly in that scope.

Note: Asynchrony does not necessarily require multithreading. Asynchrony is often accompanied by concurrency, but it is not necessary. I will be sharing a Kotlin coroutine article on this topic in the near future.

2.4 Lifecycle monitoring

Glide creates an unbounded Fragment for each Activity and Fragment scope. In this section we will look at how Glide listens to the life cycle of this unbounded Fragment.

SupportRequestManagerFragment.java

private final ActivityFragmentLifecycle lifecycle;

public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
}

@Override
public void onStart() {
    super.onStart();
    lifecycle.onStart();
}

@Override
public void onStop() {
    super.onStop();
    lifecycle.onStop();
}

@Override
public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
}

@NonNull
ActivityFragmentLifecycle getGlideLifecycle() {
    return lifecycle;
}
Copy the code

RequestManagerRetriever.java

-> RequestManager Glide Glide = Glide. Get (context); requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); Public interface RequestManagerFactory {Build (Glide, Lifecycle, Lifecycle) RequestManagerTreeNode requestManagerTreeNode, Context context); Private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() { @Override public RequestManager build( Glide glide, Lifecycle lifecycle, RequestManagerTreeNode requestManagerTreeNode, Context context) { return new RequestManager(glide, lifecycle, requestManagerTreeNode, context); }}; }Copy the code

RequestManager.java

Final Lifecycle Lifecycle has been simplified; RequestManager(Glide glide, Lifecycle lifecycle, ...) {... this.lifecycle = lifecycle; Add a listener lifecycle. AddListener (this); } @Override public synchronized void onDestroy() { ... Lifecycle. RemoveListener (this); }Copy the code

As you can see, the instantiation RequestManager need a com. Bumptech. Glide. Manager. Lifecycle object, the object is created in the fragments the interface. When the Fragment’s Lifecycle changes, it is through this Lifecycle object that events are distributed to the RequestManager.

ActivityFragmentLifecycle.java

class ActivityFragmentLifecycle implements Lifecycle { private final Set<LifecycleListener> lifecycleListeners = Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>()); . }Copy the code

2.5 Lifecycle callback

Now let’s look at what the RequestManager does when it receives a lifecycle callback.

LifecycleListener.java

public interface LifecycleListener {
    void onStart();
    void onStop();
    void onDestroy();
}
Copy the code

RequestManager.java

private final RequestTracker requestTracker; public class RequestManager implements ComponentCallbacks2, LifecycleListener, ... {@override public synchronized void onStop() {1, pauseRequests(); targetTracker.onStop(); } @override public synchronized void onStart() {public synchronized void onStart() {public synchronized void onStart(); targetTracker.onStart(); } @override public synchronized void onDestroy() {Override public synchronized void onDestroy(); for (Target<? > target : targetTracker.getAll()) { clear(target); } targetTracker.clear(); requestTracker.clearRequests(); lifecycle.removeListener(this); lifecycle.removeListener(connectivityMonitor); mainHandler.removeCallbacks(addSelfToLifecycle); glide.unregisterRequestManager(this); } public synchronized void pauseRequests() { requestTracker.pauseRequests(); } public synchronized void resumeRequests() { requestTracker.resumeRequests(); }}Copy the code

The main concerns are as follows:

  • 1. Suspend the request when the page is not visible(onStop ())
  • 2. Resume request when the page is visible(onStart ())
  • 3. Destroy the request when the page is destroyed(onDestroy ())

3. Network connection status monitoring

Glide listens for network connection status and restarts failed requests when the network is reconnected. Specific analysis is as follows:

3.1 Broadcast listener

RequestManager.java

private final ConnectivityMonitor connectivityMonitor; RequestManager(...) {... Listener connectivityMonitor = factory. Build (context. GetApplicationContext (), new RequestManagerConnectivityListener(requestTracker)); . }Copy the code

As you can see, in the constructor of the RequestManager, a ConnectivityMonitor object is built. Where, the default build factory is:

DefaultConnectivityMonitorFactory.java

public class DefaultConnectivityMonitorFactory implements ConnectivityMonitorFactory { private static final String TAG =  "ConnectivityMonitor"; private static final String NETWORK_PERMISSION = "android.permission.ACCESS_NETWORK_STATE"; @Override public ConnectivityMonitor build(Context context, ConnectivityMonitor. ConnectivityListener listener) {1, check whether the monitoring network state permissions granted int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION); boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED; Instantiate different ConnectivityMonitor return hasPermission? new DefaultConnectivityMonitor(context, listener) : new NullConnectivityMonitor(); }}Copy the code

If there are monitoring network access, instantiation DefaultConnectivityMonitor:

Has been simplified final class DefaultConnectivityMonitor implements ConnectivityMonitor {private final Context Context; final ConnectivityListener listener; boolean isConnected; private boolean isRegistered; Private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {@override public void Override onReceive(Context context, Intent intent) { boolean wasConnected = isConnected; isConnected = isConnected(context); If (wasConnected! = isConnected) { listener.onConnectivityChanged(isConnected); }}}; DefaultConnectivityMonitor(Context context, ConnectivityListener listener) { this.context = context.getApplicationContext(); this.listener = listener; } private void register() { if (isRegistered) { return; } isConnected = isConnected(context); context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); isRegistered = true; } private void unregister() { if (! isRegistered) { return; } 3, cancellation of radio listeners context. UnregisterReceiver (connectivityReceiver); isRegistered = false; } Boolean isConnected(Context Context) {ConnectivityManager ConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); return networkInfo.isConnected(); } @override public void onStart() {register() when the page is visible; } @override public void onStop() {unregister(); } @Override public void onDestroy() { // Do nothing. } }Copy the code

You can see that the broadcast listener is registered when the page is visible and unregistered when the page is not.

3.2 Network Connection Change Callback

Now let’s look at how network connection state changes are handled in RequestManager.

RequestManager.java

private class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener { private final RequestTracker requestTracker; RequestManagerConnectivityListener(RequestTracker requestTracker) { this.requestTracker = requestTracker; } @override public void onConnectivityChanged(Boolean connected) { Reopened request if isConnected () {synchronized (RequestManager. This) {requestTracker. RestartRequests (); }}}}Copy the code

Summary: Glide listens for network connection status if the application has permission to monitor network status and restarts failed requests when the network is reconnected.

4. Memory status monitoring

In this section, we will analyze the memory status monitoring module of Glide. The specific analysis is as follows:

Glide.java

private static void initializeGlide(...) {... applicationContext.registerComponentCallbacks(glide); } @override public void onTrimMemory(int level) {trimMemory(level); } @override public void onLowMemory() {clearMemory(); } public void trimMemory(int level) {1.1 Make sure the mainthread is util.assertMainThread (); For (RequestManager Manager: Managers) {manager.ontrimMemory (level); TrimMemory (level); memoryCache.trimMemory(level); bitmapPool.trimMemory(level); arrayPool.trimMemory(level); } public void clearMemory() {1.2 Make sure it is the main thread util.assertMainThread (); 1.2 memoryCache handling low memory states memorycache.clearmemory (); bitmapPool.clearMemory(); arrayPool.clearMemory(); }Copy the code

RequestManager.java

@Override public void onTrimMemory(int level) { if (level == TRIM_MEMORY_MODERATE && PauseAllRequestsOnTrimMemoryModerate) {pause request pauseAllRequestsRecursive (); }}Copy the code

Summary: in building a Glide, will call registerComponentCallbacks global registration (), nervous system in memory callback onTrimMemory (level). Glide does memoryCache/bitmapPool/arrayPool recycling based on the system memory stress level (level), while RequestManager suspuses requests at TRIM_MEMORY_MODERATE.

5. To summarize

  • Page life cycle

Suspend requests when the page is not visible Resume the request when the page is visible; Destroy the request when the page is destroyed.

  • Network Connection Status

If the application has permission to monitor network status, Glide listens for network connection status and restarts failed requests when the network is reconnected.

  • Memory state

Glide does memoryCache/bitmapPool/arrayPool recycling based on the system memory stress level (level), while RequestManager pauses requests at TRIM_MEMORY_MODERATE.


The resources

  • Glide Official Document

Creation is not easy, your “three lian” is chouchou’s biggest motivation, we will see you next time!