Glide加载一张网图时发生了啥

作者: Raven | 来源:发表于2020-05-08 13:27 被阅读0次
Glide.with(imageView.context).load(url).into(imageView)

这一串代码是加载图片的最简形式.

分别传递了3个参数

  • context主要用于生成framgnet控制glide加载生命周期
  • 基于url生成key,找到缓存中对应的图片资源
  • 有了图片资源后,加载到imageView中

生命周期的控制——RequestManager

  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

getRetriever中获取glide的RequestManagerRetriever

  • 若glide为null,则会用单例模式构造
  • RequestManagerRetriever

RequestManagerRetriever中通过对context类型的判断,生成RequestManagerFragment/SupportRequestManagerFragment.通过这个framgnet获取生命周期和RequestManagerTreeNode,生成RequestManager

  • 这里的生命周期和androidX中的不是同一个,但作用类似
  • 同一个context只会生成一个RequestManagerFragment/SupportRequestManagerFragment,并且只有这一个RequestManager
RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    
        ...

    // If we're the application level request manager, we may be created on a background thread.
    // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
    // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
    // This should be entirely safe.
    if (Util.isOnBackgroundThread()) {
      mainHandler.post(addSelfToLifecycle);
    } else {
      lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);
        
        ...
  }

RequestManager本身继承LifecycleListener,在构建的时候注册监听.

  • 除了注册自身的监听,还有个ConnectivityMonitor,在有网络权限时,默认实现为DefaultConnectivityMonitor.DefaultConnectivityMonitor在接受到onStart事件时,会注册一个监听网络状态的广播,onStop时注销.当无网状态切换到有网的时候,广播便会执行requestTracker.restartRequests(),开始处理请求.

RequestManager的onStart和onStop方法中,会针对requestTracker和targetTracker这两个对象操作

/**
   * Lifecycle callback that registers for connectivity events (if the
   * android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
   * requests.
   */
  @Override
  public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  /**
   * Lifecycle callback that unregisters for connectivity events (if the
   * android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
   */
  @Override
  public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }
  • RequestTracker 负责跟踪Request
  • TargetTracker 负责跟踪Target

RequestTracker和TargetTracker都是对数组进行操作,既然是数组,通过查看对数组操作的函数,最后定位到RequestBuilder.into函数中

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
        
        Request request = buildRequest(target, targetListener, options, callbackExecutor);
        ...

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

    return target;
  }

加载请求的构建——RequestBuilder

在into方法中,构建了target与request

  • Target继承LifecycleListener,会被TargetTracker控制;Target有很多实现,这里transcodeClass对应的是Drawable.class,生成DrawableImageViewTarget

    public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
        
            ...
    
        return into(
            glideContext.buildImageViewTarget(view, transcodeClass),
            /*targetListener=*/ null,
            requestOptions,
            Executors.mainThreadExecutor());
      }
    
    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)) {
          return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
        } else {
          throw new IllegalArgumentException(
              "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
      }
    }
    
  • Request的基本实现是SingleRequest,同样会被RequestTracker控制

    private Request obtainRequest(
          Object requestLock,
          Target<TranscodeType> target,
          RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> requestOptions,
          RequestCoordinator requestCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight,
          Executor callbackExecutor) {
        return SingleRequest.obtain(
            context,
            glideContext,
            requestLock,
            model,
            transcodeClass,
            requestOptions,
            overrideWidth,
            overrideHeight,
            priority,
            target,
            targetListener,
            requestListeners,
            requestCoordinator,
            glideContext.getEngine(),
            transitionOptions.getTransitionFactory(),
            callbackExecutor);
      }
    

上面构建Target时,之所以说transcodeClass == Drawable.class.是因为transcodeClass的类型由Glide.load函数决定的.

public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
}
public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
}

加载的逻辑

RequestManager的onStart执行时,初始status == Status.PENDING

public void begin() {
    synchronized (requestLock) {

            ...

      status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }

    }
  }

target.getSize(target为DrawableImageViewTarget,getSize为基类ViewTarget函数)→

SizeDeterminer.getSize(获取宽高,若宽高无效,通过注册ViewTreeObserver,在onPreDraw中获取宽高,获取到有效数据后,回调SingleRequest.onSizeReady)→

SingleRequest.onSizeReady→

