Android 进阶5:Glide4.0源码分析

这篇博客其实准备了很久,从Glide的各种使用到Glide的源码分析,Glide的源码很复杂,个人感觉,比LeakCanary,Eventbus等框架源码复杂很多,网上说别Picasso源码复杂,以后再看Picasso的源码吧。

本片博客打算分为三个部分

  1. Glide的使用以及原理
  2. Glide的源码分析
  3. Glide的生命周期

1: Glide的使用

其实关于Glide 的使用,网上有很多,基本都是大同小异,在此不再陈述,这里说一些Glide的比较生疏的用法;

自定义添加动画

当我们第一次加载动画的时候:

    private void glideAddAnimation() {
        Glide.with(this).load(imageUrl).transition(GenericTransitionOptions.with(R.anim.my_anim)).into(imageView);
    }

除了GenericTransitionOptions之外,还有BitmapTransitionOptions和DrawableTransitionOptions,三个的区别不再赘述;R.anim.my_anim也就是我们自己自定义的动画。

上述代码当第一次运行的时候,你会发现挺好的,但是当你第二次运行的时候(相同URL),没有动画过渡;因为上述动画仅仅针对第一次从网络加载图片有效,第二次从缓存获取图片的过渡动画需要另外设置。

        Glide.with(this).load(imageUrl)
                .transition(GenericTransitionOptions.with(R.anim.my_anim)).listener(
                new RequestListener<Drawable>() {
                    @Override
                    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target,
                            boolean isFirstResource) {
                        return false;
                    }


                    @Override
                    public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target,
                            DataSource dataSource,
                            boolean isFirstResource) {
                        if (dataSource == DataSource.MEMORY_CACHE) {
                            imageView.startAnimation(
                                    AnimationUtils.loadAnimation(getApplicationContext(), R.anim.my_anim));
                        }
                        return false;
                    }
                }).into(imageView);

添加Listener监听,当dataSource代表数据来源,等于MEMORY_CACHE(内存缓存)中获取时,添加动画。

给图片添加动画比较消耗性能,甚至比解码原图资源消耗的都大,所以为了提升性能,请在使用 Glide 向 ListView , GridView, 或 RecyclerView 加载图片时考虑避免使用动画。这是官方的原话。

加载圆角图片

在说圆角图片之前,先说一下,,Glide是支持圆形图片的:

    private void glideCircle() {
        Glide.with(this).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);
    }

关于圆角图片就需要自定义BitmapTransformation了,代码如下:


/**
 * 圆角图片
 * Created by ${liumengqiang} on 2018/10/9.
 */

public class SecondRoundTransform extends BitmapTransformation {

    private float radius = 10;
    @Override
    protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        //转化后的Bitmap(centerCrop)
        Bitmap centerCropBitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);

        //从BitmapPool(可以理解为Bitmap池)获取一份Bitmap
        Bitmap roundBitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        //创建画布,在RoundBitmap写入数据
        Canvas canvas = new Canvas(roundBitmap);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(centerCropBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
        RectF rectF = new RectF(0, 0, centerCropBitmap.getWidth(), centerCropBitmap.getHeight());
        //圆角
        canvas.drawRoundRect(rectF, radius, radius, paint);

        return roundBitmap;
    }

    @Override
    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {

    }
}
    private void glideRound() {
        Glide.with(this).load(imageUrl).apply(RequestOptions.bitmapTransform(new SecondRoundTransform()))
                .into(imageView);
    }

在上述自定义BitmapTransformation中,为了防止频繁创建Bitmap造成内存抖动,所以是从BitmapPool中获取Bitmap对象。
官方自定BitmapTransformation 文档:https://github.com/bumptech/glide/wiki/Transformations

缓存策略

关于缓存策略,3.0和4.0差别还是比较大的

Glide3.0

			DiskCacheStrategy.NONE:不缓存
			DiskCacheStrategy.SOURCE:之缓存原图
			DiskCacheStrategy.RESULT:只缓存最终要显示的图片(默认选项; Picasso默认缓存原图)
			DiskCacheStrategy.ALL:缓存所有版本的图片

