一文搞懂Glide,不懂来打我

1、什么是Glide?

1.1、官方描述

Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。

  • Glide 支持拉取,解码和展示视频快照、图片和GIF动画。Glide的Api灵活易用,开发者可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
  • Glide 的目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。

1.2、特点

  • 自动、智能地采样(downsampling)和缓存(caching),最小化存储的开销和解码的次数;
  • 有效的资源重用,例如字节数组和Bitmap,最小化垃圾回收和堆碎片的影响;
  • 深度的生命周期集成,确保优先处理活跃的Fragment和Activity的请求,同时有利于应用在必要时释放资源(页面销毁等)。

2、Glide怎么使用?

2.1、官方 Glide API

Glide 使用简明的流式语法API,这是一个非常棒的设计,因为它允许你在大部分情况下一行代码搞定需求:

// build.gradle文件添加 Glide 依赖
dependencies {
    implementation "com.github.bumptech.glide:glide:4.12.0"
}

// API 简单使用
Glide.with(context)
    .load(url)
    .into(imageView);

2.2、自定义 Glide API

通过Glide提供的注解,来添加自己定义的API

  • GlideModule 注解用于AppGlideModule
  • GlideExtension 注解用于标识一个扩展Glide API的类,任何拓展Glide API的类都必须使用这个注解来标记,被@GlideExtension注解的类应以工具类的方式实现。
  • GlideOption 注解为RequestOptions添加一个选项。
  • GlideType 添加新的资源类型的支持(GIF,SVG等)

项目需要通过GlideModule注解继承自AppGlideModule类的子类,并通过GlideExtension注解到工具类上,来扩展自定义Glide API,使用GlideOption、GlideType注解时必须为静态方法,最后经过Rebuild Project之后,最终会被编译到XXXRequest.java类

// build.gradle文件添加 Glide 注解处理器
dependencies {
    implementation "com.github.bumptech.glide:glide:4.12.0"
    annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
}

// Application模块内,GlideModule注解自定义子类继承AppGlideModule,可以不用重写任何方法。
@GlideModule(glideName = "GlideApp")
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
        // 可以添加一些全局性的options
        super.applyOptions(context, builder);
    }
}

// GlideExtension注解,添加自定义的Glide API
@GlideExtension
public class MyGlideExtensions {
    private MyGlideExtensions() {
    }

    // GlideOption注解,添加自定义的Option
    @GlideOption
    public static BaseRequestOptions<?> myMiniThumb(BaseRequestOptions<?> options, int size) {
        return options.fitCenter().override(size);
    }

    // 自定义decode resource Type
    private static final RequestOptions DECODE_TYPE_GIF = RequestOptions.decodeTypeOf(GifDrawable.class);

    // GlideType注解,添加自定义的资源类型
    @GlideType(GifDrawable.class)
    public static RequestBuilder<GifDrawable> asMyGif(RequestBuilder<GifDrawable> requestBuilder) {
        return requestBuilder
                .transition(new DrawableTransitionOptions())    // 设置用于在加载完成时从占位符到正常显示的过渡效果
                .apply(DECODE_TYPE_GIF);    // 将自定义的ResourceClass设置到resourceClass参数
    }
}

    // 使用自定义API
    GlideApp.with(context)
            .asMyGif()      // 使用自定义的资源
            .load(url)
            .myMiniThumb(100)   // 使用自定义的Option
            .into(view);

3、Glide加载图片过程

Glide加载图片的过程,可以分为三个阶段:with、load和into。

Glide 结构图如下:

3.1、with阶段

with方法用于获取到RequestManager,RequestManager用于管理图片请求;在创建RequestManager时,根据不同的Context上下文和线程,创建出绑定不同生命周期的组件(Application,Activity,Fragment)的requestManager实例。

RequestManager职责:

  • 用于管理和启动 Glide 请求的类,通过内部的requestTracker来跟踪记录所有的request;
  • 可以使用 Activity、Fragment 等连接生命周期事件来智能地停止、启动和重启请求;

通过不同的静态with方法,获取拥有不通生命周期的requestManager实例。

    Glide#with(android.app.Activity)
    Glide#with(androidx.fragment.app.FragmentActivity)
    Glide#with(android.app.Fragment)
    Glide#with(androidx.fragment.app.Fragment)
    Glide#with(Context)
    Glide#with(View)

    // 对应到上述with方法,通过不同的get重载方法来创建或检索 requestManager 对象
    RequestManagerRetriever#get(android.app.Activity)
    RequestManagerRetriever#get(androidx.fragment.app.FragmentActivity)
    RequestManagerRetriever#get(android.app.Fragment)
    RequestManagerRetriever#get(androidx.fragment.app.Fragment)
    RequestManagerRetriever#get(Context)
    RequestManagerRetriever#get(View)

