Android 圖片載入框架

貓尾巴發表於2019-05-05

基本使用流程

Glide最基本的使用流程就是下面這行程式碼,其它所有擴充套件的額外功能都是以其建造者鏈式呼叫的基礎上增加的。

GlideApp.with(context).load(url).into(iv);
複製程式碼

其中的GlideApp是註解處理器自動生成的,要使用GlideApp,必須先配置應用的AppGlideModule模組,裡面可以為空配置,也可以根據實際情況新增指定配置。

@GlideModule
public class MyAppGlideModule extends AppGlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // 實際使用中根據情況可以新增如下配置
        <!--builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));-->
        <!--int memoryCacheSizeBytes = 1024 * 1024 * 20;-->
        <!--builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));-->
        <!--int bitmapPoolSizeBytes = 1024 * 1024 * 30;-->
        <!--builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeBytes));-->
        <!--int diskCacheSizeBytes = 1024 * 1024 * 100;-->
        <!--builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));-->
    }
}
複製程式碼

GlideApp.with(context)

Android 圖片載入框架

GlideApp#with

return (GlideRequests) Glide.with(context);
複製程式碼

Glide#with

return getRetriever(context).get(context);

return Glide.get(context).getRequestManagerRetriever();

// 外部使用了雙重檢鎖的同步方式確保同一時刻只執一次Glide的初始化
checkAndInitializeGlide(context);

initializeGlide(context);

// 最終執行到Glide的另一個過載方法
initializeGlide(context, new GlideBuilder());

@SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context   context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    // 1、獲取前面應用中帶註解的GlideModule
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    // 2、如果GlideModule為空或者可配置manifest裡面的標誌為true,則獲取manifest裡面
    // 配置的GlideModule模組(manifestModules)。
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new   ManifestParser(applicationContext).parse();
    }

    ...
    //配置註解生成類的請求管理
    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
    //配置manifest的屬性
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    //配置註解生成類的屬性
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    // 3、初始化各種配置資訊
    Glide glide = builder.build(applicationContext);
    // 4、把manifestModules以及annotationGeneratedModule裡面的配置資訊放到builder
    // 裡面(applyOptions)替換glide預設元件(registerComponents)
    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;
}
複製程式碼

GlideBuilder#build

@NonNull
  Glide build(@NonNull Context context) {
    // 建立請求圖片執行緒池sourceExecutor
    if (sourceExecutor == null) {
      sourceExecutor =   GlideExecutor.newSourceExecutor();
    }

    // 建立硬碟快取執行緒池diskCacheExecutor
    if (diskCacheExecutor == null) {
      diskCacheExecutor =   GlideExecutor.newDiskCacheExecutor();
    }

    // 建立動畫執行緒池animationExecutor
    if (animationExecutor == null) {
      animationExecutor =   GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new   MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new   DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
      // 依據裝置的螢幕密度和尺寸設定各種pool的size
      int size =   memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        // 建立圖片執行緒池LruBitmapPool,快取所有被釋放的bitmap
        // 快取策略在API大於19時,為SizeConfigStrategy,小於為AttributeStrategy。
        // 其中SizeConfigStrategy是以bitmap的size和config為key,value為bitmap的HashMap
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    // 建立物件陣列快取池LruArrayPool,預設4M
    if (arrayPool == null) {
      arrayPool = new   LruArrayPool(memorySizeCalculator.getArrayPoolSiz  eInBytes());
    }

    // 建立LruResourceCache,記憶體快取
    if (memoryCache == null) {
      memoryCache = new   LruResourceCache(memorySizeCalculator.getMemoryCa  cheSize());
    }

    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);
}
複製程式碼

Glide#Glide構造方法

