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 Obtain
FRAGMENT_TAG
The 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!