Engine.load(这个时候会通过Glide.load等参数构建EngineKey,通过key去activeResources/cache中取数据)

  • activeResources 没有大小限制.保存活跃的图片资源.
  • cache 有大小限制.保存不展示的图片资源
  • 受生命周期控制,当RequestManagerFragment/SupportRequestManagerFragment销毁的时候,从activeResources中remove添加到cache中

→waitForExistingOrStartNewJob

  • 从activeResources/cache中没取到数据时,则执行这个方法,如果任务已经存在,则不重复创建,只添加回调

  • 若任务不存在,则构建EngineJob和DecodeJob,并在EngineJob中启动DecodeJob

    public synchronized void start(DecodeJob<R> decodeJob) {
        this.decodeJob = decodeJob;
        GlideExecutor executor =
            decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
        executor.execute(decodeJob);
      }
    
  • 这里利用线程池执行DecodeJob(DecodeJob本身继承Runnable)

  • decodeJob.willDecodeFromCache() 会根据diskCacheStrategy来判断使用那个线程池

    boolean willDecodeFromCache() {
        Stage firstStage = getNextStage(Stage.INITIALIZE);
        return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
    }
    
    private Stage getNextStage(Stage current) {
        switch (current) {
          case INITIALIZE:
            return diskCacheStrategy.decodeCachedResource()
                ? Stage.RESOURCE_CACHE
                : getNextStage(Stage.RESOURCE_CACHE);
          case RESOURCE_CACHE:
            return diskCacheStrategy.decodeCachedData()
                ? Stage.DATA_CACHE
                : getNextStage(Stage.DATA_CACHE);
          case DATA_CACHE:
            // Skip loading from source if the user opted to only retrieve the resource from cache.
            return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
          case SOURCE:
          case FINISHED:
            return Stage.FINISHED;
          default:
            throw new IllegalArgumentException("Unrecognized stage: " + current);
        }
      }
    
  • diskCacheStrategy对象得追溯到Glide构造的时候,有默认实现,也有set方法.共有5种类型,ALL/NONE/DATA/RESOURCE/AUTOMATIC.默认实现为AUTOMATIC.

    public final class GlideBuilder {
    
            private RequestOptionsFactory defaultRequestOptionsFactory =
          new RequestOptionsFactory() {
            @NonNull
            @Override
            public RequestOptions build() {
              return new RequestOptions();
            }
        };
    
            public GlideBuilder setDefaultRequestOptions(@Nullable final RequestOptions requestOptions) {
            return setDefaultRequestOptions(
            new RequestOptionsFactory() {
              @NonNull
              @Override
              public RequestOptions build() {
                return requestOptions != null ? requestOptions : new RequestOptions();
              }
            });
          }
    }
    
  • 通过上面的分析可知,当设置的DiskCacheStrategy == AUTOMATIC == RESOURCE == ALL的时候 firstStage == Stage.RESOURCE_CACHE. DiskCacheStrategy == DATA 时 firstStage == Stage.DATA_CACHE,便会启动diskCacheExecutor.

    • 线程池数量为1
  • 只有DiskCacheStrategy == NONE,才会启动getActiveSourceExecutor.useUnlimitedSourceGeneratorPool和useAnimationPool默认为false,则会启动sourceExecutor

    • 线程池数量由CPU核心数决定,最小为4个
    private GlideExecutor getActiveSourceExecutor() {
        return useUnlimitedSourceGeneratorPool
            ? sourceUnlimitedExecutor
            : (useAnimationPool ? animationExecutor : sourceExecutor);
    }
    
    public static int calculateBestThreadCount() {
        if (bestThreadCount == 0) {
          bestThreadCount =
              Math.min(MAXIMUM_AUTOMATIC_THREAD_COUNT, RuntimeCompat.availableProcessors());
        }
        return bestThreadCount;
      }
    

DecodeJob执行完成后,会回调EngineJob.onResourceReady→

EngineJob.notifyCallbacksOfResult

  1. CallResourceReady.run()→SingleRequest.onResourceReady→target.onResourceReady→设置资源
  2. Engine.onEngineJobComplete(将资源存储到activeResources中)

加载过程——DecodeJob

  • ResourceCacheGenerator 从内存中获取数据
  • DataCacheGenerator 从本地缓存中获取,并存储到内存中
  • SourceGenerator 从网络获取图片,并存储到本地

run→

runWrapped(初始化时,runReason == INITIALIZE)→

getNextStage(根据diskCacheStrategy,更新Stage)→

getNextGenerator(根据stage,获取需要执行的generator)→

