美文网首页Android
Android Glide源码剖析系列(二)Glide如何管理图

Android Glide源码剖析系列(二)Glide如何管理图

作者: 怡红快绿 | 来源:发表于2022-05-16 06:42 被阅读0次

    Glide源码剖析系列


    为什么选择Glide?

    • 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
    • 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)Glide可以感知调用页面的生命周期,这就是优势
    • 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
    • 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)

    小结:支持图片格式多;Bitmap复用和主动回收;生命周期感应;优秀的缓存策略;加载速度快(Bitmap默认格式RGB565)

    Glide简单使用

    Glide.with(this)
            .load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
            .into(imageView);
    

    源码分析

    通过Android Glide源码解析系列(一)图片加载请求如何感知组件生命周期一文我们知道,with()方法返回的是一个RequestManager对象,说明load()方法是在RequestManager类当中的,所以我们首先要看的就是RequestManager这个类。

    load()重载方法

    load()方法支持多种形式的图片来源,本文以RequestManager类中的 load(String string) 方法为例进行深入分析。

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

    为了使逻辑更清晰,把上面三个方法合并成一行代码来分析

    RequestManager#load(String string)
    //相当于
    new RequestBuilder<>(glide, RequestManager.this, Drawable.class, context).load(string);
    

    load(String string)最终被分为两步执行:

    1. Drawable.class为参数创建RequestBuilder实例
      RequestBuilder构造函数
      protected RequestBuilder(
          @NonNull Glide glide,
          RequestManager requestManager,
          Class<TranscodeType> transcodeClass,
          Context context) {
        this.glide = glide;
        this.requestManager = requestManager;
        this.transcodeClass = transcodeClass;  //重要变量赋值为Drawable.class,构建ImageViewTarget的时候使用
        this.context = context;
        this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
        this.glideContext = glide.getGlideContext();
    
        initRequestListeners(requestManager.getDefaultRequestListeners());    //初始化默认请求监听
        apply(requestManager.getDefaultRequestOptions());    //应用默认配置信息
      }
    
    1. 调用RequestBuilder#load(String string)
      public RequestBuilder<TranscodeType> load(@Nullable String string) {
        return loadGeneric(string);
      }
    
      @NonNull
      private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
        if (isAutoCloneEnabled()) {
          return clone().loadGeneric(model);
        }
        this.model = model;  //String变量赋值给model
        isModelSet = true;
        return selfOrThrowIfLocked();
      }
    

    RequestBuilder重要成员变量:

    • transcodeClas = Drawable.class, 构建ImageViewTarget的时候使用
    • model = 图片来源为String地址,构建Request的时候使用

    RequestManager#load(String string)分析到现在,似乎都是一些准备工作,真正的图片加载还没有出现。既然“千呼万唤不出来”,那咱们就“打破砂锅查到底”,继续查看RequestBuilder#into(ImageView view)方法

    #RequestBuilder.java
      public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
        Util.assertMainThread();  //判断是否在主线程执行
        Preconditions.checkNotNull(view);
    
        //把ImageView的scaleType写进requestOptions 
        //省略代码
    
        return into(
            //注释1:构建新的ImageViewTarget
            glideContext.buildImageViewTarget(view, transcodeClass),  
            /*targetListener=*/ null,
            requestOptions,  //请求选项
            Executors.mainThreadExecutor());  //主线程池,切回到主线程显示图片
      }
    
      private <Y extends Target<TranscodeType>> Y into(
          @NonNull Y target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> options,
          Executor callbackExecutor) {
    
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
    
        //注释2:构建新的加载请求Request 
        Request request = buildRequest(target, targetListener, options, callbackExecutor);
    
        Request previous = target.getRequest();  //获取目标View上已经存在的旧请求
        if (request.isEquivalentTo(previous)  //两次的请求相同 && !(跳过内存缓存 && 旧请求已完成)
            && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 
          if (!Preconditions.checkNotNull(previous).isRunning()) {  //如果旧请求未开始
            previous.begin();  //启动旧请求
          }
          return target;
        }
      
        //取消Glide为target准备的所有加载请求,并释放已经
        //加载的资源(例如Bitmap),以便它们可以被重用。
        requestManager.clear(target); 
    
        //利用View.setTag把request请求绑定到target指向的View
        target.setRequest(request); 
    
        //注释3
        requestManager.track(target, request);
    
        return target;
      }
    
      private boolean isSkipMemoryCacheWithCompletePreviousRequest(
          BaseRequestOptions<?> options, Request previous) {
        return !options.isMemoryCacheable() && previous.isComplete();
      }
    

    小结:

    1. 调用方法glideContext.buildImageViewTarget(view, transcodeClass)创建新的ImageViewTarget
    2. 创建新的加载请求request,并且检查target上是否已有老请求previous:
      2.1 如果两次的请求相同 && !(跳过内存缓存 && 旧请求已完成),into()方法直接返回target
      2.2 如果满足2.1且 && 请求未开始,先启动旧请求再返回target
      2.3 如果2.1和2.2都不满足,依次执行:取消Glide为target准备的所有加载请求,并释放已经加载的资源(例如Bitmap),以便它们可以被重用;利用View.setTag把新请求绑定到target指向的View;requestManager重新管理target和request。

    代码注释1分析:

      @NonNull
      public <X> ViewTarget<ImageView, X> buildImageViewTarget(
          @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
        //transcodeClas = Drawable.class
        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)) {  //transcodeClas = Drawable.class
          return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
        } else {
          throw new IllegalArgumentException(
              "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
      }
    }
    

    根据transcodeClass类型创建对应的ImageViewTarget,即Drawable.class -> DrawableImageViewTarget

    代码注释2分析:Request创建过程比较复杂,还要处理缩略图加载请求、加载错误图片加载请求等等,因此我们只分析主线buildRequest -> buildRequestRecursive -> buildThumbnailRequestRecursive -> obtainRequest -> SingleRequest.obtain -> new SingleRequest()。最终创建出一个SingleRequest实例

    代码注释3分析:

    #RequestManager
      synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
        targetTracker.track(target);
        requestTracker.runRequest(request);
      }
    

    主要做了两件事:

    1. target添加到targetTracker,管理target指向的View生命周期回调事件
    2. request添加到requestTracker,管理图片加载请求

    targetTracker和requestTracker都是RequestManager的成员变量,分别负责管理target和request

    TargetTracker源码:

    /**
     * TargetTracker 职责是维护一个Target列表,统一管理所有Target的onStart()、
     * onStop()和onDestroy()方法回调事件
     */
    public final class TargetTracker implements LifecycleListener {
      //维护target列表
      private final Set<Target<?>> targets =
          Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
    
      public void track(@NonNull Target<?> target) {
        targets.add(target);
      }
    
      public void untrack(@NonNull Target<?> target) {
        targets.remove(target);
      }
    
      @Override
      public void onStart() {
        for (Target<?> target : Util.getSnapshot(targets)) {
          target.onStart();
        }
      }
    
      @Override
      public void onStop() {
        for (Target<?> target : Util.getSnapshot(targets)) {
          target.onStop();
        }
      }
    
      @Override
      public void onDestroy() {
        for (Target<?> target : Util.getSnapshot(targets)) {
          target.onDestroy();
        }
      }
    
      @NonNull
      public List<Target<?>> getAll() {
        return Util.getSnapshot(targets);
      }
    
      public void clear() {
        targets.clear();
      }
    }
    

    RequestTracker部分源码:

    public class RequestTracker {
      private static final String TAG = "RequestTracker";
      // 如果Set直接持有request强引用,可能会发生内存泄漏,因此将request作为key存储在WeakHashMap里 
      //面,如果这些key不再使用,WeakHashMap会自动异常这些key,从而避免内存泄漏。
      private final Set<Request> requests =
          Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
    
      //直接持有未完成和等待开始的request强引用对象,防止在运行过程中被GC
      private final Set<Request> pendingRequests = new HashSet<>();
    
    //所有请求是否已经被暂停
      private boolean isPaused;
    
      /** Starts tracking the given request. 把请求添加到requests和pendingRequests*/
      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);
        }
      }
    
      /** Stops any in progress requests. 停止正在运行的请求*/
      public void pauseRequests() {
        isPaused = true;
        for (Request request : Util.getSnapshot(requests)) {
          if (request.isRunning()) {
            // Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking
            // in the UI, while still making sure that any in progress parts of requests are immediately
            // stopped.
            request.pause();
            pendingRequests.add(request);
          }
        }
      }
    
      /** Starts any not yet completed or failed requests. 开启未完成或失败的请求*/
      public void resumeRequests() {
        isPaused = false;
        for (Request request : Util.getSnapshot(requests)) {
          // We don't need to check for cleared here. Any explicit clear by a user will remove the
          // Request from the tracker, so the only way we'd find a cleared request here is if we cleared
          // it. As a result it should be safe for us to resume cleared requests.
          if (!request.isComplete() && !request.isRunning()) {
            request.begin();
          }
        }
        pendingRequests.clear();
      }
      
      //省略其他方法
    }
    

    图片加载请求的管理流程END

    结语

    Glide管理图片加载请求的方式并不复杂:使用一个请求管理类RequestTracker 维护所有请求列表,并且提供统一操作所有请求的方法(即所有请求的开启、暂停和重启等等方法),RequestManager只要通过一个RequestTracker 对象就能轻轻松松控制图片加载请求。

    下一篇文章我们将会学习Glide如何启动图片加载请求,以及图片资源是如何经历重重改造最终显示到界面上,敬请期待!

    相关文章

      网友评论

        本文标题:Android Glide源码剖析系列(二)Glide如何管理图

        本文链接:https://www.haomeiwen.com/subject/qasmurtx.html