Glide with 流程图如下:

3.1.1、获取 Glide 单例

首先从Glide.with(Context)方法开始

# Glide.java
  // 通过retriever 的get方法来获取requestManager
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

  // 获取 retriever 检索器,其内部持有RequestManagerFactory,检索器用于创建或检索 requestManager 实例
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    return Glide.get(context).getRequestManagerRetriever();
  }

  // 获取Glide,Glide内部持有检索器
  public static Glide get(@NonNull Context context) {
    // 双重检查锁的方式,获取 glide 单例
    if (glide == null) {
      // 获取App模块内自定义的AppGlideModule类(*GlideModule注解的)
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          // 创建 Glide 实例
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }
    return glide;
  }

    // checkAndInitializeGlide方法最终会调用initializeGlide方法
    private static void initializeGlide(
            @NonNull Context context,
            @NonNull GlideBuilder builder,  // builder = new GlideBuilder()
            @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
        Context applicationContext = context.getApplicationContext();

        RequestManagerRetriever.RequestManagerFactory factory =
                annotationGeneratedModule != null
                        // 获取到创建requestManager的factory,factory会创建自动生成的GlideRequests继承自RequestManager,其包含GlideType注解的API方法
                        ? annotationGeneratedModule.getRequestManagerFactory()
                        : null;
        builder.setRequestManagerFactory(factory);
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.applyOptions(applicationContext, builder);
        }
        // 构建 glide 实例,内部会创建默认的 RequestManagerRetriever和RequestManagerFactory等创建requestManager的相关参数。
        Glide glide = builder.build(applicationContext);
        if (annotationGeneratedModule != null) {
            annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
        }
        applicationContext.registerComponentCallbacks(glide);
        // 设置到静态变量,单例模式
        Glide.glide = glide;
    }

3.1.2、获取 RequestManager 实例

在Glide创建过程中会 创建检索器 RequestManagerRetriever,通过检索器获取 requestManager,接着分析 RequestManagerRetriever.get(Context)方法

