Glide加载图片流程重读(一)

2,023 阅读10分钟

前言

Glide 是如何加载图片的,如何优化的,你可能知道是处理了,但是具体处理策略没准说不上来,只能想到常规Bitmap的那几个处理方案(其实大多框架对图片处理都是调用那几个Bitmap APIGlide整体流程也看过,对于这部分没有仔细看,之前着重点在于如何绑定生命周期、如何管理缓存、如何传递图片给控件显示,至于编解码、图片没有仔细关注,还有一些线程锁的使用、线程唤醒处理

这次是带着问题查看图片处理流程的,然后一不小心整个流程又全看了一遍,干脆记录一下,方便以后看,有些知识点可能并没有写全(后文大部分是贴代码),后续遗漏的抽空继续补充。

目录

 

一、Bitmap加载必备知识

在日常开发中,我们都会留意Bitmap的相关加载,如果产生内存溢出,报类似 java.lang.OutofMemoryError: bitmap size exceeds VM budget ,这是我们不希望看到的。

1、BitmapFactory创建Bitmap的四种方法

decodeFiledecodeResourcedecodeStreamdecodeByteArray,其中decodeFiledecodeResource又间接调用了decodeStream方法,最终是在native方法实现的。

2、Bitmap占用内存计算

将图片放到res/drawable-xhdpi目录下,图片分辨率 600 * 600 ,大小65kb

通过 bitmap.getAllocationByteCount() 方法,你可以获取到内存大小位 1440000,为什么呢?

因为默认BitmapFactory使用的是Bitmap.Config.ARGB_8888的存储方式来加载图片内容的,每个像素占了 4 个字节,

内存大小 = 宽 * 高 * 4 = 600 * 600 * 4 = 1440000

假如我们将该图片移动到res/drawable-hdpi下,得到的内存大小是 2560000内存上涨了?为什么呢?

因为在BitmapFactory 在解析图片的过程中,会根据当前设备屏幕密度和图片所在的 drawable 目录来做一个对比,根据这个对比值进行缩放操作。

实际内存大小 = 600 * (320 / 240 )* 600 * (320 / 240) * 4 = 2560000

drawable目录对应的屏幕密度如下

目录drawable-mdpidrawable-hdpidrawable-xhdpidrawable-xxhdpidrawable-xxxhdpi
density11.5234
densityDpi160240320480640

计算规则如下

内存大小 = 宽 *(屏幕的desityDip / drawable所在文件的desityDip) * 高 (屏幕的desityDip / drawable所在文件的desityDip)*4 (Bitmap.Config.ARGB_8888  每个像素占4个字节) 

如果该图片移动到 assets 中,内存大小依旧是 1440000,系统并不会对assets目录中的图片进行缩放操作。

3、Config参数

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565

通过上面 inPreferredConfig 指定像素占用的字节RGB_565 占用 2 个字节,内存相对ARGB_8888直接减半。

4、inSampleSize参数

options.inSampleSize =2;

采样率,值为 2 ,采样后的宽/高 均是原图大小的 1/2,像素为原图的 1/4,内存也占原图的 1/4。值为1就是原始大小,如果值为 3 ,系统会默认向下取整,采样后 和 值为2的表现一样。

原理: 对原图取样,行列方向每隔 inSampleSize 大小格取一个像素,最后合并为一张图

5、尺寸压缩

public static void compressBitmapToFileBySize(Bitmap bmp,File file){
    int ratio = 4;//压缩尺寸的倍数,值越大,图片的尺寸就越小
    Bitmap result = Bitmap.createBitmap(bmp.getWidth()/ratio,bmp.getHeight()/ratio,Bitmap.Config.ARGE_8888);
    Canvas canvas = new Canvas(result);
    RectF rect = new RectF(0,0,bmp.getWidth()/ratio,bmp.getHeight()/ratio);
    canvas.drawBitmap(bmp,null,rect,null);

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    result.compress(Bitmap.CompressFormat.JPEG,100,baos);
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(baos.toByteArray());
    fos.flush();
    fos.close();
}
​

尺寸压缩是通过减少单位尺寸的像素值,真正意义上的降低像素

6、质量压缩

public static void compressImageToFile(Bitmap bmp,File file) throws Exception{
    int quality = 50;// 0 ~ 100
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.JPEG,quality,baos);
    FileOutputSteam fos = new FileOutputStream(file);
    fos.write(baos.toByteArray());
    fos.flush();
    fos.close();
}