Glide(...) {
    ...
    // 註冊管理任務執行物件的類(Registry)
    // Registry是一個工廠,而其中所有註冊的物件都是一個工廠員工,當任務分發時,
    // 根據當前任務的性質,分發給相應員工進行處理
    registry = new Registry();

    ...

    // 這裡大概有60餘次的append或register員工元件(解析器、編解碼器、工廠類、轉碼類等等元件)
    registry
    .append(ByteBuffer.class, new ByteBufferEncoder())
    .append(InputStream.class, new StreamEncoder(arrayPool))

    // 根據給定子類產出對應型別的target(BitmapImageViewTarget / DrawableImageViewTarget)
    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();

    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptions,
            defaultTransitionOptions,
            engine,
            logLevel);
}
複製程式碼

RequestManagerRetriever#get

@NonNull
public RequestManager get(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {
    // 如果當前執行緒是主執行緒且context不是Application走相應的get過載方法
    if (context instanceof FragmentActivity) {
      return get((FragmentActivity) context);
    } else if (context instanceof Activity) {
      return get((Activity) context);
    } else if (context instanceof ContextWrapper) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }

  // 否則直接將請求與ApplicationLifecycle關聯
  return getApplicationManager(context);
}
複製程式碼

這裡總結一下,對於當前傳入的context是application或當前執行緒是子執行緒時,請求的生命週期和ApplicationLifecycle關聯,否則,context是FragmentActivity或Fragment時,在當前元件新增一個SupportFragment(SupportRequestManagerFragment),context是Activity時,在當前元件新增一個Fragment(RequestManagerFragment)。

Android 圖片載入框架

為什麼要使用註解生成的GlideApp

ApplicationLifecycle,SupportRequestManagerFragment,RequestManagerFragment 如何跟蹤請求生命週期

load(url)

GlideRequest(RequestManager)#load

return (GlideRequest<Drawable>) super.load(string);

return asDrawable().load(string);

// 1、asDrawable部分
return (GlideRequest<Drawable>) super.asDrawable();

return as(Drawable.class);

// 最終返回了一個GlideRequest(RequestManager的子類)
return new GlideRequest<>(glide, this, resourceClass, context);

// 2、load部分
return (GlideRequest<TranscodeType>) super.load(string);

return loadGeneric(string);

@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    // model則為設定的url
    this.model = model;
    // 記錄url已設定
    isModelSet = true;
    return this;
}
複製程式碼

load這部分的原始碼很簡單,就是給GlideRequest(RequestManager)設定了要請求的mode(url),並記錄了url已設定的狀態。

Android 圖片載入框架

into(iv)

RequestBuilder.into

 @NonNull
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.
    switch (view.getScaleType()) {
      // 這個RequestOptions裡儲存了要設定的scaleType,Glide自身封裝了CenterCrop、CenterInside、
      // FitCenter、CenterInside四種規格。
      case CENTER_CROP:
        requestOptions = requestOptions.clone().optionalCenterCrop();
        break;
      case CENTER_INSIDE:
        requestOptions = requestOptions.clone().optionalCenterInside()  ;
        break;
      case FIT_CENTER:
      case FIT_START:
      case FIT_END:
        requestOptions = requestOptions.clone().optionalFitCenter();
        break;
      case FIT_XY:
        requestOptions = requestOptions.clone().optionalCenterInside()  ;
        break;
      case CENTER:
      case MATRIX:
      default:
        // Do nothing.
    }
  }

  // 注意,這個transcodeClass是指的drawable或bitmap
  return into(
      glideContext.buildImageViewTarget(view, transcodeClass),
      /*targetListener=*/ null,
      requestOptions);
}
複製程式碼

GlideContext#buildImageViewTarget

return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
複製程式碼

ImageViewTargetFactory#buildTarget

@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
    @NonNull Class<Z> clazz) {
  // 返回展示Bimtap/Drawable資源的目標物件
  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)");
  }
}
複製程式碼

Glide內部只維護了兩種target,一種是BitmapImageViewTarget,另一種則是DrawableImageViewTarget。

RequestBuilder#into

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType>   targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call   #load() before calling #into()");
    }

    options = options.autoClone();
    // 分析1.建立請求
    Request request = buildRequest(target,     targetListener, options);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousReques    t(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).isRunni  ng()) {
        // 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);
    target.setRequest(request);
    // 分析2.真正追蹤請求的地方
    requestManager.track(target, request);

    return target;
}