# RequestManagerRetriever.java

    public RequestManager get(@NonNull Context context) {
        // 省略了安全检查

        if (Util.isOnMainThread() && !(context instanceof Application)) {
            if (context instanceof FragmentActivity) {
                // 获取 FragmentActivity 级 RequestManager
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {
                // 获取 Activity 级 RequestManager
                return get((Activity) context);
            } else if (context instanceof ContextWrapper
                    // 使用 ContextWrapper 附加的 Context 继续获取
                    && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
                return get(((ContextWrapper) context).getBaseContext());
            }
        }
        // 获取 Application 级 RequestManager
        return getApplicationManager(context);
    }

    // 跟踪 FragmentActivity 方式获取的 RequestManager
    public RequestManager get(@NonNull FragmentActivity activity) {
        if (Util.isOnBackgroundThread()) {
            // 非主线程时,获取 Application 级 RequestManager
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);   // activity 销毁检查
            frameWaiter.registerSelf(activity);
            FragmentManager fm = activity.getSupportFragmentManager();
            return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
        }
    }

    // 获取 Fragment 级 RequestManager
    private RequestManager supportFragmentGet(
            @NonNull Context context,
            @NonNull FragmentManager fm,
            @Nullable Fragment parentHint,
            boolean isParentVisible) {
        // 获取 SupportRequestManagerFragment,其内部持有:
        // 1、ActivityFragmentLifecycle,在 Fragment 的模版方法中通过 lifecycle 回调生命周期事件
        // 2、RequestManagerTreeNode,用于跟踪记录嵌套的 Fragment 的RequestManager
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            // 获取全局 glide 单例
            Glide glide = Glide.get(context);
            // 通过工厂方式构建 requestManager 对象
            requestManager =
                    factory.build(
                            glide,
                            // 1. requestManager 通过 lifecycle 注册listener
                            // 2. 回调生命周期事件,在Fragment生命周期变化时,通知RequestManager实现的LifecycleListener接口方法进行响应
                            current.getGlideLifecycle(),
                            current.getRequestManagerTreeNode(), // 跟踪嵌套Fragment内的RequestManager
                            context);
            if (isParentVisible) {
                // 开始请求,并设置target显示
                requestManager.onStart();
            }
            // 给 fragment 设置RequestManger
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

3.1.3、Glide 生命周期分析

RequestMananger的构造方法中,和创建的用于监听生命周期事件的Fragment进行关联,RequestManager实现了LifeCycleListener接口,通过LifeCycle.addListener(this)的方式将观察者注入生命周期监视器。 RequestManager在实现了LifeCycleListener接口的onStart()/onStop()/onDestory()的方法中,通过RequestTracker来管理请求任务,通过TargetTracker来控制View的显示效果。

# RequestManager.java

  RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.context = context;

    connectivityMonitor =
        factory.build(
            context.getApplicationContext(),
            new RequestManagerConnectivityListener(requestTracker));
    // 非主线程,切换到主线程绑定生命周期事件
    if (Util.isOnBackgroundThread()) {
      Util.postOnUiThread(addSelfToLifecycle);
    } else {
      // 监听生命周期事件
      lifecycle.addListener(this);
    }
    // 监听网络变化事件
    lifecycle.addListener(connectivityMonitor);
    ...
  }

 @Override
  public synchronized void onStart() {
    // 恢复请求
    requestTracker.resumeRequests();
    targetTracker.onStart();
  }
  
  @Override
  public synchronized void onStop() {
    // 暂停请求
    requestTracker.pauseRequests();
    targetTracker.onStop();
  }

  @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    // 清理target
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    // 清除请求
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    Util.removeCallbacksOnUiThread(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }
 
 # RequestTracker.java
 
   public void resumeRequests() {
    isPaused = false;
    // 处理所有的请求
    for (Request request : Util.getSnapshot(requests)) {
      if (!request.isComplete() && !request.isRunning()) {
        // 重新发起请求
        request.begin();
      }
    }
    // pending队列保存未完成并排队等待再次运行的请求。 列表来维护对这些请求的硬引用,确保它们在开始运行之前或暂停时不会被垃圾收集,在重启开启请求时清理。
    pendingRequests.clear();
  }

3.1.4、Glide 网络变化分析
  1. 设置自定义的网络监听方式;
  2. 未设置自定义网络监听方式,采用默认方式;
  • 有网络权限时,通过 ConnectivityManager.android.net.conn.CONNECTIVITY_CHANGE 广播的方式监听网络事件。
  • 无网络权限时,不监听。
# DefaultConnectivityMonitorFactory.java
  // 默认监听网络变化广播
  public ConnectivityMonitor build(
      @NonNull Context context, @NonNull ConnectivityMonitor.ConnectivityListener listener) {
    int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
    // 检查网络权限
    boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
    return hasPermission
        ? new DefaultConnectivityMonitor(context, listener)
        : new NullConnectivityMonitor();
  }

  # ReuquestManager.java
  // 网络连接变化事件处理
  private class RequestManagerConnectivityListener
      implements ConnectivityMonitor.ConnectivityListener {
    @GuardedBy("RequestManager.this")
    private final RequestTracker requestTracker;

    RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
      this.requestTracker = requestTracker;
    }

    @Override
    public void onConnectivityChanged(boolean isConnected) {
      if (isConnected) {
        synchronized (RequestManager.this) {
          // 通过 requestTracker 重新发起请求
          requestTracker.restartRequests();
        }
      }
    }
  }

3.2、load阶段

load阶段创建出 RequestBuilder 对象,为每个请求封装 glide,requestManager,glideContext,model,requestOptions 等参数。

RequestBuilder extends BaseRequestOptions {}

3.3、into阶段

into阶段可以分为四个过程:

  1. target 绑定 request 并发起 request 请求;
  2. 数据加载;
  3. 资源解码;
  4. 资源缓存和显示;

Glide into 流程图如下:

# RequestBuilder.java

  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    // 省略了安全检查
    // 省略了 options 变换

    return into(
    // 获取 ImageView 载体
        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) {

    // 构建 Request 加载请求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    // 载体原有请求与新请求比对,请求等效时采用原有请求进行加载
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // 当请求未开始时,开始启动请求,加载数据
        previous.begin();
      }
      return target;
    }

    // 清理原来的请求
    requestManager.clear(target);
    // 将请求与 Target 进行绑定
    target.setRequest(request);
    // 记录请求,并启动请求
    requestManager.track(target, request);

    return target;
  }

