IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Glide图片加载流程浅析 -> 正文阅读

[移动开发]Glide图片加载流程浅析

Glide是Android开发中常用的图片框架,其最基本用法例如Glide.with(context).load(url).into(imageView),我们沿着此链式调用的顺序一窥Glide图片加载流程的样貌。

一图胜千言

在这里插入图片描述

1.Glide.with(context)

// Glide.java 
public static RequestManager with(@NonNull Context context) { 
    return getRetriever(context).get(context); 
} 
public static RequestManager with(@NonNull Activity activity) { 
    return getRetriever(activity).get(activity); 
} 
public static RequestManager with(@NonNull FragmentActivity activity) { 
    return getRetriever(activity).get(activity); 
} 
public static RequestManager with(@NonNull Fragment fragment) { 
    return getRetriever(fragment.getContext()).get(fragment); 
} 
public static RequestManager with(@NonNull View view) { 
    return getRetriever(view.getContext()).get(view); 
} 

该函数的作用是创建了RequestManager对象并返回。其参数有五种类型,这里选取Context类型情形进行分析。

可以看到RequestManager对象是先通过getRetriver()方法创建得到一个RequestManagerRetriever对象,再调用该对象的get()方法返回。

1.1RequestManager对象的创建过程

// Glide.java 
private static RequestManagerRetriever getRetriever(@Nullable Context context) { 
    // Glide.get(context)获取Glide实例 
    return Glide.get(context).getRequestManagerRetriever(); 
} 
public static Glide get(@NonNull Context context) { 
    if (glide == null) { 
      // 加载AppGlideModule 
      GeneratedAppGlideModule annotationGeneratedModule = 
          getAnnotationGeneratedGlideModules(context.getApplicationContext()); 
      synchronized (Glide.class) { 
        if (glide == null) { 
          // 加载Mainfest配置、注册模块回调 
          // 这一步执行了 Glide.build()方法构造Glide实例。build方法下面会讲到 
          checkAndInitializeGlide(context, annotationGeneratedModule); 
        } 
      } 
    } 
    return glide; 
  } 

getRetriever(context)方法中,先通过Glide.get(context)获取Glide实例,Glide采用双重校验锁的懒汉式的单例模式创建,同时读取AppGlideModule和AndroidManifest.xml的配置。

获取到Glide实例后,紧接着调用getRequestManagerRetriever方法返回了上一步已经初始化好的RequestManagerRetriever对象。

1.1.1RequestManagerRetriever是如何初始化的?

RequestManagerRetriever对象的初始化主要是在Glide对象的创建过程中完成的,也就是Glide.get(context)方法中的checkAndInitializeGlide()函数中,该函数内部调用了Glide.build方法,源码如下:

// GlideBuilder.java 
Glide build(@NonNull Context context) { 
      // 分配线程池、配置缓存策略 
      sourceExecutor = GlideExecutor.newSourceExecutor(); 
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); 
      animationExecutor = GlideExecutor.newAnimationExecutor(); 
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); 
      // 监听网络变化 
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); 
      int size = memorySizeCalculator.getBitmapPoolSize(); 
      if (size > 0) { 
        bitmapPool = new LruBitmapPool(size); 
      } else { 
        bitmapPool = new BitmapPoolAdapter(); 
      } 
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); 
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); 
      diskCacheFactory = new InternalCacheDiskCacheFactory(context); 
    // engine是负责执行加载任务的 
    if (engine == null) { 
      engine = 
          new Engine( 
              memoryCache, 
              diskCacheFactory, 
              diskCacheExecutor, 
              sourceExecutor, 
              GlideExecutor.newUnlimitedSourceExecutor(), 
              animationExecutor, 
              isActiveResourceRetentionAllowed); 
    } 
    if (defaultRequestListeners == null) { 
      defaultRequestListeners = Collections.emptyList(); 
    } else { 
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); 
    } 
   // focus 1===
    RequestManagerRetriever requestManagerRetriever = 
        new RequestManagerRetriever(requestManagerFactory); 
    return new Glide( 
        context, 
        engine, 
        memoryCache, 
        bitmapPool, 
        arrayPool, 
        requestManagerRetriever, 
        connectivityMonitorFactory, 
        logLevel, 
        defaultRequestOptionsFactory, 
        defaultTransitionOptions, 
        defaultRequestListeners, 
        isLoggingRequestOriginsEnabled, 
        isImageDecoderEnabledForBitmaps); 
  } 