Glide4.0

			DiskCacheStrategy.ALL 使用DATA和RESOURCE缓存远程数据,仅使用RESOURCE来缓存本地数据。
			DiskCacheStrategy.NONE 不使用磁盘缓存
			DiskCacheStrategy.DATA 在资源解码前就将原始数据写入磁盘缓存
			DiskCacheStrategy.RESOURCE 在资源解码后将数据写入磁盘缓存,即经过缩放等转换后的图片资源。
			DiskCacheStrategy.AUTOMATIC 根据原始图片数据和资源编码策略来自动选择磁盘缓存策略。(默认策略)

关于4.0的默认策略官方是这样说的:
默认的策略叫做 AUTOMATIC ,它会尝试对本地和远程图片使用最佳的策略。当你加载远程数据(比如,从URL下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过(比如,变换,裁剪–译者注)的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。

Glide加载一张新图片时候会检查多级的缓存:

  1. 活动资源(ActivityResource):现在是否有另一个ImageView在显示此图片(宽高也一致)
  2. 内存资源(MemoryCache):图片是否存在在内存中
  3. 资源类型(Rource):图片是否曾被解码之后写入过磁盘缓存
  4. 数据来源(Data):原始数据图片是否写入过文件缓存

第一步和第二步检查都是针对于内存的,如果有就直接返回图片。后两步是发生在磁盘上的,需要异步处理。

每个图片都对应一个Key,这个Key的生成参数:图片路径,Transformtion,Options,请求数据类型等参数。
这些参数经过哈希处理之后就生成一个单独的Key。

Glide默认网络请求方式

Glide默认的网络请求实现是:HttpUrlConnection;
添加implementation 'com.github.bumptech.glide:okhttp3-integration:4.3.1’之后默认的加载数据使用时OkHttp。
Glide也支持Volley加载数据,但是其性能要低于OKHttp。

Glide常见需求

  1. 路径不变,但是服务器的图片变了
  2. 为了保证图片安全,图片带token加载数据(比如七牛云图片)
  3. 请求自定义尺寸图片

对于需求1,该问题的产生是由于Glide的缓存机制,Key缓存是根据图片路径以及其它的一些参数决定的,造成的后果就是请求的永远是缓存中的老图片,有同学会说跳过磁盘缓存就行了,这样确实行,Glide的强大之处就在于缓存机制。再次我们提供的思路是在Key上下功夫,思路就是:定期刷新缓存策略或者是key添加标记(例如version版本号)。官网:https://muyangmin.github.io/glide-docs-cn/doc/caching.html

需求2是常见的需求,但是本身项目中没有遇到,所以没有发言权,附上解决方案:https://blog.csdn.net/wangdaqi77/article/details/72819097。
至于这个解决方案原理我们下文讲到。

需求3的场景就是:请求一定尺寸大小的图片。官方解决方案:https://github.com/bumptech/glide/wiki/Downloading-custom-sizes-with-Glide

通过上文中我们可以提出问题点:
Glide本质的请求加载数据使用的是HttpUrlConnection(默认,不导入Okhttp)

2: Glide的源码流程分析

关于Glide的源码3.0 和4.0 的差别还是很大的,从缓存策略就可以看出。接下来分析的Glide源码针对于Glide4.0。
我们最简单的使用Glide代码示下:

Glide.with(this).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(imageView);

典型的链式结构,简简单单的一句话就可以加载图片,殊不知Glide在背后为我们做了大量的工作。

先看Glide.with(this) :

//TODO 思考:为什么传入FragmentActivity对象。
  @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

可以看到该方法返回的了RequestManager对象,对于上文的思考,我们先给出答案:为了GLide的生命周期和Activity的生命周期绑定。 其实with接收的参数有好几种类型,自行查看 ,在此不再赘述。

  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }
  @NonNull
  public static Glide get(@NonNull Context context) {
  	//单例模式,这里运用了加锁机制以及双判断,防止并发。
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }
  private static void checkAndInitializeGlide(@NonNull Context context) {
  		.......
  		initializeGlide(context);
  		......
  }
