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

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

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