// 分析1
private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType>   targetListener,
      RequestOptions requestOptions) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions);
}

// 分析1
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(異常處理物件)
      errorRequestCoordinator = new   ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }

    // 遞迴建立縮圖請求
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }

    ...

    Request errorRequest =     errorBuilder.buildRequestRecursive(
        target,
        targetListener,
        errorRequestCoordinator,
        errorBuilder.transitionOptions,
        errorBuilder.requestOptions.getPriority(),
        errorOverrideWidth,
        errorOverrideHeight,
        errorBuilder.requestOptions);
    errorRequestCoordinator.setRequests(mainRequest,     errorRequest);
    return errorRequestCoordinator;
}

// 分析1
private Request buildThumbnailRequestRecursive(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {
    if (thumbnailBuilder != null) {
      // Recursive case: contains a potentially recursive thumbnail request builder.

      ...

      ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
      // 獲取一個正常請求物件
      Request fullRequest =
          obtainRequest(
              target,
              targetListener,
              requestOptions,
              coordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight);
      isThumbnailBuilt = true;
      // Recursively generate thumbnail requests.
      // 使用遞迴的方式建立一個縮圖請求物件
      Request thumbRequest =
          thumbnailBuilder.buildRequestRecursive(
              target,
              targetListener,
              coordinator,
              thumbTransitionOptions,
              thumbPriority,
              thumbOverrideWidth,
              thumbOverrideHeight,
              thumbnailBuilder.requestOptions);
      isThumbnailBuilt = false;
      // coordinator(ThumbnailRequestCoordinator)是作為兩者的協調者,
      // 能夠同時載入縮圖和正常的圖的請求
      coordinator.setRequests(fullRequest, thumbRequest);
      return coordinator;
    } else if (thumbSizeMultiplier != null) {
      // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
      // 當設定了縮略的比例thumbSizeMultiplier(0 ~  1)時,
      // 不需要遞迴建立縮圖請求
      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);

      coordinator.setRequests(fullRequest, thumbnailRequest);
      return coordinator;
    } else {
      // Base case: no thumbnail.
      // 沒有縮圖請求時,直接獲取一個正常圖請求
      return obtainRequest(
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight);
    }
}

private Request obtainRequest(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      RequestOptions requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType>   transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight) {
    // 最終實際返回的是一個SingleRequest物件(將制定的資源載入進對應的Target
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory());
}
複製程式碼

1處的buildRequest()方法裡建立了請求,且最多可同時進行縮圖和正常圖的請求,最後,呼叫了requestManager.track(target, request)方法。

RequestManager#track

// 分析2
void track(@NonNull Target<?> target, @NonNull Request request) {
    // 加入一個target目標集合(Set)
    targetTracker.track(target);

    requestTracker.runRequest(request);
}
複製程式碼

RequestTracker#runRequest

// 分析2
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");
      }
      // 否則清空請求,加入延遲請求佇列(為了對這些請求維持一個強引用,使用了ArrayList實現)
      pendingRequests.add(request);
    }
}
複製程式碼

SingleRequest#begin

// 分析2
@Override
public void begin() {

  ...

  if (model == null) {

    ...
    // model(url)為空,回撥載入失敗
    onLoadFailed(new GlideException("Received null   model"), logLevel);
    return;
  }

  if (status == Status.RUNNING) {
    throw new IllegalArgumentException("Cannot   restart a running request");
  }


  if (status == Status.COMPLETE) {
    onResourceReady(resource,   DataSource.MEMORY_CACHE);
    return;
  }

  status = Status.WAITING_FOR_SIZE;
  if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
    // 當使用override() API為圖片指定了一個固定的寬高時直接執行onSizeReady,
    // 最終的核心處理位於onSizeReady
    onSizeReady(overrideWidth, overrideHeight);
  } else {
    // 根據imageView的寬高算出圖片的寬高,最終也會走到onSizeReady
    target.getSize(this);
  }

  if ((status == Status.RUNNING || status ==     Status.WAITING_FOR_SIZE)
      && canNotifyStatusChanged()) {
    // 預先載入設定的縮圖
    target.onLoadStarted(getPlaceholderDrawable());
  }
  if (IS_VERBOSE_LOGGABLE) {
    logV("finished run method in " +   LogTime.getElapsedMillis(startTime));
  }
}
複製程式碼