注意focus 1处的代码创建了RequestManagerRetriever对象。

public interface RequestManagerFactory {
    @NonNull
    RequestManager build(
        @NonNull Glide glide,
        @NonNull Lifecycle lifecycle,
        @NonNull RequestManagerTreeNode requestManagerTreeNode,
        @NonNull Context context);
  }

从以上代码我们可以知道:

1.执行Glide.get()方法时就已经分配好了资源加载、缓存线程池、配置好了缓存策略,这里的engine专门负责加载、解码资源,ConnectivityMonitor注册了网络状态监听器,当网络断开时暂停请求网络资源,重连后继续请求资源;
2.RequestManagerRetriever是通过RequestManagerFactory工厂类构造的,进入到RequestManagerFactory.java类中,可以看到其build()方法获取到了相应的RequestManager对象;

1.1.2 factory.build()方法在什么时候调用的?

在之前getRetriever(context).get(context);的调用中可以发现
在RequestManagerRetriever类中有一个get()方法,正是在这个方法中调用了factory.build()方法返回了RequestManager对象。

如果Glide.with(context)传入的context参数是Application类或者是在后台线程调用的该方法,则调用getApplicationManager(Context context)

private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          // Normally pause/resume is taken care of by the fragment we add to the fragment or
          // activity. However, in this case since the manager attached to the application will not
          // receive lifecycle events, we must force the manager to start resumed using
          // ApplicationLifecycle.

          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }

否则调用supportFragmentGet或者fragmentGet

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

从这里也可以发现,当App进入后台后会导致页面不可见,此时RequestManager绑定到了ApplicationContext,与App的生命周期一致;在其他情况下,RequestManager是和当前页面的生命周期绑定,因此在RequestManager.java类中也实现了生命周期相关的回调函数;

执行完Glide.with(context)后我们拿到了一个对应的RequestManager对象,接下来就执行下一个任务load(url);

load(url)

拿到了RequestManager,紧接着调用load方法开始执行下一步操作,同样先看看load方法的实现

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

load()同样有多个重载函数,传入的参数可以是图片对象Bitmap、Drawable、本地资源Uri、在线资源路径Url、文件对象File、assets资源的id,这里以参数为Url为例;

asDrawable().load(url)返回了一个RequestBuilder对象,首先看看asDrawable方法干了什么;

// RequestManager.java 
  public RequestBuilder<Drawable> asDrawable() { 
    return as(Drawable.class); 
  } 
  public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) { 
    return new RequestBuilder<>(glide, this, resourceClass, context); 
  } 

asDrawable方法创建了RequestBuilder对象,然后调用RequestBuilder.java中的load方法;

// RequestBuilder.java 
  // 传入的String类型的url将会被作为缓存的key 
  public RequestBuilder<TranscodeType> load(@Nullable String string) { 
    return loadGeneric(string); 
  } 
  // 这里返回了自身 
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { 
    this.model = model; 
    isModelSet = true; 
    return this; 
  } 

load函数主要工作就是根据传入的资源类型,构造了一个相应的RequestBuilder对象。至此一切准备工作准备就绪,接下来就是最为重要的一步了-加载、展示文件,让我们来着看into(view)方法如何完成这些任务;

3.load(imageView)

拿到的是对应类型RequestBuilder实例,那么就看看该类里into方法的具体实现。同样into方法有into(@NonNull Y target)into(@NonNull ImageView )两个重载函数(这两个函数最终都会走到同一个函数中),由于调用into方法时我们传入的参数是ImageView类型的;

// RequestBuilder.java 
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { 
    Util.assertMainThread(); 
    BaseRequestOptions<?> requestOptions = this; 
    // View's scale type. 
    // 处理图片缩放,根据缩放类型来初始化对应的requestOptions对象 
    ...... 
    return into( 
        glideContext.buildImageViewTarget(view, transcodeClass), 
        /*targetListener=*/ null, 
        requestOptions, 
        Executors.mainThreadExecutor() // 运行在主线程的handler ,利用Handler实现子线程到主线程的切换,因为最终将图片设置到ImageView是需要在主线程操作的
    ); 
  } 

上面代码段首先处理图片缩放类型(裁剪、对齐方式等),并将生成的相关参数放入了requestOptions对象中,然后再将其作为参数传给了RequestBuilder.java类私有方法into。该方法定义的四个参数分别为:viewTarget、target回调监听器、请求参数、主线程的回调函数;

