Glide可以将https,文件路径,base64等转化为图片

它的基本用法就是Glide.with(context).load(url).into(imageView);

这篇博客主要就将Glide中with,load,into的源码和Glide的三级缓存

1.Glide的源码

1.1with

with的主要作用就是调用get方法,返回一个RequestManager对象

在RequestManager的get方法中  @NonNull
 public RequestManager get(@NonNull Context context) {
   if (context == null) {
     throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {
     if (context instanceof FragmentActivity) {
       return get((FragmentActivity) context);
    } else if (context instanceof Activity) {
       return get((Activity) context);
    } else if (context instanceof ContextWrapper
         // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
         // Context#createPackageContext may return a Context without an Application instance,
         // in which case a ContextWrapper may be used to attach one.
         && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
       return get(((ContextWrapper) context).getBaseContext());
    }
  }

   return getApplicationManager(context);
}


//调用get判断线程

@NonNull
 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));
  }
}

在get中会判断当前线程是否为子线程,如果是子线程的话,那么直接  return get(activity.getApplicationContext());

然后获取与应用程序的全局上下问相关联的RequestManager对象

如果不是的话,那么就将你的activity添加生命周期,然后再返回RequestManager对象

1.1.1为什么不能在子线程调用with方法

因为在子线程调用之后并不会将context与Activity或者Fragment进行绑定,当Activity处于onDestroy()的时候,图片请求资源仍然存在,导致内存泄漏

1.2load

因为我们在with()中获得了一个RequestManager,调用load(url),因为传入的可能是不同类型的,所以有很多个重载方法。最终拿到RequestBuilder对象

// RequestManager.java 的代码如下public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
   return asDrawable().load(bitmap);
}

 public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
   return asDrawable().load(drawable);
}

 public RequestBuilder<Drawable> load(@Nullable String string) {
   return asDrawable().load(string);
}

 public RequestBuilder<Drawable> load(@Nullable Uri uri) {
   return asDrawable().load(uri);
}

 public RequestBuilder<Drawable> load(@Nullable File file) {
   return asDrawable().load(file);
}

 public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
   return asDrawable().load(resourceId);
}

 public RequestBuilder<Drawable> load(@Nullable URL url) {
   return asDrawable().load(url);
}

 public RequestBuilder<Drawable> load(@Nullable byte[] model) {
   return asDrawable().load(model);
}

 public RequestBuilder<Drawable> load(@Nullable Object model) {
   return asDrawable().load(model);
}

最终拿到RequestBuilder对象

1.3into()

我们在with()中拿到了RequestManager对象,然后通过load()让Drawable加载你传入的参数,返回一个RequestBuilder对象,然后进入into

我们点开into的源码,因为into里面的源码太多,太长,所以现在只需要看如何显示ImagView就行了return into(
   glideContext.buildImageViewTarget(view, transcodeClass),
   /*targetListener=*/ null,
   requestOptions,
   Executors.mainThreadExecutor());

当into内部代码执行完成后回到 buildImageViewTarget方法,这个方法是显示使用的,通过Executors.mainThreadExecutor())来切主线程,最终显示控件。

这也符合我们的多线程的一个原则,多线程中不允许更改UI

into的主要作用是从缓存或者通过网络请求获取图片资源并最终将图片显示

1.配置缓存策略,裁剪大小,动画效果,优先级,并将ImageView包装成一个target对象,接下来通过我们在with中创建的RequestBuilder,然后用它创建一个Request对象,将它与target对象绑定,最后调用request的图片请求方法

2.加载前先判断Glide当前是否处于暂停状态,如果不处于暂停状态,才会真正执行加载操作。在加载时会对当前任务状态进行校验,避免重复加载,然后获取目标图片的宽高(实现按需加载,避免浪费内存空间),之后进行真正的加载。

3.(1)首先在弱引用缓存中查找目标图片,有的话直接复用;

(2)如果没有的话,在LruCache中查找图片,有的话直接复用;

(3)如果没有的话,在磁盘中查找图片,有的话直接复用;

(4)如果都没有的话,就发送网络请求来获取图片

稍微总结一下这三段话:

1.将图片的长宽,动画效果等设置好,然后把ImageView包装成一个target,通过RequestBuilder来创建一个Request来和target进行一个绑定

2.如果Glide不处于暂停状态,那么就加载。处于的话就算了(什么时候会处于暂停状态呢?因为我们在withglideActivity绑定起来了,所以当Activity执行到OnResume()的时候,glide就会暂停;或者当你手动调用Glide.with(context).pauseRequests();)