從requestManager.track(target, request)開始,最終會執行到SingleRequest#begin()方法的onSizeReady,可以猜到(因為後面只做了預載入縮圖的處理),真正的請求就是從這裡開始的。

SingleRequest#onSizeReady

// 分析2
@Override
public void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();

  ...

  status = Status.RUNNING;

  float sizeMultiplier = requestOptions.getSizeMultiplier();
  this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
  this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

  ...

  // 根據給定的配置進行載入,engine是一個負責載入、管理活躍和快取資源的引擎類
  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

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) {

  ...

  // 先從弱引用中查詢,如果有的話回撥onResourceReady並直接返回
  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;
  }

  // 沒有再從記憶體中查詢,有的話會取出並放到ActiveResources(內部維護的弱引用快取map)裡面
  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);
  }

  // 如果記憶體中沒有,則建立engineJob(decodejob的回撥類,管理下載過程以及狀態)
  EngineJob<R> engineJob =
      engineJobFactory.build(
          key,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache);

  // 建立解析工作物件
  DecodeJob<R> decodeJob =
      decodeJobFactory.build(
          glideContext,
          model,
          key,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          onlyRetrieveFromCache,
          options,
          engineJob);

  // 放在Jobs內部維護的HashMap中
  jobs.put(key, engineJob);

  // 關注點8 後面分析會用到
  // 註冊ResourceCallback介面
  engineJob.addCallback(cb);
  // 內部開啟執行緒去請求
  engineJob.start(decodeJob);

  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Started new load", startTime,   key);
  }
  return new LoadStatus(cb, engineJob);
}

public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    // willDecodeFromCache方法內部根據不同的階段stage,如果是RESOURCE_CACHE/DATA_CACHE則返回true,使用diskCacheExecutor,否則呼叫getActiveSourceExecutor,內部會根據相應的條件返回sourceUnlimitedExecutor/animationExecutor/sourceExecutor
    GlideExecutor executor =   
    decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
}
複製程式碼

可以看到,最終Engine(引擎)類內部會執行到自身的start方法,它會根據不同的配置採用不同的執行緒池使用diskCacheExecutor/sourceUnlimitedExecutor/animationExecutor/sourceExecutor來執行最終的解碼任務decodeJob。

DecodeJob#run

runWrapped();

private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        // 關注點1
        currentGenerator = getNextGenerator();
        // 關注點2 內部會呼叫相應Generator的startNext()
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        // 關注點3 將獲取的資料解碼成對應的資源
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized     run reason: " + runReason);
    }
}

// 關注點1,完整情況下,會非同步依次生成這裡的ResourceCacheGenerator、DataCacheGenerator和SourceGenerator物件,並在之後執行其中的startNext()
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);
    }
}
複製程式碼

SourceGenerator#startNext

