Technology TOP picture of the day before yesterday

Author: RicardoMJiang

Juejin. Cn/post / 697068…

Glide can be said to be the most commonly used picture loading framework, Glide chain call easy to use, performance can also meet the use of most scenarios, Glide source code and principle is also a frequent interview. But Glide source content is more, want to learn its source code is often a myriad of threads, catch the focus. This article to Glide to do what optimization as the entry point, introduce and learn Glide source code and principle, if you help, welcome to praise.

What has Glide done to optimize it?

To answer this question, we can start by thinking, if we were to implement an image-loading framework ourselves, what questions would we ask? 1. Image downloading is a time-consuming process. The first thing we need to consider is image caching. Image loading is also a memory consuming operation, many OOM images are caused by image loading, so we need to consider memory optimization 3. In the middle of the image load, the page closes and the image load should also stop, which brings up the life cycle management issue 4. Does the image loading framework support large image loading? What’s wrong with the big picture?

The above is we put forward a few questions about Glide, so we can easily get the main content of this article includes: 1.Glide picture loading general process introduction 2. What is the optimization of Glide cache mechanism? 3. What memory optimizations did Glide do? How does Glide manage the life cycle? 5. How does Glide do big picture loading?

Here is the question to enter the body ~

1.Glide picture loading general process introduction in the beginning to understand what specific Glide do optimization, we first Glide picture loading general process to do a simple introduction, so that we first have an overall concept. At the same time, it is convenient to know the specific step of Glide optimization. Generally speaking, image loading includes encapsulation, parsing, downloading, decoding, transformation, caching, display and other operations, as shown in the following figure: picture

1. Encapsulate parameters: The process from specifying the source to the output can go through many processes, so the first thing is to encapsulate parameters, and these parameters will continue throughout the process; 2. Analytic path: there are many sources of pictures and different formats, which need to be standardized; 3. Read cache: Caching is usually done to reduce computation; The same request, from the cache (Bitmap) can be; 4. Search for files/download files: if it is a local file, directly decode it; If it is a network picture, you need to download it first. 5. Decoding: This step is one of the most complicated in the whole process, with many details; 6. Transformation: After decoding the Bitmap, some transformation (rounded corners, filters, etc.) may be needed; 7. Cache: After the final bitmap is obtained, it can be cached for the next request. 8. Display: To display the results, some animation may be required (fade, crossFade, etc.). Glide picture loading above is the general process, here is only a simple introduction, details can be seen: talk about Glide in the interview those things

2. What are the improvements made to Glide caching mechanism? We know that downloading images is very expensive, so the image caching mechanism is an important part of the image loading framework. Here is a table to illustrate Glide caching.

Cache Type Cache Description Active Cache If the current image resource is retrieved from the memory cache, the image is stored in the active resource. The memory cache LruResourceCache has been parsed and loaded recently. Disk cache – Resource type DiskLruCacheWrapper The decoded image is written to the disk file Disk cache – Raw data DiskLruCacheWrapper The original data is cached on disk after the network request is successful. Before we talk about caching, Glide’s cache mechanism is mainly divided into two kinds of cache, one is memory cache, the other is disk cache. The reason for using the memory cache is to prevent the application from repeatedly reading images into the memory, resulting in memory resource waste. The reason for using disk caching is to prevent applications from repeatedly downloading and reading data from the network or elsewhere. Because of the combination of these two kinds of cache, it has formed Glide’s excellent cache effect.

Memory cache is enabled by Glide by default, and skipMemoryCache is disabled by Glide. ActiveResources is a weakly referenced HashMap that caches images in use. Using ActiveResources to cache images in use, These images can be protected from being recycled by LruCache algorithm

The loading sequence of memory cache is as follows: 1. Generate key 2 based on the image address, width, transform, and signature. The first load did not fetch the active cache. 3. Then load the memory resource cache, clear the memory cache first, and then add the active cache. 4. The second load activity cache already exists. 5. When the current image reference is 0, clean up the active resource and add it to the memory resource. 6. It’s back to step one, and then it all comes together.