显然外部传入ImageView对象最终被转换成了ViewTarget对象,转换函数便是glideContext.buildImageViewTarget(view, transcodeClass);
无论传入参数是何种类型,最终都会转换成两种类型的ViewTarget :BitmapImageViewTarget或者DrawableImageViewTarget;具体取决于asBitmap()、asGif()和asDrawable()函数是否被调用,默认是调用的asDrawable(),所以默认返回的是DrawableImageViewTarget类型。

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

  public DrawableImageViewTarget(ImageView view) {
    super(view);
  }

  /** @deprecated Use {@link #waitForLayout()} instead. */
  // Public API.
  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource); //显示图片
  }
}

至此ViewTarget创建完毕,我们再回到RequestBuilder.java私有into方法

// RequestBuilder.java` 
 private <Y extends Target<TranscodeType>> Y into( 
      @NonNull Y target, 
      @Nullable RequestListener<TranscodeType> targetListener, 
      BaseRequestOptions<?> options, 
      Executor callbackExecutor) { 
    // 注释1:创建request 
    Request request = buildRequest(target, targetListener, options, callbackExecutor); 
    // 获取前一个reqeust请求对象 
    Request previous = target.getRequest(); 
    // 与上一个请求相同 并且 上一个请求已完成 
    if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 
     // 上一个请求已完成,那么重新启动它 
      if (!Preconditions.checkNotNull(previous).isRunning()) { 
        previous.begin(); 
      } 
      return target; 
    } 
    // 与上一个请求不同,则清除掉上一个,再将加入新请求 
    requestManager.clear(target); 
    target.setRequest(request); 
    requestManager.track(target, request); 
    return target; 
  } 

顺着代码次序,来看看这个方法每一步都干了什么:

1.首先执行buildRequest方法创建一个新的Request请求req1;
2.获取当前ViewTarget上正在进行中的Request请求req2;
3.判断新建的请求req1与已有的请求req2是否相同,如果相同则判断是否跳过req2请求的缓存,两个条件都满足则开始执行begin()方法开始请求资源并停止往下执行,条件都不满足则继续执行第四步;
4.给ViewTarget设置最新的请求req1,然后执行track方法追踪req1。
5.执行into(view)方法首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();

当我们在常见的列表界面中(如 recycleview 实现的列表),使用上面的代码,在我们快速滑动中,glide 是如何实现正确加载图片,而没有导致图片内容的错位或者是不正确呢?

答案就在上面的代码中。执行上面的代码后,glide 要把最新的图片加载到正确的对象上,而取消对象之前关联的图片加载请求。

首先,在该方法中构建了一个 Request 并持有了 target 。这样便可以将结果通知给 target。Target 是 Glide 对我们要加载目标的一个封装和抽象。
判断Taget 中的之前的 Request 和最新构建的 Request 是否相同,如果相同回收最新的 Request ,让旧的 Request 继续运行。如果不同,就取消之前的 Request 和 target 的关联。

 requestManager.clear(target);

最终会触发下面的代码

if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {
      Request request = target.getRequest();
      target.setRequest(null);
      request.clear();
    }

可以看到, target 对应的 request 被置 null, 而旧的 request 被 “clear”。旧的 Request 被 clear 后,又是如何让资源没有去加载到关联的 Target 上的? 我们看其中 SingleRequest 的实现

# SingleRequest.java
  /**
   * Cancels the current load if it is in progress, clears any resources held onto by the request
   * and replaces the loaded resource if the load completed with the placeholder.
   *
   * <p> Cleared requests can be restarted with a subsequent call to {@link #begin()} </p>
   *
   * @see #cancel()
   */
  @Override
  public void clear() {
    Util.assertMainThread();
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    if (status == Status.CLEARED) {
      return;
    }
    cancel();
    // Resource must be released before canNotifyStatusChanged is called.
    if (resource != null) {
      releaseResource(resource);
    }
    if (canNotifyCleared()) {
      target.onLoadCleared(getPlaceholderDrawable());
    }
    // Must be after cancel().
    status = Status.CLEARED;
  }

可以看到 clear() 方法中先是 执行了 cancel(),该方法会取消加载资源请求与该 Request 的回调关联。

  /**
   * Cancels the current load but does not release any resources held by the request and continues
   * to display the loaded resource if the load completed before the call to cancel.
   *
   * <p> Cancelled requests can be restarted with a subsequent call to {@link #begin()}. </p>
   *
   * @see #clear()
   */
  void cancel() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    target.removeCallback(this);
    status = Status.CANCELLED;
    if (loadStatus != null) {
      loadStatus.cancel();
      loadStatus = null;
    }
  }