// 關注點2
@Override
public boolean startNext() {
  // dataToCache資料不為空的話快取到硬碟(第一執行該方法是不會呼叫的)
  if (dataToCache != null) {
    Object data = dataToCache;
    dataToCache = null;
    cacheData(data);
  }

  if (sourceCacheGenerator != null &&     sourceCacheGenerator.startNext()) {
    return true;
  }
  sourceCacheGenerator = null;

  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    // 關注點4 getLoadData()方法內部會在modelLoaders裡面找到ModelLoder物件
    // (每個Generator對應一個ModelLoader),
    // 並使用modelLoader.buildLoadData方法返回一個loadData列表
    loadData =   helper.getLoadData().get(loadDataListIndex++);
    if (loadData != null
        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
        || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
      started = true;
      // 關注點6 通過loadData物件的fetcher物件(有關注點3的分析可知其實現類為HttpUrlFetcher)的
      // loadData方法來獲取圖片資料
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}
複製程式碼

DecodeHelper#getLoadData

List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      List<ModelLoader<Object, ?>> modelLoaders =   glideContext.getRegistry().getModelLoaders(model)  ;
      //noinspection ForLoopReplaceableByForEach to   improve perf
      for (int i = 0, size = modelLoaders.size(); i <   size; i++) {
        ModelLoader<Object, ?> modelLoader =     modelLoaders.get(i);
        // 注意:這裡最終是通過HttpGlideUrlLoader的buildLoadData獲取到實際的loadData物件
        LoadData<?> current =
            modelLoader.buildLoadData(model, width,     height, options);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
    return loadData;
}
複製程式碼

HttpGlideUrlLoader#buildLoadData

@Override
public LoadData<InputStream> buildLoadData(@NonNull   GlideUrl model, int width, int height,
    @NonNull Options options) {
  // GlideUrls memoize parsed URLs so caching them     saves a few object instantiations and time
  // spent parsing urls.
  GlideUrl url = model;
  if (modelCache != null) {
    url = modelCache.get(model, 0, 0);
    if (url == null) {
      // 關注點5
      modelCache.put(model, 0, 0, model);
      url = model;
    }
  }
  int timeout = options.get(TIMEOUT);
  // 注意,這裡建立了一個DataFetcher的實現類HttpUrlFetcher
  return new LoadData<>(url, new HttpUrlFetcher(url,     timeout));
}

// 關注點5
public void put(A model, int width, int height, B value) {
    ModelKey<A> key = ModelKey.get(model, width,     height);
    // 最終是通過LruCache來快取對應的值,key是一個ModelKey物件(由model、width、height三個屬性組成)
    cache.put(key, value);
}
複製程式碼

從這裡的分析,我們明白了HttpUrlFetcher實際上就是最終的請求執行者,而且,我們知道了Glide會使用LruCache來對解析後的url來進行快取,以便後續可以省去解析url的時間。

HttpUrlFetcher#loadData

@Override
public void loadData(@NonNull Priority priority,
    @NonNull DataCallback<? super InputStream>   callback) {
  long startTime = LogTime.getLogTime();
  try {
    // 關注點6
    // loadDataWithRedirects內部是通過HttpURLConnection網路請求資料
    InputStream result =   loadDataWithRedirects(glideUrl.toURL(), 0, null,   glideUrl.getHeaders());
    // 請求成功回撥onDataReady()
    callback.onDataReady(result);
  } catch (IOException e) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Failed to load data for url", e);
    }
    callback.onLoadFailed(e);
  } finally {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Finished http url fetcher fetch in     " + LogTime.getElapsedMillis(startTime));
    }
  }
}

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
  Map<String, String> headers) throws IOException {

    ...

    urlConnection.connect();
    // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    // 只要是2xx形式的狀態碼則判斷為成功
    if (isHttpOk(statusCode)) {
      // 從urlConnection中獲取資源流
      return getStreamForSuccessfulRequest(urlConnection);
    } else if (isHttpRedirect(statusCode)) {

      ...

      // 重定向請求
      return loadDataWithRedirects(redirectUrl, redirects + 1, url,   headers);
    } else if (statusCode == INVALID_STATUS_CODE) {
      throw new HttpException(statusCode);
    } else {
      throw new HttpException(urlConnection.getResponseMessage(),   statusCode);
    }
}

private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
  throws IOException {
    if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
      int contentLength = urlConnection.getContentLength();
      stream = ContentLengthInputStream.obtain(urlConnection.getInputStr  eam(), contentLength);
    } else {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Got non empty content encoding: " +     urlConnection.getContentEncoding());
      }
      stream = urlConnection.getInputStream();
    }
    return stream;
}
複製程式碼

在HttpUrlFetcher#loadData方法的loadDataWithRedirects裡面,Glide通過原生的HttpURLConnection進行請求後,並呼叫getStreamForSuccessfulRequest()方法獲取到了最終的圖片流。

DecodeJob#run