Summary of the flow chart is as follows: the picture here is not posted source code, if you want to see the source code of the students can refer to: Analysis of Glide cache strategy from the perspective of source code

We have summarized the Glide memory cache loading process above, so it is easy to wonder why Glide designed two types of memory cache.

2.1.1 Why are two types of memory caches designed? The implementation of the LruCache algorithm uses a LinkedHashMap to cache objects. Each time memory exceeds the cache setting, the least recently used cache will be removed. Therefore, it is possible to accidentally remove the cache that is in use while I am still using it. So the weak reference is probably a way to protect the image in use by removing it from the LruCache and adding it back to the cache when it is used up.

For example, we set the size of Lru memory cache to hold 99 pictures. In the sliding RecycleView, if we just slide to 100 pictures, the first one we have loaded will be recycled. At this time, if we slide back to the first one, we will re-judge whether there is memory cache. If it doesn’t, we’re going to make a new Request, and obviously if we clean up the first image, that’s not what we’re looking for. So when the resource data is retrieved from the memory cache, it is actively added to the active resource and the memory cache is cleared. The obvious benefit of doing this is to protect images that don’t want to be recycled from the LruCache algorithm, which makes full use of resources.

2.1.1 Summary This section mainly summarizes the Glide memory cache loading process 1. Get the active cache first, return if loaded, go to step 2 if not. The LRU cache is then fetched, and when fetched it is removed from the LRU and added to the active cache 3. 4. When the image reference is 0, it is cleared from the active cache and added to the LRU cache 5. The reason for the design of the two memory caches is to prevent loaded images from being recycled by the LRU

2.2 Disk Caching First look at the disk caching policy

Diskcachestrategy. NONE: indicates that no content is cached. DiskCacheStrategy. RESOURCE: after resources to decode the data to disk cache, namely after scaling transformation of image resources. Diskcachestrategy. DATA: Writes raw DATA to the disk cache before decoding the resource. Diskcachestrategy. ALL: uses DATA and RESOURCE to cache remote DATA, and only RESOURCE to cache local DATA. DiskCacheStrategy. AUTOMATIC: it will try to use the best strategy for local and remote images. When you load remote data, AUTOMATIC only stores raw data that has not been modified by your load process, because downloading remote data is much more expensive than adjusting data that already exists on disk. For local data, AUTOMATIC stores only the thumbnails that have been transformed, because it’s easy to retrieve the original data even if you need to generate another image of a different size or type. The main thing you need to know about disk caching is that when you use Glide to load an image, Glide does not display the original image by default, it compresses and transforms the image, so it is the image that you get after a series of operations. It’s called the transformed image. We can cache both the original image before the transformation and the image after the transformation

2.2.1 why need two disk cache has been said above, DiskCacheStrategy. The RESOURCE cache is transformed resources, DiskCacheStrategy. The DATA cache is transform resources before For example, with a picture, We’re going to show it on the 100100 View, and then we’re going to show it on the 200200 View. If you don’t cache the transformed type, you’re going to have to do a transformation every time. If you don’t cache the raw data, you’re going to have to download the data again every time

DiskCacheStrategy.RESOURCE currentKey = new ResourceCacheKey(helper.getArrayPool(),sourceId,helper.getSignature(),helper.getWidth(),helper.getHeight(),transformatio n,resourceClass,helper.getOptions());

DiskCacheStrategy.DATA DataCacheKey newOriginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); 2.2.2 Summary This section mainly describes the Glide cache strategy and why the need for two types of disk cache there is no source code posted here, if you want to see the source code for students to refer to: From the perspective of the source code analysis Glide cache strategy