起关键作用代码为

 loadStatus.cancel();
public static class LoadStatus {
    private final EngineJob<?> engineJob;
    private final ResourceCallback cb;

    LoadStatus(ResourceCallback cb, EngineJob<?> engineJob) {
      this.cb = cb;
      this.engineJob = engineJob;
    }

    public void cancel() {
      engineJob.removeCallback(cb);
    }
  }

LoadStatus 实际上只是持有了ResourceCallback回调(该回调可获取资源加载成功或失败)和 EngineJob。
EngineJob 是负责加载资源,并在加载成功后回调回去,这里 SingleRequest 实现了回调,所以它便可得知资源加载完成并获取到。所以 cancel() 调用后,即使旧的加载请求完成也不会回调到 Tareget 上。

  target.setRequest(request);
    requestManager.track(target, request);

在该方法中,方法中,Target 持有了最新的 request , requestManager.track() 方法则触发了 request 的加载请求,实际是由内部 Engine 和 EngineJob 负责。当顺利加载成功后便回调到 Target 对象上,触发 target.onResourceReady(result, animation) 方法,图片便被正确显示出来了。

###########################################################

回到上面的流程,在into(view)方法中,首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();

// ReqeustManager.java   
synchronized void track(@NonNull Target<?> target, @NonNull Request request) { 
    // 与lifecycle绑定 
    targetTracker.track(target); 
    // 启动reqeust 
    requestTracker.runRequest(request); 
  } 
// RequestTracker.java 
  public void runRequest(@NonNull Request request) { 
    requests.add(request); 
    if (!isPaused) { 
      request.begin(); // 立即开始加载 
    } else { 
      //防止从以前的请求中加载任何位图,释放该请求所拥有的任何资源,显示当前占位符(如果提供了该占位符),并将该请求标记为已取消。 
      // request.java( Interface ) 
      request.clear(); 
      pendingRequests.add(request); // 加入队列等待执行 
    } 
  } 

Request类是interface类型,begin()是它的抽象方法,要想弄清楚begin()的具体实现,那就要先找到Request的实现类,SingleRequest,我们直接去SingleReqeust类里面 看看begin方法如何实现的;

// SingleReqeust.java 
public void begin() { 
      if (status == Status.COMPLETE) { 
        // 资源已下载,直接回调 
        // 执行动画 
        onResourceReady(resource, DataSource.MEMORY_CACHE); 
        return; 
      } 
        // 计算尺寸 
      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()); 
      } 
  } 

进入begin方法后首先判断如果资源已经过加载好了则直接回调onResourceReady显示图片并缓存,否则测量出图片尺寸后再开始加载图片(onSizeReady()中执行加载任务)并同时显示占位图:
接着再看onSizeReady()测量完图片尺寸后如何加载图片的:

// SingleRequest.java 
@Override 
  public void onSizeReady(int width, int height) { 
      if (status != Status.WAITING_FOR_SIZE) { 
        return; 
      } 
      status = Status.RUNNING; 
      // 获取图片尺寸 
      float sizeMultiplier = requestOptions.getSizeMultiplier(); 
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier); 
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier); 
      // 开始加载任务 
      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); 
  } 

可以看到真正的下载任务是在Engine类的load方法中实现的,其中也涉及到了图片缓存逻辑;

最终通过Handler机制,Glide从工作线程切换到主线程,并最终将Drawable对象显示到ImageView上;
具体再分析一下线程是如何切换的。

线程切换的过程

回到SingleRequest.onSizeReady()

# SingleRequest
@Override
  public void onSizeReady(int width, int height) {
  ...
  // 省略不相关代码
  ...
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);
}
#Engine
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) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//focus1 更新相应属性创建缓存key--EngineKey 
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) {
  current.addCallback(cb);
  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Added to existing load", startTime, key);
  }
  return new LoadStatus(cb, current);
}
//创建请求job
EngineJob<R> engineJob =
    engineJobFactory.build(
        key,
        isMemoryCacheable,
        useUnlimitedSourceExecutorPool,
        useAnimationPool,
        onlyRetrieveFromCache);
//创建解码job
DecodeJob<R> decodeJob =
    decodeJobFactory.build(
        glideContext,
        model,
        key,
        signature,
        width,
        height,
        resourceClass,
        transcodeClass,
        priority,
        diskCacheStrategy,
        transformations,
        isTransformationRequired,
        isScaleOnlyOrNoTransform,
        onlyRetrieveFromCache,
        options,
        engineJob);