通过算法扣掉了图片中的一些某些点附近相近的像素,达到降低质量减少文件大小的目的

7、Bitmap复用

像一些页面中有很多图片,通过按钮控件切换,可能会使用到。

按正常写法,我们可能会直接新建出两个Bitmap

BitmapFactory.decodeResource(getResources(),R.drawable.icon,options);

如果不作为全局变量存放这两个Bitmap,那就面临创建新的Bitmap后,前一个BitmapGC回收,频繁操作对应于Bitmap不断创建和销毁,导致频繁GC、内存抖动,这明显不可取。

如果作为全局变量来存储这两个Bitmap,如果该页面有很多个呢?不可取

所以需要使用 Options.inBitmap 来进行优化

int[] resIds ={R.drawable.icon,R.drawable.icon2};
Bitmap resultBitmap  = getBitmap();
private Bitmap getBitmap(){
    final BitmapFactory.Options options =  new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResource(),resIds[resIndex],options);
    if(canUserForInBitmap(resultBitmap,options)){ //是否可以复用内存
        options.inMutable = true; //这里如果不置为 true 的话,BitmapFactory 将不会重复利用 Bitmap 内存,并输出相应 warning 日志
        options.inBitmap = resultBitmap;//赋值 为之前创建的 resultBitmap 对象,避免重新分配内存
    }
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(getResources(),resIds[resIndex],options);
}

为什么复用的时候还去判断是否能复用呢?

因为在Android 4.4 之前,只能重用相同大小的 Bitmap内存区域,Android 4.4 之后只要这块内存比将要分配内存的Bitmap大就可以重用

public static boolean canUserForInBitmap(Bitmap bitmap,BitmapFactory.Options targetOptions){
    int width = targetOptions.outWidth /  Math.max(targetOptions.inSampleSize,1);
    int height = targetOptions.outHeight / Math.max(targetOptions.inSampleSize,1);
    int  byteCount  = widht * height* getBytesPerPixel(bitmap.getConfig());
    return byteCount <= bitmap.getAllocationByteCount();
}
private static int  getBytesPerPixel(Bitmap.Config config){
    int bytePerPixel;
    switch(config){
        case ALPHA_8:
            bytePerPixel= 1;
            break;
        case RGB_565:
        case ARGB_4444:
            bytePerPixel = 2;
            break;
        default:
            bytePerPixel= 4;
            break;
    }
    return bytePerPixel;
}

8、recycle 方法

if(bitmap != null && !bitmap.isRecycled()){
    bitmap.recycle();
    bitmap = null;
}

Bitmap内存分配三、Android内存优化 提到过

  • Android 3.0前,Bitmap对象放在堆,像素数据放在Native种,如果不手动调用recycle, Bitmap Native 内存回收完全依赖 finalize 回调,这个时机很不可控

  • Android 3.0Android 7.0 ,将Bitmap对象和像素数据一起放在Java堆中,这样就算不调用recycle, Bitmap内存也会随着对象一起被回收。不过Bitmap 是内存消耗大户,放到Java堆中,App最大内存会不会立马被使用完了,而且放到堆中,不好好优化,还会出现频繁GC

  • Android 8.0 ,将像素数据放到了Native中,实现了和对象一起快速释放回收。并且还增加了硬件位图 Hardware Bitmap,减少了图片内存,提升了绘制效率

那么问题来了,recycle() 方法需不需要调用?目前开发是不需要主动调用的,毕竟api target 都在 Android 3.0Android 3.0 后,垃圾回收器会自动收集不可用的Bitmap对象进行回收。

9、图片分片显示

加载的图片很长时,例如滚动截图生成的图片,我们可能就需要使用到BitmapRegionDecoder 类。

使用规则,具体用法可仔细搜索,或者见 Android高清加载巨图方案 拒绝压缩图片、网上成熟的例子,添加touch事件动态设置Bitmap显示的区域 LargeImageView

首先需要使用 BitmapRegionDecoder 将图片加载到内存中,图片可以以绝对路径、文件描述符、输入流的方式传递给 BitmapRegionDecoder

