glide源码分析

glide是 Bump出品的一个图片加载库,开源地址:github.com/bumptech/gli

glide使用方法如下:

Glide.with(this).load("pic9.nipic.com/20100919").into(mImageview);

效果如下:








先看一下glide的总体框架:

接下来我们从调用方法入口开始

1.with

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

作用:初始化glide,返回一个RequestManger对象

以传入的是FragmentActivity为例,getRetriever (activity)流程如下:


initializeGlide主要做了以下工作:

  1. 获取应用中带注解的GlideModule(annotationGeneratedModule),这里要解释一下GlideModule:用户自定义glide配置模块,用来修改默认的glide配置信息。如果这个为空或者可配置menifest里面的标志为true,则获取menifest里面配置的GlideModule模块(manifestModules)。
  2. 把manifestModules以及annotationGeneratedModule里面的配置信息放到builder里面(applyOptions)替换glide默认组件(registerComponents)
  3. 各种初始化信息Glide glide = builder.build(applicationContext);
  4. 看一下build的源码,主要做了以下工作:
  • 创建请求图片线程池sourceExecutor,创建硬盘缓存线程池diskCacheExecutor。动画线程池animationExecutor
  • 依据设备的屏幕密度和尺寸设置各种pool的size
  • 创建图片线程池LruBitmapPool,缓存所有被释放的bitmap, LruBitmapPool依赖默认的缓存策略和缓存配置。缓存策略在API大于19时,为SizeConfigStrategy,小于为AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config为key,value为bitmap的HashMap
  • 创建对象数组缓存池LruArrayPool,默认4M
  • 创建LruResourceCache,内存缓存
  • new glide里面 new Registry()注册管理任务执行对象的类(Registry),可以简单理解为:Registry是一个工厂,而其中所有注册的对象都是一个工厂员工,当任务分发时,根据当前任务的性质,分发给相应员工进行处理
Glide build(@NonNull Context context) {if (sourceExecutor == null) {sourceExecutor = GlideExecutor.newSourceExecutor();}

  
if (diskCacheExecutor == null) {diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();}

  
if (animationExecutor == null) {animationExecutor = GlideExecutor.newAnimationExecutor();}

  
if (memorySizeCalculator == null) {memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();}

  
if (connectivityMonitorFactory == null) {connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();}

  
if (bitmapPool == null) {int size = memorySizeCalculator.getBitmapPoolSize();if (size > 0) {bitmapPool = new LruBitmapPool(size);} else {bitmapPool = new BitmapPoolAdapter();}}

  
if (arrayPool == null) {arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());}

  
if (memoryCache == null) {memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());}

  
if (diskCacheFactory == null) {diskCacheFactory = new InternalCacheDiskCacheFactory(context);}

  
if (engine == null) {engine =new Engine(memoryCache,diskCacheFactory,diskCacheExecutor,sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor(),GlideExecutor.newAnimationExecutor(),isActiveResourceRetentionAllowed);}

  
RequestManagerRetriever requestManagerRetriever =new RequestManagerRetriever(requestManagerFactory);


  return new Glide(context,engine,memoryCache,bitmapPool,arrayPool,requestManagerRetriever,connectivityMonitorFactory,logLevel,defaultRequestOptions.lock(),defaultTransitionOptions);}

glide参数含义:

context上下文,engine:任务和资源管理(线程池,内存缓存和硬盘缓存对象),memoryCache:内存缓存,bitmapPool:bitmap内存缓存,后续会单独介绍。

arrayPool: connectivityMonitorFactory:回调监听,defaultRequestOptions:默认请求配置,defaultTransitionOptions:默认过度效果

getRetriever(activity).get(activity)

然后我们看一下get(activity)的流程:

public RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}

流程图如下:

主要工作是,创建一个supportFragment(SupportRequestManagerFragment),把Request和Fragment绑定在一起,主要是生命周期。

引用xiaodanchen.github.io/2

文章中的一个图片:


总结以下with中的工作主要有以下几点:

  1. 初始化配置信息(包括缓存,请求线程池,大小,图片格式等等)以及glide组件,
  2. 将glide和Fragment的生命周期绑定在一块。

Load

asDrawable().load(string);

先分析一下asDrawable:


最终返回RequestBuider对象

into

private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,@Nullable RequestListener<TranscodeType> targetListener,@NonNull RequestOptions options) {Util.assertMainThread();Preconditions.checkNotNull(target);if (!isModelSet) {throw new IllegalArgumentException("You must call #load() before calling #into()");}

  
options = options.autoClone();Request request = buildRequest(target, targetListener, options);


  Request previous = target.getRequest();if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {request.recycle();// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
    
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
 
   if (!Preconditions.checkNotNull(previous).isRunning()) {// Use the previous request rather than the new one to allow for optimizations like skipping
 
     // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions

      // that are done in the individual Request.
 
     previous.begin();}return target;}

  requestManager.clear(target);target.setRequest(request);requestManager.track(target, request);


  return target;}