在我們通過HtttpUrlFetcher的loadData()方法請求得到對應的流之後,我們還必須對流進行處理得到最終我們想要的資源。這裡我們回到第10步DecodeJob#run方法的關注點3處,這行程式碼將會對流進行解碼。

decodeFromRetrievedData();
複製程式碼
private void decodeFromRetrievedData() {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Retrieved data", startFetchTime,
          "data: " + currentData
              + ", cache key: " + currentSourceKey
              + ", fetcher: " + currentFetcher);
    }
    Resource<R> resource = null;
    try {
      //  核心程式碼 
      // 從資料中解碼得到資源
      resource = decodeFromData(currentFetcher, currentData,   currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      // 關注點8 
      // 編碼和釋出最終得到的Resource<Bitmap>物件
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
}

 private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
  DataSource dataSource) throws GlideException {
    try {
      if (data == null) {
        return null;
      }
      long startTime = LogTime.getLogTime();
      // 核心程式碼
      // 進一步包裝瞭解碼方法
      Resource<R> result = decodeFromFetcher(data, dataSource);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Decoded result " + result, startTime);
      }
      return result;
    } finally {
      fetcher.cleanup();
    }
}

@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
  throws GlideException {
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    // 核心程式碼
    // 將解碼任務分發給LoadPath
    return runLoadPath(data, dataSource, path);
}

private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
  LoadPath<Data, ResourceType, R> path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    // 將資料進一步包裝
    DataRewinder<Data> rewinder =     glideContext.getRegistry().getRewinder(data);
    try {
      // ResourceType in DecodeCallback below is required for   compilation to work with gradle.
      // 核心程式碼
      // 將解碼任務分發給LoadPath
      return path.load(
          rewinder, options, width, height, new   DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
}
複製程式碼

LoadPath#load

public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
  int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
    List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
    try {
      // 核心程式碼
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      listPool.release(throwables);
    }
}
複製程式碼
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
      @NonNull Options options,
      int width, int height, DecodePath.DecodeCallback<ResourceType>   decodeCallback,
      List<Throwable> exceptions) throws GlideException {
    Resource<Transcode> result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path =   decodePaths.get(i);
      try {
        // 核心程式碼
        // 將解碼任務又進一步分發給DecodePath的decode方法去解碼
        result = path.decode(rewinder, width, height, options,     decodeCallback);
      } catch (GlideException e) {
        exceptions.add(e);
      }
      if (result != null) {
        break;
      }
    }

    if (result == null) {
      throw new GlideException(failureMessage, new   ArrayList<>(exceptions));
    }

    return result;
}
複製程式碼

DecodePath#decode

public Resource<Transcode> decode(DataRewinder<DataType> rewinder,     int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback)   throws GlideException {
    // 核心程式碼
    // 繼續呼叫DecodePath的decodeResource方法去解析出資料
    Resource<ResourceType> decoded = decodeResource(rewinder, width,     height, options);
    Resource<ResourceType> transformed =     callback.onResourceDecoded(decoded);
    return transcoder.transcode(transformed, options);
}

@NonNull
private Resource<ResourceType> decodeResource(DataRewinder<DataType>   rewinder, int width,
    int height, @NonNull Options options) throws GlideException {
  List<Throwable> exceptions =     Preconditions.checkNotNull(listPool.acquire());
  try {
    // 核心程式碼
    return decodeResourceWithList(rewinder, width, height, options,   exceptions);
  } finally {
    listPool.release(exceptions);
  }
}

@NonNull
private Resource<ResourceType>   decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
    int height, @NonNull Options options, List<Throwable> exceptions)   throws GlideException {
  Resource<ResourceType> result = null;
  //noinspection ForLoopReplaceableByForEach to improve perf
  for (int i = 0, size = decoders.size(); i < size; i++) {
    ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
    try {
      DataType data = rewinder.rewindAndGet();
      if (decoder.handles(data, options)) {
        // 獲取包裝的資料
        data = rewinder.rewindAndGet();
        // 核心程式碼 
        // 根據DataType和ResourceType的型別分發給不同的解碼器Decoder
        result = decoder.decode(data, width, height, options);
      }
    } catch (IOException | RuntimeException | OutOfMemoryError e) {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Failed to decode data for " + decoder, e);
      }
      exceptions.add(e);
    }

    if (result != null) {
      break;
    }
  }

  if (result == null) {
    throw new GlideException(failureMessage, new   ArrayList<>(exceptions));
  }
  return result;
}
複製程式碼