private static void initializeGlide(@NonNull Context context) {
    // 在此处创建了GlideBuilder对象,既然Glide是单例的,那么这个对象同样也是单例的。
    initializeGlide(context, new GlideBuilder());
  }

  @SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    .........
    
    if (Log.isLoggable(TAG, Log.DEBUG)) {
    	//这里开始加载manifest配置,不过为了提升性能,在自定义GlideMoudle的时候,我们都禁止从manifest读取信息了,这样能够提高新能
      for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
        Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
      }
    }

	//是否设置了RequestManagerFactory,一般是null。
    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //这里返回了Glide对象,重点分析。
    Glide glide = builder.build(applicationContext);
    .......
    Glide.glide = glide;
  }

通过上述代码,我们能够总结一下Gide.get(context)方法中主要做了几件事:

  1. 创建GlideBuilder对象
  2. 通过GlideBuilder的build方法返回Glide对象。

我们进入build方法看下是怎样创建Glide对象的。


  @NonNull
  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();
    }
	//Bitmap缓存池创建
    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());
    }

	//内存加载(Lru算法)
    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);

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

GlideBuilder的build方法主要做了三件事:

  1. 创建各种加载器或者执行器
  2. 创建RequestManagerRetriever对象
  3. 创建Glide对象,并且将前两步创建的对象都回传给Glide。

我们看下RequestManagerRetriever的构造方法:

  public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
  	//由于我们没有传递factory,所以默认使用DEFAULT_FACTORY。
    this.factory = factory != null ? factory : DEFAULT_FACTORY;
    //创建handler对象,回调给主线程
    handler = new Handler(Looper.getMainLooper(), this /* Callback */);
  }
  
    private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
    @NonNull
    @Override
    //此处返回的是RequestManager对象,需要注意。
    public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
        @NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
      return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
    }
  };

通过默认的DEFAULT_FACTORY的build方法,返回的是RequestManager对象.。

至此我们的GLide对象创建好了,重新回答with方法,看下get方法,其实get方法最终调用的都是RequestManagerRetriever类的supportFragmentGet方法,

  @NonNull
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      //创建RequestManager对象
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

factory默认是DEFAULT_FACTORY,通过build方法创建requestManager对象。至此Glide.with(this)最终创建RequestManager对象就分析完了。

接下来看下load(url)方法:

	//通过RequestManager的load方法返回RequestBuilder对象。
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }
  
   public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  //创建RequestBuidler对象
    public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

其实load是有好几个重载方法的,各位可以自行查看,上述代码做的事就是:创建RequestBuidler对象。顾名思义:请求构造,也就是请求参数设置啦。注意一下asDrawable方法内as方法的参数类型就是Drawable.class。

再来看下RequestBuilder对象的load方法:

  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

此处我们需要注意的是,我们的路径赋值给了model对象!这个下文会用到的。

请求构造好了,是不是该添加请求设置了? 没错,就是Glide的load方法之后into方法之前中间的一系列设置,在此不再详述。那么设置都设置好了,请求数据在哪里呢? 看into(imageView)方法:

  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
   ........
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

重载了into方法,先来看下glideContext.buildImageView方法,这里参数view就是我们的ImageVIew,transcodeClass是在RequestBuilder构造方法中赋值的,还记得上文中的as方法的参数就是Drawable.class,所以此处的transcodeClass就是Draable.class对象。

  @NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
  
  public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
      @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
}

由于clazz是Drawable.class,所以返回的是DrawableImageViewTarget对象。这个东西下文会用到。
绕了一大圈,重新进入到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();
    //这个target就是DrawableImageViewTarget对象,需要注意
    Request request = buildRequest(target, targetListener, options);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();

      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // 默认情况下,当我们加载一次图片之后,再次加载该图片,如果请求还存在,就直接请求,
        //再次我们是第一次加载,所以不再详述
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    //给target添加一个新请求 
    target.setRequest(request);
    //加载请求
    requestManager.track(target, request);

    return target;
  }