构建Request对象,实际是在buildRequestRecursive里面创建了一个buildThumbnailRequestRecursive的对象以及errorRequestCoordinator(异常处理对象),buildThumbnailRequestRecursive会进行判断是否采用thumbnail方式进行展示,如果没有,则到了obtainRequest,创建SingleRequest<>并初始化最后调用requestManager.track(target,request);

track干了两件事:

  1. 加入target目标队列(view)
  2. 加入请求Request队列,如果缓存中没有开始请求数据
  3. 接着往下分析:
@Override
public void begin() {

  assertNotCallingCallbacks();

  stateVerifier.throwIfRecycled();

  startTime = LogTime.getLogTime();

  if (model == null) {

    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

      width = overrideWidth;

      height = overrideHeight;

    }
    // Only log at more verbose log levels if the user has set a fallback drawable, because

    // fallback Drawables indicate the user expects null models occasionally.

    int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;

    onLoadFailed(new GlideException("Received null model"), logLevel);

    return;

  }


  if (status == Status.RUNNING) {

    throw new IllegalArgumentException("Cannot restart a running request");

  }

  // If we're restarted after we're complete (usually via something like a notifyDataSetChanged

  // that starts an identical request into the same Target or View), we can simply use the

  // resource and size we retrieved the last time around and skip obtaining a new size, starting a

  // new load etc. This does mean that users who want to restart a load because they expect that

  // the view size has changed will need to explicitly clear the View or Target before starting

  // the new load.

  if (status == Status.COMPLETE) {

    onResourceReady(resource, DataSource.MEMORY_CACHE);

    return;

  }

 
 // Restarts for requests that are neither complete nor running can be treated as new requests

  // and can run again from the beginning.


  status = Status.WAITING_FOR_SIZE;

  if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

    onSizeReady(overrideWidth, overrideHeight);

  } else {

    target.getSize(this);

  }

  
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
 && canNotifyStatusChanged()) {

    target.onLoadStarted(getPlaceholderDrawable());

  }
  
if (IS_VERBOSE_LOGGABLE) {

    logV("finished run method in " + LogTime.getElapsedMillis(startTime));

  }

}








不管是onSizeReady还是target.getSize(ImageViewTarget->ViewTarget->getSize->cb.
onSizeReady),最终都会走到onSizeReady。

begin在onSizeReady执行engine.load,
先从弱引用中查找loadFromActiveResources(),如果有的话直接返回,没有再从内存中查找loadFromCache, 有的话会取出并放到ActiveResources里面,如果内存中没有,则创建engineJob(decodejob的回调类,管理下载过程以及状态)线程decodeJob,先下载,后解析,并把Job放到Hashmap里面。流程图如下:

开启线程是从engineJob.start开始的,流程图如下:

看一下DecodeJob线程的run方法,实际起作用的是runWrapped方法:

private void runWrapped() {

  switch (runReason) {

    case INITIALIZE:

      stage = getNextStage(Stage.INITIALIZE);

      currentGenerator = getNextGenerator();

      runGenerators();

      break;

    case SWITCH_TO_SOURCE_SERVICE:

      runGenerators();

      break;

    case DECODE_DATA:

      decodeFromRetrievedData();

      break;

    default:

      throw new IllegalStateException("Unrecognized run reason: " + runReason);

  }

}

完整执行的情况下,会依次调用ResourceCacheGenerator、DataCacheGenerator和SourceGenerator中的startNext()

首次下载图片创建的是SourceGenerator:

runGenerators流程如下:

调用了SourceGenerator的startNext方法:

  1. dataToCache数据不为空的话缓存到硬盘(非第一次)
  2. 在modelLoaders里面找到ModelLoder对象(每个Generator对应一个ModelLoader)
  3. 通过(HttpGlideUrlLoader)buildLoadData获取到实际的loadData对象(key 为URL,value创建的HttpUrlFetcher对象)
  4. 通过loadData对象的fetcher对象(HttpUrlFetcher)的loadData方法来获取图片数据
  5. HttpUrlFetcher 通过HttpURLConnection网络请求数据

时序图如下:

至此,分析完毕。

参考:

Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能 - CSDN博客blog.csdn.net图标跟着源码学设计:Glide框架及源码解析(三)xiaodanchen.github.io图标Glide之旅 -- Registry - CSDN博客blog.csdn.net

发布于 2018-05-26