最終執行到了decoder.decode()這行程式碼,decode是一個ResourceDecoder介面(資源解碼器),根據不同的DataType和ResourceType它會有不同的實現類,這裡的實現類是ByteBufferBitmapDecoder,接下來讓我們來看看這個解碼器內部的解碼流程。

ByteBufferBitmapDecoder#decode

/**
 * Decodes {@link android.graphics.Bitmap Bitmaps} from {@link    java.nio.ByteBuffer ByteBuffers}.
 */
public class ByteBufferBitmapDecoder implements     ResourceDecoder<ByteBuffer, Bitmap> {

  ...

  @Override
  public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width,   int height,
      @NonNull Options options)
      throws IOException {
    InputStream is = ByteBufferUtil.toStream(source);
    // 核心程式碼
    return downsampler.decode(is, width, height, options);
  }
}
複製程式碼

最終是使用了一個downsampler,它是一個壓縮器,主要是對流進行解碼,壓縮,圓角等處理。

DownSampler#decode

public Resource<Bitmap> decode(InputStream is, int outWidth, int outHeight,
  Options options) throws IOException {
    return decode(is, outWidth, outHeight, options, EMPTY_CALLBACKS);
}

 @SuppressWarnings({"resource", "deprecation"})
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
      Options options, DecodeCallbacks callbacks) throws IOException {
    Preconditions.checkArgument(is.markSupported(), "You must provide an     InputStream that supports"
        + " mark()");

    ...

    try {
      // 核心程式碼
      Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
          downsampleStrategy, decodeFormat, isHardwareConfigAllowed,   requestedWidth,
          requestedHeight, fixBitmapToRequestedDimensions, callbacks);
      // 關注點7   
      // 解碼得到Bitmap物件後,包裝成BitmapResource物件返回,
      // 通過內部的get方法得到Resource<Bitmap>物件
      return BitmapResource.obtain(result, bitmapPool);
    } finally {
      releaseOptions(bitmapFactoryOptions);
      byteArrayPool.put(bytesForOptions);
    }
}

private Bitmap decodeFromWrappedStreams(InputStream is,
      BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
      DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
      int requestedHeight, boolean fixBitmapToRequestedDimensions,
      DecodeCallbacks callbacks) throws IOException {

    // 省去計算壓縮比例等一系列非核心邏輯
    ...

    // 核心程式碼
    Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
    callbacks.onDecodeComplete(bitmapPool, downsampled);

    ...

    // Bimtap旋轉處理
    ...

    return rotated;
}

private static Bitmap decodeStream(InputStream is,     BitmapFactory.Options options,
      DecodeCallbacks callbacks, BitmapPool bitmapPool) throws   IOException {

    ...

    TransformationUtils.getBitmapDrawableLock().lock();
    try {
      // 核心程式碼
      result = BitmapFactory.decodeStream(is, null, options);
    } catch (IllegalArgumentException e) {
      ...
    } finally {
      TransformationUtils.getBitmapDrawableLock().unlock();
    }

    if (options.inJustDecodeBounds) {
      is.reset();
    }
    return result;
}
複製程式碼

最後是在DownSampler的decodeStream()方法中使用了BitmapFactory.decodeStream()來得到Bitmap物件。然後,我們來分析下圖片時如何顯示的,我們回到步驟19的DownSampler#decode方法,看到關注點7,這裡是將Bitmap包裝成BitmapResource物件返回,通過內部的get方法可以得到Resource物件,再回到步驟15的DecodeJob#run方法,這是使用了notifyEncodeAndRelease()方法對Resource物件進行了釋出。