3. What memory optimizations did Glide do? Glide memory optimization is primarily a Bitmap optimization. Before answering this question, let’s think about some common Bitmap optimization methods. InSampleSize can be used for sizing when the image size is inconsistent with the View size 2. Pictures that memory of wide high _ _ each pixel of the memory size, different patterns of each pixel of the memory size is different, we can use inpreferredconfig configuration 3. Bitmpa of memory is bigger, if often create recycling Bitmap memory may cause memory jitter, We can use inBitmap to use Bitmap memory 4. Memory cache, Glide’s weak reference cache and LRU cache were introduced earlier

There are only a few common Bitmap memory optimizations, but we rarely use them directly in our work. Let’s talk about how they are used in Glide.

3.1 Size Optimization When the image container such as ImageView is only 100*100 and the image resolution is 800 * 800, it is easy to OOM to place the image directly on the container, and it is also a waste of image and memory resources. When the width and height of the container are much smaller than the width and height of the image, it is necessary to compress the image size, adjust the image resolution to the size of the ImageView width and height, on the one hand, it will not affect the image quality, but also greatly reduce the memory usage

We usually use inSampleSize to scale the Bitmap

If inSampleSize is set to a value greater than 1, the decoder will be requested to subsample the image of the original bitmap and then return a smaller image to reduce memory usage. For example, inSampleSize == 4, the width and height of the sampled image will be 1/4 of the original image and the pixel value will be 1/16 of the original image. In other words, the memory of the sampled image is 1/16 of the memory of the original image; When inSampleSize <=1, we treat it as 1, which is the same size as the original image. InSampleSize is always a power of 2, such as 1,2,4,8. Any other values are also rounded to the nearest power of 2.

//1 int widthScaleFactor = orientedSourceWidth / outWidth; int heightScaleFactor = orientedSourceHeight / outHeight; //2 int scaleFactor = rounding == SampleSizeRounding.MEMORY ? Math.max(widthScaleFactor, heightScaleFactor) : Math.min(widthScaleFactor, heightScaleFactor); int powerOfTwoSampleSize; //3 if (Build.VERSION.SDK_INT <= 23 && NO_DOWNSAMPLE_PRE_N_MIME_TYPES.contains(options.outMimeType)) { powerOfTwoSampleSize = 1; } else { //4 powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor)); / / 5 if (rounding = = SampleSizeRounding. The MEMORY / / exactScaleFactor by individual tailoring strategies such as CenterCrop rewrite, && powerOfTwoSampleSize < (1. F/exactScaleFactor)) {powerOfTwoSampleSize = powerOfTwoSampleSize << 1; } } options.inSampleSize = powerOfTwoSampleSize;Copy the code

1. First calculate the width and height ratio of the picture and View 2. Depending on whether the scaling strategy is economical or high quality, decide to choose the maximum or minimum aspect ratio of 3. When build.version.sdk_int <=23, some formats of images cannot be scaled 4. HighestOneBit’s function is to round the scale we calculated to the nearest power of 2, 5. If the scaling strategy is economized and we calculate SampleSize<exactScaleFactor, inSampleSize*2

Glide picture as above is the approximate logic of size optimization when loading

3.2 Image Format Optimization We know that the memory size of Bitmap is determined by the memory size per pixel of width and height. The above size optimization determines the width and height, while the image format optimization determines the memory size per pixel

In API29, Bitmap is divided into six levels: ALPHA_8, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16 and HARDWARE.

ALPHA_8: does not store color information, each pixel occupies 1 byte; RGB_565: Only RGB channels are stored, each pixel accounts for 2 bytes, there is no high requirement for Bitmap color, this mode can be used; ARGB_4444: deprecated, replaced with ARGB_8888; ARGB_8888: each pixel occupies 4 bytes and maintains high quality color fidelity. This mode is used by default; RGBA_F16: Each pixel occupies 8 bytes, suitable for wide color gamut and HDR; HARDWARE: A special configuration that reduces memory footprint and speeds up Bitmap drawing. Each level also takes up different bytes per pixel and stores different color information. ARGB_8888 takes up 400 bytes for the same 100 pixel image, while RGB_565 takes up 200 bytes. RGB_565 has the advantage in memory, but the color value and sharpness of the Bitmap are not as good as ARGB_8888

