Glide图片加载库源码分析(主要是内存方面)

682 阅读6分钟

本文基于glide4.10.0分析,首先根据简单的调用梳理流程,主要以流程图的形式展示,然后基于流程图,着重分析glide的内存管理.这样方便在后期遇到问题能及时找到地方和原因.

首先是流程图:

QtaL3q.png

内存分析

内存类的创建

  1. 当我们调用Glide.with(context)时,其内部通过单例的方式返回Glide实例
public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }

    return glide;
  }

2,然后通过逐层调用,最后通过GlideBuilder.build方法创建Glide实例对象

initializeGlide(..){
    ...
     Glide glide = builder.build(applicationContext);
    ...
    
}

3,进入Build方法,查看具体内存管理的实现类.

  • 我们都知道是基于三级内存管理策略,这里主要看实现逻辑
  • 内存缓存类LruResourceCache,具体给类可阅读网上的文章
if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
  • 磁盘缓存处理类InternalCacheDiskCacheFactory,其继承DiskLruCacheFactory
if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory    

这里我们找到了,在Glide中内存管理中,主要负责缓存管理的两个类.在流程分析中最终数据的加载都是通过LoadData和相应的Fetcher实现,而其是在构造方法添加的,这里重点了解其都由那些类,这样后面分析加载过程直接查看具体的类即可.
4,Loader的添加

register.
...
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
        .append(File.class, InputStream.class, new FileLoader.StreamFactory())
        .append(File.class, File.class, new FileDecoder()).
...


内存缓存的读取和写入

读取

1,Glide.into(imageView)根据流程会调用Engine.load方法,这里我们查看该方法

  • 首先会生成资源key,发现其根据我们load传入的参数(model),以及图标的高度,类等生成key
  • 缓存的调用通过方法loadFromMemory方法获取
public <R> LoadStatus load(...){
    
    EngineKey key = keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource<?> memoryResource;
    synchronized (this) {
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
    }
}

2,loadFromMemory负责从内存缓存中读取数据,其内部有两级缓存,弱引用缓存和LruResourceCache缓存(其变量名为memory)

  • loadFromActiveResources获取弱引用缓存,弱引用缓存通过HashMap实现
private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }

    return active;
  }
  //ActiveResource
  final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
  synchronized EngineResource<?> get(Key key) {
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    if (activeRef == null) {
      return null;
    }

    EngineResource<?> active = activeRef.get();
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }

  • loadFromCache 获取LruResourceCache缓存
    !. 其通过构造方法传入engine,变量为cache
//GlideBuild.build
engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }
    
 Engine(
      MemoryCache cache,
      ...) {
    this.cache = cache;
    }

!!. 当从LruResourceCache获取缓存后,会将其删除,

private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    return cached;
  }
  
  private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);
 }

!!!. 然后添加到弱引用HashMap缓存中

//ActiveResource
synchronized void activate(Key key, EngineResource<?> resource) {
   ResourceWeakReference toPut =
       new ResourceWeakReference(
           key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

   ResourceWeakReference removed = activeEngineResources.put(key, toPut);
   if (removed != null) {
     removed.reset();
   }
 }

写入

写入分析从获得返回数据开始分析,查看流程图我们直到其通知回调,最终会调用EngineJob.onResourceReady方法,我们分析写入内存缓存就从这里开始,省略前面的

  1. notifyCallbacksOfResult 方法内部会调用RequestBuilder.into传入的Executors.mainThreadExecutor()
  • Executors.mainThreadExecutor传递线路
//RequestBuilder
return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//request最终通过obtainQuest生成
public static <R> SingleRequest<R> obtain(...
      Executor callbackExecutor) {
      }
//SingleReqeust.onSizeReady调用engine.load
 public void onSizeReady(int width, int height) {
loadStatus =
          engine.load(...
              this(cb),
              callbackExecutor);
}
//Engine.load创建engineJob,添加callbackExecutor,即Executors.mainThreadExecutor
//engineJob.addCallback(cb, callbackExecutor);
  • 同时发现,添加的cb为SingleRequest,这个在数据返回的回调会用到
  • Executors.mainThreadExecutor内部利用handler.post发送消息,这样我们发现其如何切换会主线程了
public static Executor mainThreadExecutor() {
    return MAIN_THREAD_EXECUTOR;
  }
  
private static final Executor MAIN_THREAD_EXECUTOR =
      new Executor() {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
          handler.post(command);
        }
      };

  1. 回到EngineJob的notifyCallbacksOfResult方法,其在最后回执行entry.executro方法,之所以上面说执行的Executors.mainThreadExecutor,这里继续说明,
  • 倒叙最终是否是Executors.mainThreadExecutor
for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }

 copy = cbs.copy();
 
 synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
    stateVerifier.throwIfRecycled();
    cbs.add(cb, callbackExecutor);
    if (hasResource) {
      // Acquire early so that the resource isn't recycled while the Runnable below is still sitting
      // in the executors queue.
      incrementPendingCallbacks(1);
      callbackExecutor.execute(new CallResourceReady(cb));
    } else if (hasLoadFailed) {
      incrementPendingCallbacks(1);
      callbackExecutor.execute(new CallLoadFailed(cb));
    } else {
      Preconditions.checkArgument(!isCancelled, "Cannot add callbacks to a cancelled EngineJob");
    }
  }
  • 首先是copy,然后我们找到cps,最后发现addCallBack最终添加到了cbs,在上面查看Executors.mainThreadExecutor的传递路线,最终就是通过addCallBack添加的,这样就明白了
  • 这样notifyCallbacksOfResult方法最后的执行,就回到主线程,这样我们看CallResourceReady,其应该实现了Runnable接口,看起run方法
  • run方法里面回调用callCallbackOnResourceReady(cb)