//保存当前请求job
jobs.put(key, engineJob);

engineJob.addCallback(cb);
//开始请求
engineJob.start(decodeJob);

return new LoadStatus(cb, engineJob);
}

注意focus1处的代码

假设一个ImageView的宽高是300300,另一个ImageView的宽高是900900,则在这两个ImageView中加载同一张图片资源会缓存几次?

创建图片缓存Key和图片的model(图片链接Url)、图片资源的宽高都有关系,因此两个不同宽高的ImageView加载同一图片会缓存两次。

1.尝试从活跃的资源内存缓存中中获取资源,取到直接回调返回,没取到执行第二步
2.尝试从内存缓存中获取资源,取到直接回调返回,没取到执行第三步
3.创建请求engineJob
4.执行请求

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

DecodeJob实现了Runnable接口,这里将其添加到线程池中,接下来看下run方法 。

#DecodeJob
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {
  //判断是否已经被取消
  if (isCancelled) {
    notifyFailed();
    return;
  }
  runWrapped();
} catch (Throwable t) {
  if (stage != Stage.ENCODE) {
    throwables.add(t);
    notifyFailed();
  }
  if (!isCancelled) {
    throw t;
  }
} finally {
  if (localFetcher != null) {
    localFetcher.cleanup();
  }
  GlideTrace.endSection();
}
 }
private void runWrapped() {
switch (runReason) {
  case INITIALIZE:
    stage = getNextStage(Stage.INITIALIZE);
    //根据当前流程步骤获取相应获取数据类(刚开始肯定为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);
}
}
# DecodeJob.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);
}
}

默认的AUTOMATIC方式是允许解码缓存的RESOURCE,所以在 getNextStage 会先返回Stage.RESOURCE_CACHE,然后在start中会返回diskCacheExecutor,然后开始执行DecodeJob,也就是说这里会获得一个ResourceCacheGenerator,

#DecodeJob.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();
  if (stage == Stage.SOURCE) {
    reschedule();
    return;
  }
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
  notifyFailed();
}
 }

在 runGenerators 中,会调用 startNext,目前currentGenerator是ResourceCacheGenerator, 那么就是调用它的startNext方法:

@Override
 public boolean startNext() {
  ...
 # 省略部分代码
  ...
  // 这里会从registry中匹配相应的Modeloader来加载数据
       loadData.fetcher.loadData(helper.getPriority(), this);
  ...
   return started;
 }

这里是loadData.fetcher是HttpUrlFetcher。

# HttpUrlFetcher.loadData()
@Override
public void loadData(@NonNull Priority priority,
  @NonNull DataCallback<? super InputStream> callback) {
// 省略部分代码
 //连接网络获取图片流
  InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
  //成功回调
  callback.onDataReady(result);
}

请求成功之后回调了DataCallback方法。最终会回调到DecodeJob的onDataFetcherReady方法,之后在进行解码操作:
解码成功之后,回调EngineJob中的onResourceReady方法:

# EngineJob
 public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  }

在notifyCallbacksOfResult方法中:

# Engine.java
void notifyCallbacksOfResult() {
   // 省略部分代码
    engineJobListener.onEngineJobComplete(this, localKey, localResource);

    for (final ResourceCallbackAndExecutor entry : copy) {
    // focus 
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
  }

注意focus行的代码:

使用entry中的executor执行线程,而entry就是ResourceCallbackAndExecutor ,而它里面的executor正好是我们Executors.mainThreadExecutor()

很明显它是在这里切换到了主线程。

补充说明1:

首先回顾下主线程的创建,在RequestBuilder.java中的into方法中:

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    ...

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

Executor.java
  private static final Executor MAIN_THREAD_EXECUTOR =
      new Executor() {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
          handler.post(command);
        }
      };

它最终会一层一层传递到EngineJob中。engineJob.addCallback(cb, callbackExecutor); 它将被实例化为一个ResourceCallbackAndExecutor对象并add到cbs集合中。

EngineJob.java
  synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {
    ...
    cbs.add(cb, callbackExecutor);
    ...
  }

    void add(ResourceCallback cb, Executor executor) {
      callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
    }

补充说明2:

以上代码基于glide 4.11.0版本,可能代码有所不同,但是原理是一样。

参考:
Android源码进阶之Glide加载流程和源码详解

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 13:14:01  更:2022-02-16 13:14:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:39:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码