It is important to note that Glide used the RGB565 format by default prior to Glide4.0, which is relatively frugal, but since Glide4.0, the default has been changed to ARGB_8888, which eliminates this advantage. This in itself is a trade-off between quality and memory, and the default format can be modified if the quality of the image required by the application is not high

Public static final Option DECODE_FORMAT = option.memory (ARGB_8888 public static final Option DECODE_FORMAT = option.memory (ARGB_8888 public static final Option DECODE_FORMAT = option.memory) “com.bumptech.glide.load.resource.bitmap.Downsampler.DecodeFormat”, DecodeFormat.DEFAULT); 3.3 Memory Overcommitment Optimization Bitmap occupies a large memory, if we frequently create and reclaim Bitmap, it is easy to cause memory jitter, so we should try to reuse Bitmap memory Glide mainly uses inBitmap and BitmapPool to achieve memory reuse

3.3.1 inBitmap introduced in Android 3.0 (API level 11), the system introduced BitmapFactory. Options. InBitmap field. If this option is set, the decoding method using Options objects attempts to reuse inBitmap when generating the target Bitmap, which means that inBitmap memory is reused, improving performance, while removing memory allocation and unallocation. However, inBitmap can be used with some limitations. Before Android 4.4 (API level 19), only bitmaps of the same size can be reused. After Android 4.4, the size of inBitmap can be larger than the target Bitmap

You can reuse memory using inBitmap, but you need a place to store reusable bitmaps. This is ThreadPoolExecutor in the BitmapPool JDK. We commonly refer to this as a “thread pool.” Pooling is a very common concept, and its purpose is to achieve object reuse. For example, ThreadPoolExecutor implements thread reuse mechanism, and BitmapPool implements Bitmap pooling