runGenerators(while循环执行generator.startNext)→

reschedule(当循环到stage =SOURCE时执行,currentGenerator = SourceGenerator 。交给线程池,重新run)→

run→

runWrapped(runReason = SWITCH_TO_SOURCE_SERVICE)→

SourceGenerator.startNext(初始逻辑,获取loadData中的fetcher,执行fetcher的加载逻辑,fetcher执行成功后)→

SourceGenerator.onDataReady→

DecodeJob.reschedule(重新调度)→

run→

runWrapped(runReason = SWITCH_TO_SOURCE_SERVICE)→

SourceGenerator.startNext(写入本地文件逻辑)→

SourceGenerator.cacheData→

DataCacheGenerator.startNext(找到对应的LoadData,加载数据,加载完成执行callback onDataReady,再接口回调至SourceGenerator.onDataFetcherReady,再接口回调至DecodeJob.onDataFetcherReady)→

reschedule(runReason = RunReason.DECODE_DATA,重新调度)→

run→

runWrapped(runReason = SWITCH_TO_SOURCE_SERVICE)→

decodeFromRetrievedData→

decodeFromData(根据currentData类型,currentDataSource数据源类型,在Registry中找到对应的decoder和transcoder,并组建出decodePaths;根据decodePaths组建LoadPath;根据LoadPath获取Resource<>,正常为LazyBitmapDrawableResource类型)→

notifyEncodeAndRelease→

notifyComplete→触发EngineJob.onResourceReady

总结

  1. Glide在加载时,会生成全局唯一的glide和engine.
  2. 每个activity加载图片时,都会额外创建一个不可见的fragmnt,用于控制生命周期,基于此生成requestManager.
  3. 每个加载请求都会生成target与request,target中收集了view信息及view资源类型.在target确认好view的宽高后,会通过request触发engine的加载逻辑.
  4. engine会尝试从activeResources和cache中加载数据,未成功,便会创建engineJob和decodeJob.
  5. engineJob会启动decodeJob,尝试从ResourceCacheGenerator/DataCacheGenerator/SourceGenerator中获取数据.
  6. 取得数据后,便交由target设置资源,同时保存到engine的activeResources中.
  7. 当activity销毁时,触发生命周期销毁逻辑,资源从activeResources中remove添加到cache中.

调试日志

adb shell setprop log.tag.Engine VERBOSE 根据打印内容判断加载源

  • Started new load 磁盘或者网络
  • Loaded resource from active resources
  • Loaded resource from cache

DecodeJob|Engine|OkHttpLoadImg|PrintingEventListener|SourceGenerator|loadImg

关闭日志 adb shell setprop log.tag.Engine ERROR

相关文章

  • Glide加载一张网图时发生了啥

    这一串代码是加载图片的最简形式. 分别传递了3个参数 context主要用于生成framgnet控制glide加载...

  • Android 关于图片的处理------->圆形图、Glide

    Glide加载圆形图 Glide.with(context).load(url).apply(RequestOpt...

  • Glide加载圆形图片第一次只显示默认图片

    Glide加载圆形图,又设置了默认图,很多时候第一次加载的时候只显示默认图。下面的方案可以解决。 Glide.wi...

  • Glide库的使用

    问题1:如何用glide来加载长图,在我的另外一个文章中有,如下: Glide加载长图方法 问题2:如何用glid...

  • Glide 源码分析之一 with

    前言 最近使用到 Glide 加载本地大图,写代码时想看看 Glide 有没有压缩图片避免出现 OOM 问题,随手...

  • Android 图片预览工具

    仿微信的图片预览工具 这是一个仿朋友圈的列表。 加载图片工具使用Glide 在加载大图时,由于小图在列表已经加载出...

  • Glide-翻译系列-高级加载

    上周,我们讨论了为什么需要使用Glide并写了一个简单的例子展示一张网络上的图片。但是这个并不是Glide加载图片...

  • Glade 图片加载库的使用

    1、导入glide 2、使用glide 增加网络权限: 普通加载网络图片-占位图 和 加载错误图 缩略图:首先显示...

  • Glide

    Glide常用: ////原生加载,图片过大,OOM异常 使用Glide加载 Glide 1.简介 Glide,一...

  • Glide

    Glide常用: //原生加载,图片过大,OOM异常 使用Glide加载 Glide 1.简介 Glide,一个被...

网友评论

    本文标题:Glide加载一张网图时发生了啥

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