# RequestManager.java

  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    // 记录请求,并启动请求
    requestTracker.runRequest(request);
  }

# RequestTracker.java

  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.java

  public void begin() {
    synchronized (requestLock) {
      // 省略了安全检查

      if (status == Status.COMPLETE) {
        // 直接从请求中缓存的 Resource 返回,回调给 ViewTarget 显示资源
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return;
      }


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

  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = Status.RUNNING;
      // 开启加载,engine 是 Glide创建时构造的
      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,
              callbackExecutor);
    }
  }

3.3.1、数据加载流程

数据加载分为两个部分,一部分是内存(活跃资源 HashMap 和内存 LruCache)中加载;另一部分是从本地或远端加载。

  • 缓存策略,决定缓存的数据类型:
缓存策略 是否支持转换资源缓存 是否支持原始数据缓存
NONE 不支持 不支持
ALL 数据源不是磁盘与内存缓存时,支持 数据源是远程,支持
RESOURCE 支持 不支持
DATA 不支持 数据源不是磁盘与内存缓存时,支持
AUTOMIC 数据源是本地,支持 数据源是远程,支持
  • 缓存数据类型对应的加载器:
缓存类型 Generator 描述
RESOURCE ResourceCacheGenerator 从包含采样/转换资源数据的缓存文件生成DataFetcher
DATA DataCacheGenerator 从包含原始未修改源数据的缓存文件生成DataFetcher
SOURCE SourceGenerator 使用注册的ModelLoaders和为加载提供的模型从原始源数据生成DataFetcher
FINISHED NULL NULL
3.3.1.1、内存数据加载流程

内存中缓存的数据分为两种,一种是活跃资源的Map缓存,一种是LRU缓存,数据首先会从这两个缓存中加载,如果有则直接返回使用,如果资源为null,则从本地或远端数据加载数据。

内存数据加载流程图如下:

# Engine.java

  public <R> LoadStatus load() {

    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource<?> memoryResource;
    synchronized (this) {
      // 从内存中加载数据
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

      if (memoryResource == null) {
        // 从本地或者远端加载数据
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }

    // 将内存中加载的资源回调给 ViewTarget 显示
    cb.onResourceReady(
        memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
    return null;
  }

  private EngineResource<?> loadFromMemory(
      EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {
      return null;
    }
    // 从获取资源缓存中加载
    EngineResource<?> active = loadFromActiveResources(key);
    if (active != null) {
      return active;
    }
    // 从内存缓存中加载
    EngineResource<?> cached = loadFromCache(key);
    if (cached != null) {
      return cached;
    }

    return null;
  }

  // 加载活跃资源
  private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }
    return active;
  }

  // 加载内存资源
  private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      // 缓存活跃资源,弱引用方式保存到 Map 中
      activeResources.activate(key, cached);
    }
    return cached;
  }

  private EngineResource<?> getEngineResourceFromCache(Key key) {
    // 从 LruCache 中加载
    Resource<?> cached = cache.remove(key);

    final EngineResource<?> result;
    if (cached == null) {
      result = null;
    } else if (cached instanceof EngineResource) {
      result = (EngineResource<?>) cached;
    } else {
      // 使用 EngineResource包装缓存资源
      result =
          new EngineResource<>(
              cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
    }
    return result;
  }

3.3.1.2、本地或远端数据加载流程

当从内存中没有找到资源时,会开启本地或远端数据加载的操作,此过程是异步行为,通过线程池方式提交加载任务启动加载请求。

本地或者远程数据加载流程图如下:

# Engine.java

  private <R> LoadStatus waitForExistingOrStartNewJob() {

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

    // 创建 EngineJob
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
    // 创建 DecodeJob,解码流程
    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, callbackExecutor);
    engineJob.start(decodeJob); // 开启异步加载流程,decodeJob 实现了 Runnable 接口

    return new LoadStatus(cb, engineJob);
  }

# DecodeJob.java

  public void run() {
      // 匹配 DataFetcherGenerator 进行数据加载
      runWrapped();
  }

  // 从 runWrapped 开始,会调用到 runGenerators 方法
  private void runGenerators() {
    boolean isStarted = false;
    while (!isCancelled
        && currentGenerator != null
        // 开始加载数据,ResourceCacheGenerator,DataCacheGenerator,SourceGenerator
        && !(isStarted = currentGenerator.startNext())) {
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }
  }