//以下代码是显示图片左上角(200,200) 区域
private void  showRegionImage(){
    try{
      InputStream  is = getAssets().open("big.png");
      //设置显示图片的中心区域
      BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is,false);
      BitmapFactory.Options options = new BitmapFactory.Options();
      Bitmap  bitmap = decoder.decodeRegion(new Rect(0,0,200,200), options);
      regionImage.setImageBitmap(bitmap);
    }catch(IOException e){

    }
}

10、注意点

由于加载大图时不能真正加载图片到内存,然后获取原图宽高,所以对一些参数的设置,例如 inSampleSzie 等,需要设置 inJustDecodeBounds 参数,实现只解析图片的宽高信息,并不会真正的加载图片,所以这个操作时轻量级

int inSampleSize = 8;//数值越高,图片像素越低
BitmapFactory.Options options = new BitmapFatcory.Options();
options.inJustDecodeBounds = true;//为true不会真正加载图片
options.inSampleSize = inSampleSize;//这里计算采样率
.......//各种操作
options.inJustDecodeBounds = false; //重新加载
Bitmap bitamp = BitampFactory.decodeFile(filePath,options);

注意避免 先加载原来的Bitmap,然后再进行一系列的优化。

11、IOS拍照1M的图片为什么比Android拍照出来的5M图片还清楚

  • 1995年出来的JPEG图片处理引擎,用于最初的PC上面

  • 2005Skia开发了一套 Skia引擎,基于JPEG的二次开发,便于浏览器的使用

  • Android 使用的是 Skia引擎,不过是阉割版的Skia引擎,谷歌去掉了内部的 哈夫曼算法,采用定长编码算法,导致图片处理后文件变大了。

  • 为什么会替换成定长编码算法,因为Android 手机上,CPU和内存都非常吃紧,而哈夫曼算法非常吃 CPU

二、Glide加载图片流程

三分钟全面了解Android主流图片加载库

这里对加载流程的详细过程不过多分析,大概流程如下图,至于基于Glide哪个版本看的忘了。

大概流程就是通过一个SupportRequestManagerFragment,绑定了LifeCycle,后续在onDestroy时会触发回收弱引用缓存(ActiveResources持有的Map),放回到内存缓存(LruResourceCache)中。

图片处理利用了Bitmap的各种优化参数 inBitmapinDensity等。

三、Glide加载图片

Glide版本 4.5.0

1、Glide的with方法

就是拿到RequestManager对象,在RequestManager的构造函数中,会构造一个默认的RequestOptions,顾名思义,这是一个对图片操作的类

这个过程中会利用SupportRequestManagerFragment来绑定到当前Activity中,并通过以下的代码片段避免了类似并发场景获取Fragment

  SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

通过with阶段也初始化了各种配置信息,可以大概看一下

  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    // GlideModule的相关配置
    //获取注解@GlideModule
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }
​
    if (annotationGeneratedModule != null
        && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
      Set<Class<?>> excludedModuleClasses =
          annotationGeneratedModule.getExcludedModuleClasses();
      Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
      while (iterator.hasNext()) {
        com.bumptech.glide.module.GlideModule current = iterator.next();
.......
​
RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //这里就是一些缓存的配置
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

builder.build

public Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }
​
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
​
    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }
​
    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }
​
    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }
​
    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }
​
    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }
​
    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
​
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
​
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              GlideExecutor.newAnimationExecutor(),
              isActiveResourceRetentionAllowed);
    }
​
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);
​
    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptions.lock(),
        defaultTransitionOptions);
  }

2、Glide的load方法

返回了 RequestBuilder对象,例如load(String url)

 public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }
 public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;//设置的 url
    isModelSet = true; //url已经设置
    return this;
  }

可以看到这两个方法并没有编解码的设置,那就是在into方法中

3、Glide的into方法

构建请求、发起请求、取缓存、读缓存、服务器请求、设置缓存、显示图片的一个过程

 public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
​
    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      // 这里看官方注释也知道,会根据ImageView的设置来保存对应的图片缩放类型
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
     ......
      }
    }
    //下面参数 transcodeClass 是Drawable.class 或者是 Bitmap.class ,这个参数是 load方法时构建RequestBuilder设置好的
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

继续看一下 glideContext.buildImageTarget方法

  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
    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)");
    }
  }