for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
private class CallResourceReady implements 

@Override
    public void run() {
      synchronized (cb.getLock()) {
        synchronized (EngineJob.this) {
          if (cbs.contains(cb)) {
            // Acquire for this particular callback.
            engineResource.acquire();
            callCallbackOnResourceReady(cb);
            removeCallback(cb);
          }
          decrementPendingCallbacks();
        }
      }
    }

  • callCallbackOnResourceReady(cb)回调cb即SingleRequest的onResourceReady
  • target即DrawableImageTarget这样
  • 最终设置给了iamgeView
target.onResourceReady(result, animation);

@Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } 
    ...
  }
  private void setResourceInternal(@Nullable Z resource) {
    // Order matters here. Set the resource first to make sure that the Drawable has a valid and
    // non-null Callback before starting it.
    setResource(resource);
    maybeUpdateAnimatable(resource);
  }
  
 @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

上面1,2主要分析了Glide如何将返回的数据设置给iamgeView,发现其是通过handlers实现的,同时我们分析也可以知道,如果设置缓存应该在前面 3,再次回到notifyCallbacksOfResult,分析内存的添加,其调用下面的方法

 engineJobListener.onEngineJobComplete(this, localKey, localResource);
  • engineJobListener实际是Engine.
 engineJobFactory =
          new EngineJobFactory(
              diskCacheExecutor,
              sourceExecutor,
              sourceUnlimitedExecutor,
              animationExecutor,
              /*engineJobListener=*/ this,
              /*resourceListener=*/ this);
EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
<R> EngineJob<R> build(
        Key key,
        boolean isMemoryCacheable,
        boolean useUnlimitedSourceGeneratorPool,
        boolean useAnimationPool,
        boolean onlyRetrieveFromCache) {
      EngineJob<R> result = Preconditions.checkNotNull((EngineJob<R>) pool.acquire());
}

final Pools.Pool<EngineJob<?>> pool =
        FactoryPools.threadSafe(
            JOB_POOL_SIZE,
            new FactoryPools.Factory<EngineJob<?>>() {
              @Override
              public EngineJob<?> create() {
                return new EngineJob<>(
                    diskCacheExecutor,
                    sourceExecutor,
                    sourceUnlimitedExecutor,
                    animationExecutor,
                    engineJobListener,
                    resourceListener,
                    pool);
              }
            });
            
  • 这样我们进入Engine的onEngineJobComplete
public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null && resource.isMemoryCacheable()) {
      activeResources.activate(key, resource);
    }

    jobs.removeIfCurrent(key, engineJob);
  }

4,内存添加方法的最终调用

  • 上面的步骤最终调用 activeResources.activate,其内部回添加弱引用缓存
synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
  }
  • 但是没有发现LruResourceCache缓存的添加,我们知道notifyCallbacksOfResult内部最终调用CallResourceReady的run方法
  • 前面没有找到,我们从后面寻找,发现其最后回调用decrementPendingCallbacks
  • decrementPendingCallbacks内部回调用EngineReource.release
  • release方法内部调用listener.onResourceReleased,那我们周到listener即可
  • listener为接口ResourceListener的方法,那我们寻找其实现,我们发现Engine实现了给接口
  • 但是我们好奇其是在哪里设置进去的呢,在notifyCallbacksOfResul里面找到下面一句代码
 engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
  • resourceListener 是在EngineJob构造方法设置进去的,那我们返回上面EngineJob构建,发现跟engineJobListener的设置一样,在创建engineJobFactory设置进去
engineJobFactory =
          new EngineJobFactory(
              diskCacheExecutor,
              sourceExecutor,
              sourceUnlimitedExecutor,
              animationExecutor,
              /*engineJobListener=*/ this,
              /*resourceListener=*/ this);

5,接下来,进入 EngineResource.ResourceListener的onResourceReleased方法,即Engine

  • 这里我们发现了cache的put方法,到此,内存的添加就都完成了
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

我们总结一下内存的写入

  1. 首先我们分析了Glide如何把返回的数据设置给imageView,其是通过handler.post一个消息,切换回主线程,然后通过回调SingleRequest的方法,然后调用Target.onResourceReady方法,最终调用了ImageView的setImageDrawable方法
  2. 然后我们分析了其如何往两个内存里写入数据
  • 首先在创建EngineJob的实例是会传入两个监听engineJobListener和resourceListener,其就是Engine本身
  • 然后在任务完成回调EngineJob的onResourceReady,接着调用notifyCallbacksOfResult,在这个方法里,调用engineJobListener(Engine).onEngineJobComplete设置弱引用Hashmap缓存,同时,在notifyCallbacksOfResult方法,会创建EngineResource,并将resourceListener传入,即Engine
  • 然后在执行Handler.post的消息时,当设置完资源给iamgeView时,最终会调用engineresource的release方法
  • release方法,最终会调用resourceListener.onResourceReleased,即engine的方法
  • 然后在engine重写的这个方法里设置LruResourceCache缓存