# ResourceCacheGenerator.java,源码分析该DataFetcherGenerator

  public boolean startNext() {
    List<Key> sourceIds = helper.getCacheKeys();
    while (modelLoaders == null || !hasNextModelLoader()) {
      // Resource 是转码后的资源类型,对应的 Key 为 ResourceCacheKey
      currentKey =
          new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
      cacheFile = helper.getDiskCache().get(currentKey);
      if (cacheFile != null) {
        sourceKey = sourceId;
        // 根据 CacheFile 匹配出所有符合的 ModelLoaders
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      // 通过 ModelLoader 构造出 LoadData
      loadData =
          modelLoader.buildLoadData(
              cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        // 通过 DataFetcher 开始加载数据
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }

    return started;
  }

# FileLoader.java 内对应的 FileFetcher 嵌套类

  public void loadData(@NonNull Priority priority, @NonNull DataFetcher.DataCallback<? super Data> callback) {
    try {
         // file 为 modelLoader.buildLoadData 时传入的 model,即 cacheFile
         // opener 是 FileInputStream
        data = opener.open(file);
        // 将打开的文件流数据,成功回调
        callback.onDataReady(data);
    } catch (FileNotFoundException e) {
    // 失败回调
        callback.onLoadFailed(e);
    }
  }

3.3.2、资源解码流程

在数据被加载成功之后,会进行资源的解码操作,转成Android可以支持显示的资源数据。

Glide 解码资源流程图如下:

# DecodeJob.java

  public void onDataFetcherReady(
        Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    // 省略了变量的赋值操作
    if (Thread.currentThread() != currentThread) {
        // 切换到指定线程进行资源解码操作
        runReason = DecodeJob.RunReason.DECODE_DATA;
        callback.reschedule(this);
    } else {
        GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
        try {
            // 资源解码
            decodeFromRetrievedData();
        } finally {
            GlideTrace.endSection();
        }
    }
  }

  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
        throws GlideException {
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    // 通过 LoadPath 进行解码,loadPath 内有 decodePaths
    return runLoadPath(data, dataSource, path);
  }

# DecodePath.java

  public Resource<Transcode> decode(
      DataRewinder<DataType> rewinder,
      int width,
      int height,
      @NonNull Options options,
      DecodeCallback<ResourceType> callback)
      throws GlideException {
    // 解码资源,DecodePath内部会匹配注册的Decoder进行decode操作,解码出原始的Resource
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    // 资源转码,DecodeJob是实现方,内部通过匹配注册的Transformation进行transform操作
    // 最后根据缓存策略,决定缓存转码资源或者原始资源
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    // 转换为新资源类型,eg: Bitmap -> BitmapDrawable
    return transcoder.transcode(transformed, options);
  }

3.3.3、资源缓存和显示流程

资源在经过加载和解码之后,进行转码阶段时,根据DataSource判断资源是缓存原始资源还是转码资源,策略如下:

EncodeStrategy 缓存到磁盘策略描述 缓存Key
SOURCE 将原始资源数据缓存到磁盘 DataCacheKey
TRANSFORMED 将转码后的资源数据缓存到磁盘 ResourceCacheKey
NONE 数据不缓存到磁盘

资源缓存方案主要是表中三种:

缓存方案 方案介绍
ActiveResource 内存缓存,采用Map<Key, WeakReference> 弱引用的方案保存正在使用的资源,防止出现LRU导致正在使用的资源被回收
LruCache 内存缓存,采用最近最少使用的策略,保证资源的使用效率,且尽量避免出现OOM问题
DiskLruCache 磁盘缓存,最近最少使用的策略,减少对网络耗时的请求操作

在经过DataFetcher.loadData数据提取之后,进行数据的一个缓存,缓存分两种,一种是缓存到磁盘(默认是应用data目录下的image_manager_disk_cache文件,默认大小为250M),一种是缓存到内存。

Glide 资源缓存和显示流程图如下:

// 1、缓存活跃资源

# Engine.java

  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    if (resource != null && resource.isMemoryCacheable()) {
      // 活跃资源缓存
      activeResources.activate(key, resource);
    }
  }

# ActiveResources.java,内部通过 Map<Key, ResourceWeakReference> 缓存

  synchronized void activate(Key key, EngineResource<?> resource) {
    // 通过弱引用的方式持有资源
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

    // 将弱引用资源放入Map
    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }

// 2、内存缓存资源,通过 LRU 最近最少使用方案

# Engine.java

  public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
      // Cache 是 MemoryCache,内部持有 LruCache
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
    }
  }