这里 如果调用 load(String url),那么返回的时DrawableImageViewTarget,如果是 loadBitmap 那么返回的是BitmapImageViewTarget

接下来看 into 方法,参数target 为上面说到的两种ImageViewTarget

  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    //注释 1 使用Glide必须调用load方法,否则这里会报错
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
​
    options = options.autoClone();
    //注释 2 建立请求
    Request request = buildRequest(target, targetListener, options);
​
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }
​
    requestManager.clear(target);
    //注释 3 内部为对View设置Tag
    target.setRequest(request);
    //注释 4 跟踪请求
    requestManager.track(target, request);
​
    return target;
  }

第一问,Request 内部是如何构建的,真实类形是什么?

注释 2 建立请求这里,内部会调用到 buildRequestRecursive 方法

private Request buildRequestRecursive(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {
​
    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
​
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions);
​
    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
​
    .....
  }
​

我们继续看一下 buildThumbnailRequestRecursive 方法

private Request buildThumbnailRequestRecursive(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {
      //如果是下面这样调用Glide加载图片的话,缩略图 thumbnailBuilder = null,thumbSizeMultiplier = null
      //Glide.witt(this).load("xxx").thumbnail().into(view) 
      //一般都会调用thumbnail传递浮点数,即thumbSizeMultiplier,0.1f就代表原图的 10%,所以这里直接分析第二个
    if (thumbnailBuilder != null) {
     ......
    } else if (thumbSizeMultiplier != null) {
      // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
      ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
     //获取一个正常请求的对象
     Request fullRequest =
          obtainRequest(
              target,
              targetListener,
              requestOptions,
              coordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight);
      RequestOptions thumbnailOptions = requestOptions.clone()
          .sizeMultiplier(thumbSizeMultiplier);
     //获取一个缩略图请求对象
      Request thumbnailRequest =
          obtainRequest(
              target,
              targetListener,
              thumbnailOptions,
              coordinator,
              transitionOptions,
              getThumbnailPriority(priority),
              overrideWidth,
              overrideHeight);
     // ThumbnailRequestCoordinator coorinator是一个能同时加载缩略图和正常图的请求对象
      coordinator.setRequests(fullRequest, thumbnailRequest);
      return coordinator;
    } else {
      // Base case: no thumbnail.
      return obtainRequest(
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight);
    }
  }

真正建立请求时都调用了 obtainRequest 方法

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

可以看到最终返回的RequestSingleReqeust 对象,可同时进行缩略图和正常图的请求。

第二问,跟踪请求里做了些什么?

我们看最初into方法内的requestManager.track(target, request) 方法

  void track(Target<?> target, Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

直接看 requestTracker.runRequest 方法

  public void runRequest(Request request) {
    requests.add(request);
    //不是暂停状态则开始请求
    if (!isPaused) {
    //之前已经分析了,Request的真实对象类型是 SingleRequest 
      request.begin();
    } else {
      pendingRequests.add(request);
    }
  }

SingleRequestbegin方法

  public void begin() {
    .....
    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }
    .......
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }
​
    // Restarts for requests that are neither complete nor running can be treated as new requests
    // and can run again from the beginning.
​
    status = Status.WAITING_FOR_SIZE;
    //注释
    //如果在使用Glide时,通过apply传递了一个新的RequestOptions,并且设置了oveerideWith和overrideHeight
    //就会执行 onSizeReady
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
    //无效就会等到设置ViewTreeObervable 等待通知拿到,然后调用 onSizeReady
      target.getSize(this);
    }
​
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
        //注释
        //占位图设置
      target.onLoadStarted(getPlaceholderDrawable());
    }
​
  }

通过上面代码以及分析可以了解到,begin方法,最终会执行到 onSizeReady方法,然后会设置占位图。

关键代码在onSizeReady

public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (IS_VERBOSE_LOGGABLE) {
      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    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);
...
  }

上述代码中的 Engine 是一个负责加载、管理活跃缓存和缓存资源的类,我们进入load方法一探究竟,分析直接卸载代码内,这里对缓存的KeyValue说明可以看看 常用集合类相关知识点总结 LruCahche部分,后续代码分析中也有体现,根据不同策略,构建的Key的对象类型不一样。

下文 Glide加载图片流程重读(二)