Private static void setInBitmap(bitmapFactory. Options Options, BitmapPool BitmapPool, int width, int height) { @Nullable Bitmap.Config expectedConfig = null; if (expectedConfig == null) { expectedConfig = options.inPreferredConfig; } // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe. options.inBitmap = bitmapPool.getDirty(width, height, expectedConfig); } It is Glide to set the inBitmap code, to the BitmapPool into the width and height and format, get a reusable object, so that the implementation of Bitmap memory multiplexing due to space reasons, the detailed source code is not posted here, For more information, see Coil and Glide’s Bitmap cache reuse mechanism

How does Glide manage the life cycle? When we do a network request, we should abort the request when the page exits, otherwise it will leak memory. The same is true for image loading. We should abort the request when the page exits and destroy resources. So it’s something that you don’t have to do when the page exits, so it’s something that you can release automatically when the page closes. So let’s see how it works. Use Glide. With to pass the context and build a Fragment 2 using the context. Listen for the Fragment life cycle and release Glide resources when it is destroyed

Public RequestManager get(@nonnull Activity Activity) {public RequestManager get(@nonnull Activity Activity) { . / / get the current Activity FragmentManager android app. FragmentManager FM = Activity. GetFragmentManager (); RequestManager Return fragmentGet(Activity, FM, /parentHint=/ null, isActivityVisible(activity)); }

private RequestManager fragmentGet(@NonNull Context context, @NonNull android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, Boolean isParentVisible) {//① Add a Fragment to the current Activity to manage the lifecycle of the request RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); / / get RequestManager RequestManager RequestManager = current. GetRequestManager (); If (RequestManager == null) {Glide Glide = Glide. Get (context); / / (2) build RequestManager / / current getGlideLifecycle () is ActivityFragmentLifecycle, Also is in the building when it was introduced into fragments RequestManager ActivityFragmentLifecycle RequestManager = factory. Build (glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); / / to build the RequestManager binding to current in the fragments. The setRequestManager (RequestManager); } // Return requestManager; } add a transparent Fragment to the current Activity to manage the request lifecycle 2. Build the RequestManager and pass in the Fragment life cycle

Public class RequestManager implements LifecycleListener, ModelTypes<RequestBuilder> {

RequestManager( Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) { … / / the current object to register ActivityFragmentLifecycle lifecycle. The addListener (this); } / /…

RequestManager implements fragment lifecycle callback @override public synchronized void onStart() {resumeRequests(); targetTracker.onStart(); }

@Override public synchronized void onStop() { pauseRequests(); targetTracker.onStop(); }

@Override public synchronized void onDestroy() { targetTracker.onDestroy(); }

}

Public class RequestManagerFragment extends fragments {/ / the key to the life cycle in ActivityFragmentLifecycle private final ActivityFragmentLifecycle lifecycle; public RequestManagerFragment() { this(new ActivityFragmentLifecycle()); }

RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) { this.lifecycle = lifecycle; } @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(); } / /… } The logic is simple: A Fragment lifecycle change calls back to the RequestManager lifecycle and then does the associated resource release

Glide. With (this) binds the Activity’s life cycle. A new ui-free Fragment is created within the Activity that will hold a Lifecycle and inform the RequestManager of slave actions during the critical Lifecycle of the Fragment. Continue loading at life cycle onStart, pause loading at life cycle onStop, and stop loading tasks and cleanup operations at life cycle onDestory.

Because of limited space, there is not too much code here, more details can be referred to :Glide life Cycle management

There is also a case for image loading where a single image is very large and compression is not allowed. For example: World map, Qingming River Map, Weibo long map, etc., do not compress first, load according to the size of the original picture, then the screen is certainly not big enough, and considering the memory, it is impossible to load the whole picture into the memory at one time, so the optimization idea of this situation is generally local loading, In this case usually Glide is only responsible for the image download down, image loading by our custom ImageView to achieve

BitmapRegionDecoder is used to display a rectangular region of an image. This class is great if you need to display a specific region of an image. For the use of this class, very simple, since it is to display a certain area of the image, then at least one method to set the image; For example, a method passed into the displayed area:

/ / set the display area of the center of the picture BitmapRegionDecoder BitmapRegionDecoder = BitmapRegionDecoder. NewInstance (inputStream, false); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 – 100, height / 2 – 100, width / 2 + 100, height / 2 + 100), options); mImageView.setImageBitmap(bitmap); Although this method can also load large images, but it is not enough, memory jitter when sliding, the phenomenon is more obvious, can not be used for online pictures. The following introduction of a large image loading scheme can be used for online

5.2 Large image Loading scheme for Online Introduction an open source library: Subsampling – scale – image – view SubsamplingScaleImageView will larger image slices, and then determine whether visible, if visible in memory, or recycling, reduce the memory footprint and jitter At the same time, according to the different scale to choose the appropriate sampling rate, At the same time, the decodeRegion operation is carried out in the child thread, and the decoder is successfully called back to the main thread to reduce THE UI lag.

I also do before BitmapRegionDecoder and SubsamplingScaleImageView memory analysis Interested students can know: the Android UI caton optimization example analysis of performance optimization

Summary this paper mainly to Glide to do what optimization as the entry point, answer the following several questions 1. 2. What is the optimization of Glide cache mechanism? 3. What memory optimizations did Glide do? How does Glide manage the life cycle? 5. How does Glide do big picture loading?

If you help, welcome to like, thank you ~

—END—

Kotlin 2021 roadmap Kotlin 2021 roadmap for Retrofit has been pulled after the APP was removed from the market. How does Dropbox fix memory leaks in Android App? To clarify, how do you understand the Window mechanic? Compose 1.0 is coming, are you ready? Google is officially pushing Fuchsia OS to users. Click “Check it out” to support it at 👇

Read 1099 to write your message select messages

 chief webmaster Glide loading subsamplingImageView or not enough detail

🇨🇳 Liao.²₀² former ourselves looking at Glide source, can only understand half and half, read this article, can understand more than 80%!