# LruResourceCache.java,继承自LruCache

public synchronized Y put(@NonNull T key, @Nullable Y item) {
    final int itemSize = getSize(item);
    // 超出数量上限时,直接返回,并清理当前需要缓存的资源
    if (itemSize >= maxSize) {
      onItemEvicted(key, item);
      return null;
    }

    if (item != null) {
      currentSize += itemSize;
    }
    // 将资源放入 LinkedHashMap,会通过 afterNodeAccess()方法将最近访问数据放在双链表的尾部
    @Nullable Entry<Y> old = cache.put(key, item == null ? null : new Entry<>(item, itemSize));
    if (old != null) {
      currentSize -= old.size;

      // 对废弃资源进行清理
      if (!old.value.equals(item)) {
        onItemEvicted(key, old.value);
      }
    }
    // 重新计算缓存空间,大小超出时,则移除最久未使用的资源
    evict();

    return old != null ? old.value : null;
  }

// 3、磁盘缓存资源,采用 DiskLruCache 方案

# DecodeJob.java 的嵌套类 DeferredEncodeManager

  void encode(DiskCacheProvider diskCacheProvider, Options options) {
    GlideTrace.beginSection("DecodeJob.encode");
    try {
    // 写入资源时,根据缓存策略,已经确定 toEncode 资源是转码资源还是原始资源
      diskCacheProvider
          .getDiskCache()
          .put(key, new DataCacheWriter<>(encoder, toEncode, options));
    } finally {
      // 资源回收
      toEncode.unlock();
      GlideTrace.endSection();
    }
  }

// DiskLruCacheWrapper.java 内部持有 DiskLruCache

  private synchronized DiskLruCache getDiskCache() throws IOException {
    if (diskLruCache == null) {
      // 打开磁盘缓存器
      diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
    }
    return diskLruCache;
  }

  public void put(Key key, Writer writer) {
    // 将 Key 安全的转成 sha256 字符串编码,key会被保存在LruCache内,获取时会加锁
    String safeKey = safeKeyGenerator.getSafeKey(key);

    // 加锁,进行安全的写入操作
    writeLocker.acquire(safeKey);
    try {
      try {
        // 获取 DiskLruCache
        DiskLruCache diskCache = getDiskCache();
        // 如果资源已缓存,则退出
        Value current = diskCache.get(safeKey);
        if (current != null) {
          return;
        }

    // 获取资源写入的编辑器
        DiskLruCache.Editor editor = diskCache.edit(safeKey);
        if (editor == null) {
          throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
        }
        try {
          // 写入到文件内
          File file = editor.getFile(0);
          if (writer.write(file)) {
            editor.commit();
          }
        } finally {
          editor.abortUnlessCommitted();
        }
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.WARN)) {
          Log.w(TAG, "Unable to put to disk cache", e);
        }
      }
    } finally {
      // 释放锁
      writeLocker.release(safeKey);
    }
  }

# Extectors.java
  // 主线程池,用于显示图片
  private static final Executor MAIN_THREAD_EXECUTOR =
      new Executor() {
        @Override
        public void execute(@NonNull Runnable command) {
          Util.postOnUiThread(command);
        }
      }

4、总结

使用建议:

1. 结合自身需求,决定是否考虑在AppGlideModule内应用全局性的Options。
2. 在使用Glide时,尽可能的保证context上下文符合预期,防止产生内存泄漏问题。
3. 在滑动事件时,可以考虑结合RequestManager内的 resume 和 pause 来处理快速滑动产生的卡顿问题。

总结,Glide 框架主要分为三个部分:

  • 第一个部分: with 阶段,注册编解码器,初始化变量(Glide,RequestManager,Engine等)和绑定页面生命周期等操作,用于管理请求和监听生命周期事件。
  • 第二个部分:load 阶段,为每个请求配置单独的 Option,比如:设置 width,height,DiskCacheStrategy,Transaction等。
  • 第三个部分:into 阶段,最复杂的阶段,启动请求,开始加载数据,对数据进行解码和转码操作,缓存解码数据或者原始数据,显示视图。

作者:jaymzyang
转载于:https://juejin.cn/post/7044079102839488543
如有侵权,请联系删除!

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

推荐阅读更多精彩内容