Glide源码和缓存

1.主要方法流程(4.9.0)

看下用法,主要是 with 、load和into三个主要的模块。

Glide.with(this)
        .load(url)
        .into(imgView);
  • with方法
    先来看with,是通过RequestManager这个方法来实现的,可以看到这几个重载的方法作用其实一样就是传入当前页面的上下文(Activity或者Fragment的)
RequestManager with(Context context)
RequestManager with(android.app.Activity)
RequestManager with(android.app.Fragment)
RequestManager with(android.support.v4.app.Fragment)
RequestManager with(android.support.v4.app.FragmentActivity)

重点是这个方法,无论在哪里调用,都会传入一个FragmentManager


image.png

这里用到了这个传入的fm,用来生成一个fragment,这个fragment,是绑定在当前Activity中的。

  private RequestManager fragmentGet(@NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = getRequestManagerFragment(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 =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

可以看到RequestManager被创建,是需要一个FragmentManager的,其实他的作用是帮我们创建一个空的Fragment来实现对当前Activity页面的声明周期的监听,其实这也是with这个方法或者RequestManager主要功能,Glide通过在这个空Fragment里的生命周期监听回调,就可以感知到Activity的声明周期,这样就省了我们自己去维护了。比如之前用过的EventBus,在onCreate里register,在onDestroy里unregister,Gilde这里就全帮我们做了,省了我们自己去维护了。

  • load方法
    首先这个方法重载了一堆方法,不得不说Glide牛逼,都帮我们考虑到了,你能想到的加载图片的方式,一个load就都能传进去
Glide.with(this).load(R.drawable.ic_android).into(imageView);
Glide.with(this).load("http://xxx.xxx.png").into(imageView);
Glide.with(this).load(Uri.parse("xxxxx")).into(imageView);
Glide.with(this).load(getResources().getDrawable(R.drawable.ic_android)).into(imageView);
Glide.with(this).load(new File("xxx")).into(imageView);
Glide.with(this).load(BitmapFactory.decodeFile("xxx")).into(imageView);

然后会进步的根据我们传入类型不同,做进一步的处理

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
  ······
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }
  @NonNull
  @CheckResult
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  @NonNull
  @CheckResult
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
  ······

最后会返回这个RequestBuilder对象,然后在下一部进行处理。

  • into方法
    into方法一般都是传递了一个 ImageView 图片控件,然后Glide就会帮我们把之前传进去的资源给绘制到ImageView上,继上一个RequestBuilder对象
public class RequestBuilder<TranscodeType> implements Cloneable,
    ModelTypes<RequestBuilder<TranscodeType>> {
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);//判断传进来的 ImageView 是否为空
    RequestOptions requestOptions = this.requestOptions;
  ······
    }

    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }
}

当我们调用 RequestBuilder.into 方法时会根据传入参数创建对应类型的 Target 实现类。这里我们不考虑bitmap或者drawble,只看网络相关的,默认创建的Request对象是SingleRequest,由于默认是第一次加载图片,所以我们来看RequestManager的track方法。

  void track(@NonNull Target<?> target, @NonNull Request request) {
    ...
    requestTracker.runRequest(request);
  }
  //RequestTracker中
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {//当没有暂停时Request就开始执行
      request.begin();
    } else {//暂停执行
      request.clear();
      ...
      pendingRequests.add(request);
    }
  }

由于这里Request的具体实现是SingleRequest,所以我们来看它的begin方法。

  public void begin() {
    ...
    //传入的model为null,在本文中就是传入的URL为null
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      ...
      //图片加载失败
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }

    //从缓存中拿数据
    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 {
      //根据View的宽高来计算出图片的宽高,最后回调的也是onSizeReady方法
      target.getSize(this);
    }

    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      //图片开始加载时的默认显示
      target.onLoadStarted(getPlaceholderDrawable());
    }
    ...
    }
  }
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    ...
    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.load这个方法,这里边是Glide一些缓存的核心方法

public <R> LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb) {
    ...
    //根据model计算出每张图片对应的key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    //从ActiveResources中获取图片,ActiveResources是一个弱引用的HashMap
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      return null;
    }
    //从缓存中获取图片,采用了Lrucache
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }

    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      return new LoadStatus(cb, current);
    }

    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);

    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);
    engineJob.addCallback(cb);
    //从本地或者网络获取数据,会切换到子线程
    engineJob.start(decodeJob);
    return new LoadStatus(cb, engineJob);
  }

engineJob.start(decodeJob)这个方法就开始进行线程切换和触发网络请求了

  public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    //向子线程添加任务
    executor.execute(decodeJob);
  }
image.png

2.缓存

主要是三级的一个缓存,ActiveResources缓存(内存)-> MemoryCache缓存(内存)->硬盘->都没有发起网络请求


image.png

看下上边那个engine.load里的方法

    //从ActiveResources中获取图片,ActiveResources是一个弱引用的HashMap
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      return null;
    }
    //从缓存中获取图片,采用了Lrucache
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }

正常先从网络加载到数据,然后放入磁盘缓存中,然后ActiveResources中可以引用到,计数+1,当ActiveResources中不再用到的时候,此时从自身移除(先不考虑GC),然后会放入到MemoryCache的中,而当MemoryCache也存储满了的时候(LRU算法),就会从自身移除掉,当这个图片再需用使用的时候,又会从磁盘开始重复上边的流程了。

需要注意的是在活动缓存里去取,如果取不到才去MemoryCache内存缓存中去,这两个存储是互斥的,如果在MemoryCache找到了,就会把MemoryCache中的删掉,放入到ActiveResources中去。
为什么有MemoryCache内存缓存,还需要弱引用的活动缓存呢?个人理解是MemoryCache 缓存是LRU算法的,里边会有一个maxsize,如果是最少使用,内部算法就会去回收了他,而当我们如果大量图片频繁滑动使用的时候,这时候可能会导致回收加快。ActiveResources活动内存是用弱引用,如果当前内存足够,理论上是会比MemoryCache缓存更多的资源,避免重复回收,同时当如果内存真的超出触发GC时候,那些没有被使用的资源也会通过GC被动回收。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容