Have you learned about Glide’s level 3 cache?

  • Let’s take a look at what we call image level 3 caching

Strong references, soft references, and file systems are common. Android provides LruCache, which maintains a LinkedHashMap to store the types of data we need, such as the Bitmap we need here. LruCache is usually set to 1/8 of the maximum storage space of the system, and its mechanism is often referred to as the least recently used principle. If the image size in Lru exceeds the default size, the oldest image will be removed.

When images are removed by Lru, we need to manually add images to soft references. Need to maintain a collection of soft applications in our project.

  • A brief overview of the commonly used three-level cache flow:
First go to the Lru to find, or directly take. If no, go to SoftRefrence, if yes, fetch the image and put it back into Lru. If not, go to the file system, if yes, add the image to the Lru. If not, download the image logic, save it to the file system, and place it in Lru.Copy the code

Here is the Glide cache structure:

Glide cache strictly only memory cache and disk cache, memory cache is divided into Lru and weak reference cache.

So Glide’s three level cache can be divided into: Lru cache, weak reference cache, disk cache.

Let’s look at the order in which Glide is read. This is a little different. I’m using Glide version 4.8, which is a little different from the previous version.

Part of the source code:  @NonNull Glide build(@NonNull Context context) { if (memoryCache == null) { memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); } if (engine == null) { engine = new Engine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), GlideExecutor.newAnimationExecutor(), isActiveResourceRetentionAllowed); }Copy the code
  • MemoryCache is the memoryCache used by Glide, LruResourceCache class inherits LruCache, this part can be viewed by yourself.

As you can see above, the Glide #build() method instantiates memoryCache as Glide’s memoryCache and passes it to Engine as an input parameter to the constructor.

  • Engine.class extracts some of the source code
BuildKey = keyFactory. BuildKey (model, signature, width, height, resourceClass, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah, blah transcodeClass, options); // Read cache from weak application EngineResource<? > active = loadFromActiveResources(key, isMemoryCacheable); if (active ! = null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } // Read the cache from LruCache EngineResource<? > cached = loadFromCache(key, isMemoryCacheable); if (cached ! = null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineJob<R> engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); jobs.put(key, engineJob); engineJob.addCallback(cb); DecodeJob (decodeJob); }Copy the code

Glide load process uses loadFromActiveResources method and loadFromCache method to obtain memory cache.

If it does not, it is read by Lru. If it does, it is fetched and added to the weak reference. If it does not, EngineJob will be enabled for the image loading logic.

Here’s a quick look at the cache code:

  • Engine#onEngineJobComplete()
public void onEngineJobComplete(EngineJob<? > engineJob, Key key, EngineResource<? > resource) { Util.assertMainThread(); // A null resource indicates that the load failed, usually due to an exception. if (resource ! = null) { resource.setResourceListener(key, this); if (resource.isCacheable()) { activeResources.activate(key, resource); } } jobs.removeIfCurrent(key, engineJob); } void activate(Key key, EngineResource<? > resource) { ResourceWeakReference toPut = new ResourceWeakReference( key, resource, getReferenceQueue(), isActiveResourceRetentionAllowed); ResourceWeakReference removed = activeEngineResources.put(key, toPut); if (removed ! = null) { removed.reset(); }}Copy the code

Activeresources.activate (key, resource) puts the EngineResource in the weak reference. The lRU placement logic is as follows:

  • EngineResource#release()
void release() { if (acquired <= 0) { throw new IllegalStateException("Cannot release a recycled or not yet acquired resource"); } if (! Looper.getMainLooper().equals(Looper.myLooper())) { throw new IllegalThreadStateException("Must call release on the main  thread"); } if (--acquired == 0) { listener.onResourceReleased(key, this); }}Copy the code

When the acquired variable is greater than 0, the image is in use and should be placed in the activeResources weak reference cache. After release(), if the acquired variable is equal to 0, the picture will no longer be used. The onResourceReleased() method of the listener will be called to release the resource.

  • Engine#onResourceReleased()
@Override
  public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    Util.assertMainThread();
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }
Copy the code

The cached image is first removed from activeResources and then put into LruResourceCache. In this way, images in use are cached using weak references and images not in use are cached using LruCache.

Glide is the next DiskCache, DiskCache is simply according to the Key to DiskCache cache, interested can look at the source code.

Why do you choose Glide and not another picture loading framework?

  • Glide and Picasso was

The former is more memory efficient and can load images on demand. The default is ARGB_565, while the latter is ARGB_8888.

The former supports GIFs, the latter does not.

  • Glide and Fresco”

The lower version of Fresco has the advantage of taking up some native memory, but the higher version is also Java memory.

Fresco loading has a limit on image size, Glide has almost none.

Fresco recommends using SimpleDraweeView when it comes to layout files, so you have to consider the cost of migration.

Fresco has many native implementations, and the cost of changing the source code is much higher.

Glide provides TransFormation to help process images, Fresco does not.

Glide version iterates relatively quickly.

Glide has several significant advantages:

  • Life cycle management

GLide#with

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

  @NonNull
  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }

  @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

  
  @NonNull
  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }

 
  @Deprecated
  @NonNull
  public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }

Copy the code

You can see that there are multiple overloaded methods, which mainly handle two different types of Context differently

  • The loading life cycle of an image is the same as that of an Application, and is definitely not recommended.
  • The rest of the Context, like the current Activity, creates a hidden Fragment bound to the lifecycle.

Take an Activity as an example:

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

Specific see # fragmentGet ()

@NonNull private RequestManager fragmentGet(@NonNull Context context, @NonNull android.app.FragmentManager fm, @nullable Android.app. Fragment parentHint, Boolean isParentVisible) { RequestManagerFragment RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); RequestManager requestManager = current.getRequestManager(); return requestManager; }Copy the code

Then see RequestManagerFragment

public class RequestManagerFragment extends Fragment { @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(); }}Copy the code

LifeCycle is associated with the corresponding methods.

In simple terms, the #with() method binds the lifecycle to the different contexts that pass through.

  • Bitmap object pool

Glide provides a BitmapPool to hold bitmaps. In simple terms, when a bitmap needs to be loaded, it will find an appropriate bitmap in the pool according to the parameters of the picture, and create it again if there is no bitmap. BitMapPool also works according to the Lru algorithm. Thus improving performance.

  • Efficient cache

Caching can be described above, memory and disk. Disk caching also provides several caching strategies.

  1. NONE: no content is cached
  2. SOURCE: caches only raw images
  3. RESULT, caching only converted images (default)
  4. ALL: caches both the original and converted images