注意:当将Glide设置为暂停状态并不会取消已经发起的请求,只是暂时停止新的加载请求。已经发起的请求会在恢复Glide的正常状态后继续执行,通过调用Glide.with(context).resumeRequests()来恢复Glide的正常状态,执行之前暂停的加载请求

3.就是先在缓存中找有没有目标图片,有的话就直接用,没有的话,则网络请求

2.Glide的缓存

Glide的缓存,总共是3级

一级缓存:活动缓存 ,当前Activity退出缓存销毁。

二级缓存:LRU内存缓存 ,APP应用退出缓存销毁。

三级缓存:LRU磁盘缓存 ,一直存在。

(1)活动缓存:Glide自己实现的一种缓存策略,将使用的对象存放在HashMap,里面使用的弱引用,不需要时立即移除及时释放资源。

(2)内存缓存:使用的LRU算法进行处理,核心是使用 LinkedHashMap 实现,保存到内存中。

(3)磁盘缓存:使用的LRU算法进行处理,核心是使用 LinkedHashMap 实现,保存到磁盘中。(Glide使用DiskLruCache实现,将图片进行的加密、压缩处理,所以文件读写比普通IO处理效率高)

2.1缓存的实现

Glide的缓存都通过一个key来实现,就是EngineKey对象,其中有请求的Url、请求的签名、ImageView的宽高等参数,在获取缓存之前都会创建这个key。

Glide的缓存分为内存缓存和磁盘缓存,而内存缓存又分为ActiveResources中的弱引用HashMap缓存和MemoryCache这个LruCache。我们分为取缓存和存缓存两个方式来说明。

2.2取缓存

取缓存的顺序是先从ActivityResources中取出HashMap中的弱引用对象,然后如果没有的话,从MemoryCahce中取出LruCache,如果MemoryCache也没有的话,那么从DiskLruCache中取出

如果DiskLruCache中也没有的话,那么网络请求

2.3存缓存

1.在获取图片资源后最终会回调EngineJobonResourceReady()将获取到的图片进行显示,然后将数据存到弱引用中。如果acquired引用计数为0的话,则代表没有地方使用这个资源了(就可以说HashMap的大小不够放下了,它不会进行扩容),就将最先放进去的没有用到的对象放进LruCache中,然后把新的放进Memory Cache

2:LruCache:如果从LruCache中找到后,会将缓存从LruCache中删除,然后加入弱引用队列HashMap中,同时MemoryCache中的缓存数量+1

3:DiskLruCache:在网络请求后如果有必要的话,会存放在DiskLruCache中,首先会判断是否允许缓存原始图片,如果不允许的话,对图片进行转码,然后缓存

2.4如何监听生命周期

基于在Activity中添加无UI的Fragment,通过Fragment接收Activity传递的生命周期。Fragment和RequestManager基于LifeCycle接口建立联系,并传递生命周期事件,实现生命周期感知。

当我们调用 Glide.with(FragmentActivity)方法时,此时就已经完成了 无UI的Fragment 的注入,这个空白的Fragment会监听view所在的Actvity的生命周期。

注意:

如果外部是通过子线程来调用的,那么就统一使用 Application,此时就不需要注入 Fragment,直接使用 ApplicationLifecycle,不进行生命周期观察,默认外部会一直处于活跃状态

简单的来说,如果是在主线程调用的话,因为在with方法中会创建一个RequestManager,RequestManager会与Activity中的无UI的Fragment通过LifeCycle接口建立联系,传递生命周期事件,实现生命周期感知

如果是子线程的话,因为RequestManager不会与Fragment进行绑定,不会通过LifeCycle来监听生命周期,也就一直处于活跃状态

2.5如何启动加载图片的任务

当调用into 方法后,就会构建出一个代表当前加载任务的 Request 对象,并且将该任务传递给 RequestManager,以此开始跟踪该任务。

requestManager.track(target, request)这一句代码,这就是任务的发起点。

RequestTracker 就用于存储所有的 Request,即存储所有加载图片的任务,并提供了开始、暂停和重启所有任务的方法。外部通过改变 isPaused 变量值,用来控制当前是否允许启动任务,runRequest 方法中就会根据 isPaused 来判断当前是马上启动任务还是将任务暂存到待处理列表 pendingRequests 中。

2.6为什么使用弱引用缓存

  • 假如没有弱引用缓存,就无法保证当前使用的资源被LruCache算法回收(没有弱引用就无法移入到LRU缓存中)。
  • 使用弱引用,既可以缓存正在使用的强引用资源,也不阻碍系统回收不被引用的资源。

一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。