上述代码主要做了三件事

  1. 创建请求,
  2. 判断请求是否已经运行过了,如果运行过了,就直接请求
  3. 如果是新的请求,那么加载请求

我们先看下创建请求Request。buildRequest最终调用的方法就是:buildThumbnailRequestRecursive

  private Request buildThumbnailRequestRecursive(一堆参数) {
  	
    if (thumbnailBuilder != null) {
    //省略代码,判断是否添加了缩略图,显然我们这里没有添加缩略图
    } else {
      // Base case: no thumbnail.
      return obtainRequest(
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight);
    }
  }

  private Request obtainRequest(一堆参数) {
  //创建SingleRequest对象
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory());
  }

上述代码主要就是创建请求只不过就是看创建哪一种类型的请求,由于我们没有添加缩略图,所以最终创建的是SingleRequest对象。

有了请求对象,就该执行请求了,我们看下requestManager.track(target, request);

  void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
    //开始一个请求
      request.begin();
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

由于我们创建的是SingleRequest对象,所以进入到其begin方法内:

  @Override
  public void begin() {
    .......

	//该Request是否正在运行
    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }
    
    //该请求是否已经完成了
    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 {
    //先测量size
      target.getSize(this);
    }

   ........
  }

如果我们没有调用override方法设置宽高,那么此时的宽高都是没有值的,所以此时会先测量宽高; 需要注意getSize的参数是this,进入到其方法内:

  @CallSuper
  @Override
  //cb也就是SingleRequest对象。说明最后还要回到给SingleRequest对象
  public void getSize(@NonNull SizeReadyCallback cb) {
    sizeDeterminer.getSize(cb);
  }
  
    void getSize(@NonNull SizeReadyCallback cb) {
    //获取目标image View的宽高
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
      //回调给SingleRequest的onSizeReady方法执行。
        cb.onSizeReady(currentWidth, currentHeight);
        return;
      }

     ........
    }

上述代码做了一件事就是获取ImageView的宽高,然后在回调给SingleRequest的onSizeReady方法:

  @Override
  public void onSizeReady(int width, int height) {
   	........
    //设置标记
    status = Status.RUNNING;

   .......
    loadStatus = engine.load(
        glideContext,
        model,
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),
        transcodeClass,
        priority,
        requestOptions.getDiskCacheStrategy(),
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.isScaleOnlyOrNoTransform(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        requestOptions.getUseAnimationPool(),
        requestOptions.getOnlyRetrieveFromCache(),
        this);

    .........
  }

看到engine对象是不是眼熟呢? 它是在GlideBuilder的build方法中创建的,上文中有提到。 执行了engine.load方法,参数一大堆,注意一下最后一个参数:this, 也就是SiingleRequest对象! 需要特别注意。看下load方法做了什么:

  public <R> LoadStatus load(一大堆参数) {
    Util.assertMainThread();
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

	//此处生成缓存的key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
	
	//检查活动资源中是否存在要请求的资源
    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;
    }

	//检查缓存中是否存在要请求的资源
    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<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
    	//此处需要特别注意,添加SingleRequest对象给EngineJob。
      current.addCallback(cb);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

	//创建一个工作执行器
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
            
	//DecodeJob继承自Runnable接口,等待执行器  执行任务
    DecodeJob<R> decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);

    jobs.put(key, engineJob);
	//添加回调SingleRequest给EngineJob。
    engineJob.addCallback(cb);
    //开始执行
    engineJob.start(decodeJob);
	.......
  }

上述代码充分体现了Glide的缓存机制,首先从活动资源查找资源,再从缓存查找资源,如果都没找到,就返回开启异步执行任务。需要注意两点:

  1. decodeJobFactory.build返回的是DecodeJob对象,最后一个参数是EngineJob对象,在DecodeJob的init方法内,将EngineJob对象赋值给了callback对象,此处需要格外注意!
  2. 创建EngineJob之后,调用了addCallbackf方法,这样,EngineJob就拥有了SingleRequest实例