DecodeJob#notifyEncodeAndRelease

private void notifyEncodeAndRelease(Resource<R> resource, DataSource     dataSource) {

    ...

    notifyComplete(result, dataSource);

    ...

}

private void notifyComplete(Resource<R> resource, DataSource     dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
}
複製程式碼

從以上EngineJob的原始碼可知,它實現了DecodeJob.CallBack這個介面。

class EngineJob<R> implements DecodeJob.Callback<R>,
    Poolable {
    ...
}
複製程式碼

EngineJob#onResourceReady

@Override
public void onResourceReady(Resource<R> resource, DataSource   dataSource) {
  this.resource = resource;
  this.dataSource = dataSource;
  MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

private static class MainThreadCallback implements Handler.Callback{

    ...

    @Override
    public boolean handleMessage(Message message) {
      EngineJob<?> job = (EngineJob<?>) message.obj;
      switch (message.what) {
        case MSG_COMPLETE:
          // 核心程式碼
          job.handleResultOnMainThread();
          break;
        ...
      }
      return true;
    }
}
複製程式碼

通過主執行緒Handler物件進行切換執行緒,然後在主執行緒呼叫了handleResultOnMainThread這個方法。

@Synthetic
void handleResultOnMainThread() {
  ...

  //noinspection ForLoopReplaceableByForEach to improve perf
  for (int i = 0, size = cbs.size(); i < size; i++) {
    ResourceCallback cb = cbs.get(i);
    if (!isInIgnoredCallbacks(cb)) {
      engineResource.acquire();
      cb.onResourceReady(engineResource, dataSource);
    }
  }

  ...
}
複製程式碼

這裡又通過一個迴圈呼叫了所有ResourceCallback的方法,讓我們回到步驟9處Engine#load方法的關注點8這行程式碼,這裡對ResourceCallback進行了註冊,在步驟8出SingleRequest#onSizeReady方法裡的engine.load中,我們看到最後一個引數,傳入的是this,可以明白,engineJob.addCallback(cb)這裡的cb的實現類就是SingleRequest。接下來,讓我們看看SingleRequest的onResourceReady方法。

SingleRequest#onResourceReady

/**
 * A callback method that should never be invoked directly.
 */
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource, DataSource   dataSource) {
  ...

  // 從Resource<Bitmap>中得到Bitmap物件
  Object received = resource.get();

  ...

  onResourceReady((Resource<R>) resource, (R) received, dataSource);
}

private void onResourceReady(Resource<R> resource, R resultDataSource dataSource) {

    ...

    try {
      ...

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        // 核心程式碼
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
}
複製程式碼

在SingleRequest#onResourceReady方法中又呼叫了target.onResourceReady(result, animation)方法,這裡的target其實就是我們在into方法中建立的那個BitmapImageViewTarget,看到ImageViewTarget類,我們並沒有發現onResourceReady方法,但是我們從它的子類ImageViewTarget中發現了onResourceReady方法,從這裡我們繼續往下看。

ImageViewTarget#onResourceReady

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {

    ...

    @Override
    public void onResourceReady(@NonNull Z resource, @Nullable       Transition<? super Z> transition) {
      if (transition == null || !transition.transition(resource, this))   {
        // 核心程式碼
        setResourceInternal(resource);
      } else {
        maybeUpdateAnimatable(resource);
      }
    }

    ...

    private void setResourceInternal(@Nullable Z resource) {
        // Order matters here. Set the resource first to make sure that the         Drawable has a valid and
        // non-null Callback before starting it.
        // 核心程式碼
        setResource(resource);
        maybeUpdateAnimatable(resource);
    }

    // 核心程式碼
    protected abstract void setResource(@Nullable Z resource);
}
複製程式碼

這裡我們在回到BitmapImageViewTarget的setResource方法中,我們終於看到Bitmap被設定到了當前的imageView上了。

public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {

    ...

    @Override
    protected void setResource(Bitmap resource) {
      view.setImageBitmap(resource);
    }
}
複製程式碼

Android 圖片載入框架

相關文章