看一下创建Key的代码,可以看出Key是由图片路径,宽高等相关设置共同决定的。老版本3.0的代码是:

        final String id = fetcher.getId();
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());

这个和老版本的代码还是有差距的,上述需求点访问七牛云图片就是在这里做了手脚。
进入到EngineJob的start方法:

  public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

执行decodejob ,之后就都是异步了,进入到DecodeJob的run方法内:

 @Override
  public void run() {
    .......
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      //进入此方法
      runWrapped();
    } catch (Throwable t) {
     .........
  }
  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE: //runReason的初始化实在DecodeJob的init方法中初始化的 默认是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);
    }
  }
  
  //递归返回Stage.SOURCE
  private Stage getNextStage(Stage current) { //current是INITIALIZE
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

关于DecodeJob的runReason变量初始化可以自行查找。getNextStage方法的current参数是INITIALIZE,所以先在磁盘是否有图片转换处理过的缓存,显然是没有的,在查找是否存在图片的原始数据,很显然,还是没有;此时就返回Stage.SOURCE,开始从网络查找。

至此本地缓存就算都遇见了:

  1. 活动资源查找
  2. 内存缓存查找
  3. 磁盘查找——转换过的图片缓存
  4. 磁盘查找——原视图片数据

接下来看下getNextGenerator方法:

  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE: //网络获取数据
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

由于是从网络加载数据,所以返回SourceGenerator,也就是说currentGenerator就是SourceGenerator对象。
接下来就是runGenerators方法了:


  private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();
	.........
  }

重点看startNext方法,进入到SourceGenerator的startNext方法:


  @Override
  public boolean startNext() {
    .......
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        //重点
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

此处调用了fetcher.loadData方法,Glide默认加载使用的是HttpUrlFetcher方法,注意一下参数this代表的是SourceGenerator类对象:

 @Override
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
    //输入流,是不是惊喜?
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } 
    ......
  }
 
  private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {
   //使用的是HttpUrlConnection
    urlConnection = connectionFactory.build(url);
    .......
  }

最终使用的还是HttpUrlConnection,返回输入流,返回输入流给SourceGenerator:

  public void onDataReady(Object data) {
    ......
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

在回调给 DecodeJob ,cb就是DecodeJob对象,上文有让注意的。

 @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    .........
    decodeFromRetrievedData();
    .........
  }
 private void decodeFromRetrievedData() {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Retrieved data", startFetchTime,
          "data: " + currentData
              + ", cache key: " + currentSourceKey
              + ", fetcher: " + currentFetcher);
    }
    Resource<R> resource = null;
    try {
    //此处将操作输入流,并且经过转换图片返回资源。
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }
  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
   	........
   	notifyComplete(result, dataSource);
   	........
  }
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }

上述代码的主要流程就是操作读取流, 转化图片资源,并通过回调返回给SingleRequest, 这个callback就是EngineJob对象,上文有让注意的,实际上上述代码流程还是很复杂的,内部还进行了转化图片,缓存图片等等操作

  @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
  }
  @Synthetic
  void handleResultOnMainThread() {
    .....
    cb.onResourceReady(engineResource, dataSource);
    .....
  }

发送handle信息,切回UI主线程。此处又有一个回调,cb就是我们的SingleRequest,上文有注意点;

  @SuppressWarnings("unchecked")
  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) {
   	.......
    onResourceReady((Resource<R>) resource, (R) received, dataSource);
  }
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    ........
    target.onResourceReady(result, animation);
    ........
  }

上述的target就是DrawableImageViewTarget对象,这个上文也是有让注意的。但是在DrawableImageViewTarget类继承自ImageViewTarget,在ImageViewTarget中定义了onResourceReady方法:

  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else { //是否添加了过渡动画
      maybeUpdateAnimatable(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);
  }

setResource是个抽象方法,在子类DrawableImageViewTarget中实现了:

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

这里的view就是ImageView!
整个流程走完了,烧脑,叉会儿腰,喝杯水,再来看Glide加载数据的生命周期

猜你喜欢

转载自blog.csdn.net/lmq121210/article/details/83004031