Glide深入淺出(二)——源碼解析

我們先來看看glide最基本使用方式,如下:

Glide.with(this)
                .asDrawable()
                .load("http://i6.topit.me/6/5d/45/1131907198420455d6o.jpg")
                .apply(fitCenterTransform(this))
                .apply(placeholderOf(R.drawable.skyblue_logo_wechatfavorite_checked))
                .into(imageView);

本片文章就從這個最簡單的使用方法來對源碼進行一波解析。

一、大概介紹

Glide.java 入口文件

這裏其實就是一個入口文件,所有功能都是放在後面的類中,這裏所有需要的組件都在這個文件裏面進行統一初始化。

RequestManager.java

這個類主要是用於管理和啓動glide的所有請求,可以使用activity,fragment或者連接生命週期的事件去只能的停止,啓動和重啓請求。也可以檢索或者通過實例化一個新的對象,或者使用靜態的glide去利用構建在activity和fragment生命週期處理中。它的方法跟你的fragment和activity是同步的。

RequestBuilder.java

可以處理設置選項,並啓動負載的通用資源類型。

看前面的例子代碼中,asDrawable()最終調用的的RequestBuilder中的transition()函數,這個方法主要是用於加載對象從佔位符(placeholder)或者縮略圖(thumbnail)到真正對象加載完成的專場動畫。

load()方法中,這裏可以加載很多類型的數據對象,可以是string,uri,file,resourceId,byte[]這些。對應的編碼方式也是不一樣的。

into()方法,是真正啓動加載的地方。

二、具體源碼解析

Glide.with()函數

先看一下glide中的with函數,如下:

  public static RequestManager with(Activity activity) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(activity);
  }

這裏返回的是RequestManagerRetriever.get(),我們看一下這個函數,如下:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public RequestManager get(Activity activity) {
    if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, null);
    }
  }

(當然這個with()可以傳很多類型進來,我們只看activity的情況)
源碼中,如果該activity在後臺的時候,我們進入get(Application)中,繼續我們剛剛說的這個步驟來。
所以我們就直接看fragmentGet(activity, fm, null)函數,源碼如下:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
  RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
      android.app.Fragment parentHint) {
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          new RequestManager(glide, current.getLifecycle(), current.getRequestManagerTreeNode());
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

這個函數作用正如它的名字一樣,就是獲取fragment,其實是返回的RequestManager,那這個到底是啥?看一下getRequestManagerFragment函數,源碼如下:

  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  RequestManagerFragment getRequestManagerFragment(
      final android.app.FragmentManager fm, android.app.Fragment parentHint) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

(其中傳進來的參數fm,是我們獲取的activity的FragmentManager)
這裏其實就是就是將我們傳進來的activity轉化爲glide需要的RequestManagerFragment,RequestManagerFragment是一個自己重新以的fragment而已。

asDrawable函數

源碼如下:

  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class).transition(new DrawableTransitionOptions());
  }

添加一個DrawableTransitionOptions類型的動畫。

load函數

public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }

public RequestBuilder<TranscodeType> load(@Nullable Object model) {
    return loadGeneric(model);
  }

  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

將你要傳遞的uri,file等等傳進來,放到RequestBuilder中。

into函數

最終我們都要調用這個into了,源碼如下:

public Target<TranscodeType> into(ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      if (requestOptions.isLocked()) {
        requestOptions = requestOptions.clone();
      }
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions.optionalCenterCrop(context);
          break;
        case CENTER_INSIDE:
          requestOptions.optionalCenterInside(context);
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions.optionalFitCenter(context);
          break;
        //$CASES-OMITTED$
        default:
          // Do nothing.
      }
    }

    return into(context.buildImageViewTarget(view, transcodeClass));
  }

這裏針對ImageView的填充方式做了篩選並對應設置到requestOptions上。最終的是通過ImageView和轉碼類型(transcodeClass)創建不通過的Target(例如Bitmap對應的BitmapImageViewTarget和Drawable對應的DrawableImageViewTarget)。
到這裏,我們看到的源碼,並沒有涉及到解碼、緩存、加載等這些功能啊,是不是我們漏掉一些重要的函數?沒錯,我們的確漏掉一個很重要的函數,在into函數中,有一行代碼如下:

requestManager.track(target, request);

這個就是最核心的方法,它纔是真正觸發請求、編解碼、裝載、緩存等這些功能的。下面我詳細介紹一下。

track()函數

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

第一行,就是把target加入targets隊列(WeakHashMap)中。
看一下runRequest函數,如下:

 public void runRequest(Request request) {
    requests.add(request);//添加到內存緩存
    if (!isPaused) {
      request.begin();//開始
    } else {
      pendingRequests.add(request);//掛起請求
    }
  }

然後看一下request.begin()是如何運作的,SingleRequest繼承一個抽象類,定義了begin方法,所以我們看一下SingleRequest中begin方法。

@Override
  public void begin() {
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    // 如果model空的,那麼是不能執行的。 這裏的model就是前面提到的RequestBuilder中的model
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      // Only log at more verbose log levels if the user has set a fallback drawable, because
      // fallback Drawables indicate the user expects null models occasionally.
      int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }
    status = Status.WAITING_FOR_SIZE;
    //如果當前的View尺寸獲取到了,就會進入加載流程
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }

//如果等待和正在執行狀態,那麼當前會加載佔位符Drawable
    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }

看上面註釋,如果,當前view尺寸還沒有獲取到,我們要執行target.getSize(this)函數,我們看一下這個函數實現方法(ViewTarget實現的)。

void getSize(SizeReadyCallback cb) {
      int currentWidth = getViewWidthOrParam();
      int currentHeight = getViewHeightOrParam();
      if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
        int paddingAdjustedWidth = currentWidth == WindowManager.LayoutParams.WRAP_CONTENT
            ? currentWidth
            : currentWidth - ViewCompat.getPaddingStart(view) - ViewCompat.getPaddingEnd(view);
        int paddingAdjustedHeight = currentHeight == LayoutParams.WRAP_CONTENT
            ? currentHeight
            : currentHeight - view.getPaddingTop() - view.getPaddingBottom();
        cb.onSizeReady(paddingAdjustedWidth, paddingAdjustedHeight);
      } else {
        // We want to notify callbacks in the order they were added and we only expect one or two
        // callbacks to
        // be added a time, so a List is a reasonable choice.
        if (!cbs.contains(cb)) {
          cbs.add(cb);
        }
        if (layoutListener == null) {
        //這是尺寸大小的監聽器
          final ViewTreeObserver observer = view.getViewTreeObserver();          
          layoutListener = new SizeDeterminerLayoutListener(this);
          observer.addOnPreDrawListener(layoutListener);
        }
      }
    }

這裏加了ViewTreeObserver用來監聽view尺寸大小,我們可以看看SizeDeterminerLayoutListener做了什麼。

private static class SizeDeterminerLayoutListener implements ViewTreeObserver
        .OnPreDrawListener {
      private final WeakReference<SizeDeterminer> sizeDeterminerRef;

      public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {
        sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
      }

      @Override
      public boolean onPreDraw() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "OnGlobalLayoutListener called listener=" + this);
        }
        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
        if (sizeDeterminer != null) {
        // 通知SizeDeterminer去重新檢查尺寸,並觸發後續操作。
          sizeDeterminer.checkCurrentDimens();
        }
        return true;
      }
    }

看完這個getsize,我們再返回到上面begin中的onSizeReady(overrideWidth, overrideHeight)函數,這個纔是真正加載的函數。主要是engine發起的load操作,如下:

@Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      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);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    loadStatus = engine.load(
        glideContext,
        model,
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),
        transcodeClass,
        priority,
        requestOptions.getDiskCacheStrategy(),
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        this);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }

那麼這個engine從哪裏來的呢?它在glide初始化的時候,就創建了。

if (engine == null) {
      engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor);
    }

包括參數內存緩存和磁盤緩存。然後我們看一下load具體實現,這裏代碼比較多,所以我就挑了幾個重要的看了一下:

//給每次加載資源創建一個key,作爲唯一的標識。
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);

//通過key load緩存資源,這是一級內存緩存,是從內存緩存中直接拿出來的
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
         cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }

//這裏是二級內存緩存,使用Map<Key, WeakReference<EngineResource<?>>>保存起來的
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }

//根據key獲取緩存的任務
    EngineJob<?> current = jobs.get(key);
    if (current != null) {
      current.addCallback(cb);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

//創建任務
    EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
        useUnlimitedSourceExecutorPool);
    DecodeJob<R> decodeJob = decodeJobFactory.build(
        glideContext,
        model,
        key,
        signature,
        width,
        height,
        resourceClass,
        transcodeClass,
        priority,
        diskCacheStrategy,
        transformations,
        isTransformationRequired,
        options,
        engineJob);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    //放入線程池,執行
    engineJob.start(decodeJob);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);

還有一個問題,這個二級緩存,是誰放入二級內存中呢,我們看一下上面一級緩存的loadFromCache方法。

 private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
  }

這裏把帶有key的緩存存到activeResources中,二級緩存中loadFromActiveResources函數,也是從activeResources中拿到資源,如下:


  private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> active = null;
    //從activeResources中拿到key的cache
    WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
    if (activeRef != null) {
      active = activeRef.get();
      if (active != null) {
        active.acquire();
      } else {
        activeResources.remove(key);
      }
    }

    return active;
  }

除此之外呢?
1、一級內存緩存,glide中默認的就是我們常用的LruResourceCache。
2、我們說glide是加載imageview,我們從哪裏知道的呢,還記得使用的時候into()函數中我們傳入的是imageview,而我們源碼中的target就是我們傳進來的imageview。在track函數中

 public void track(Target<?> target) {
    targets.add(target);
  }

我們將target也就是我們的imageview加入到緩存中去了。
3、爲何要兩級內存緩存(loadFromActiveResources)。從資料上看猜測可能是一級緩存採用LRU算法進行緩存,不一定每個都能緩存到,添加二級緩存 可以保證每個都能緩存到;
4、EngineJob和DecodeJob各自職責:EngineJob充當了管理和調度者,主要負責加載和各類回調通知;DecodeJob是真正幹活的勞動者,這個類實現了Runnable接口。

那我們來看一下真正線程(DecodeJob)裏面是怎麼執行的?
我怕大家忘了engine.load的代碼,所以我先把使用EngineJob和DecodeJob的代碼貼在下面:

//創建任務
    EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
        useUnlimitedSourceExecutorPool);
    DecodeJob<R> decodeJob = decodeJobFactory.build(
        glideContext,
        model,
        key,
        signature,
        width,
        height,
        resourceClass,
        transcodeClass,
        priority,
        diskCacheStrategy,
        transformations,
        isTransformationRequired,
        options,
        engineJob);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    //放入線程池,執行
    engineJob.start(decodeJob);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);

然後我們在繼續看一下DecodeJob是如何執行的:

@Override
  public void run() {
    // This should be much more fine grained, but since Java's thread pool implementation silently
    // swallows all otherwise fatal exceptions, this will at least make it obvious to developers
    // that something is failing.
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } catch (RuntimeException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "DecodeJob threw unexpectedly"
            + ", isCancelled: " + isCancelled
            + ", stage: " + stage, e);
      }
      // When we're encoding we've already notified our callback and it isn't safe to do so again.
      if (stage != Stage.ENCODE) {
        notifyFailed();
      }
      if (!isCancelled) {
        throw e;
      }
    }
  }

然後在看裏面的runWrapped函數,如下


  private void runWrapped() {
     switch (runReason) {
      case INITIALIZE:
      //初始化,獲取下一個階段的狀態
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        //運行
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

先看getNextStage方法:

// 這裏的階段策略首先是從resource中尋找,然後再是data,,再是source
private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
      // 根據定義的緩存策略來回去下一個狀態
      // 緩存策略來之於RequestBuilder的requestOptions域
      // 如果你有自定義的策略,可以調用RequestBuilder.apply方法即可
      // 詳細的可用緩存策略請參看DiskCacheStrategy.java
        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:
        return Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

再看getNextGenerator函數,如下

// 根據Stage找到數據抓取生成器。
private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
       // 產生含有降低採樣/轉換資源數據緩存文件的DataFetcher。
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
      // 產生包含原始未修改的源數據緩存文件的DataFetcher。
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
      // 生成使用註冊的ModelLoader和加載時提供的Model獲取源數據規定的DataFetcher。
      // 根據不同的磁盤緩存策略,源數據可首先被寫入到磁盤,然後從緩存文件中加載,而不是直接返回。
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

經過上面的流程,最後就是發起實際請求的地方了,SourceGenerator.startNext()方法。

@Override
  public boolean startNext() {
    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()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

這裏的Model必須是實現了GlideModule接口的,fetcher是實現了DataFetcher接口。有興趣同學可以繼續看一下integration中的okhttp和volley工程。Glide主要採用了這兩種網絡libray來下載圖片。

數據下載完成後緩存處理SourceGenerator.onDataReady

 @Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

這裏將下載的data存到磁盤cache中,但是咋就一句dataToCache = data,其實是在cb.reschedule(),cb就是decodeJob。

public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }

這裏又有一個Callback,繼續追蹤,這裏的Callback接口是定義在DecodeJob內的,而實現是在外部的Engine中(這裏會用線程池重新啓動當前job,那爲什麼要這樣做呢?源碼中的解釋是爲了不同線程的切換,因爲下載都是借用第三方網絡庫,而實際的編解碼是在Glide自定義的線程池中進行的)

public void reschedule(DecodeJob<?> job) {
  if (isCancelled) {
    MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget();
  } else {
    sourceExecutor.execute(job);
  }
}

接下來繼續DecodeJob.runWrapped()方法。這個時候的runReason是SWITCH_TO_SOURCE_SERVICE,因此直接執行runGenerators(),這裏繼續執行SourceGenerator.startNext()方法,值得注意的dataToCache域,因爲上一次執行的時候是下載,因此再次執行的時候內存緩存已經存在,因此直接緩存數據cacheData(data):

  private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    try {
      Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
      DataCacheWriter<Object> writer =
          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      helper.getDiskCache().put(originalKey, writer);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished encoding source to cache"
            + ", key: " + originalKey
            + ", data: " + dataToCache
            + ", encoder: " + encoder
            + ", duration: " + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }

    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }

我們在會到SourceGenerator.startNext()方法,這個時候已經有了sourceCacheGenerator,那麼直接執行DataCacheGenerator.startNext()方法:

 @Override
  public boolean startNext() {
    while (modelLoaders == null || !hasNextModelLoader()) {
      sourceIdIndex++;
      if (sourceIdIndex >= cacheKeys.size()) {
        return false;
      }

      Key sourceId = cacheKeys.get(sourceIdIndex);
      Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
      cacheFile = helper.getDiskCache().get(originalKey);
      if (cacheFile != null) {
        this.sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    // 這裏會通過model尋找註冊過的ModelLoader
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData =
          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
              helper.getOptions());
              // 通過FileLoader繼續加載數據
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

這裏的ModelLoader跟之前提到過的Register的模塊加載器(ModelLoader)對應是modelLoaderRegistry域,具體執行的操作是Registry.getModelLoaders(…)方法如下:

public <Model> List<ModelLoader<Model, ?>> getModelLoaders(Model model) {
   List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
   if (result.isEmpty()) {
     throw new NoModelLoaderAvailableException(model);
   }
   return result;
 }

繼續回到DataCacheGenerator.startNext()方法,找到了ModelLoader,然後跟蹤到的是FileLoader類(FileFetcher.loadData(…)方法):

public void loadData(Priority priority, DataCallback<? super Data> callback) {
    // 讀取文件數據
     try {
       data = opener.open(file);
     } catch (FileNotFoundException e) {
       if (Log.isLoggable(TAG, Log.DEBUG)) {
         Log.d(TAG, "Failed to open file", e);
       }
    //失敗
       callback.onLoadFailed(e);
       return;
     }
  // 成功
     callback.onDataReady(data);
   }

裝載流程

代碼太多,實在有點繞暈了,所以就將一位大神的博客裏面裝載流程粘了過來。
主要線路如下:
–>DataCacheGenerator.onDataReady
–>SourceGenerator.onDataFetcherReady
–>DecodeJob.onDataFetcherReady
–>DecodeJob.decodeFromRetrievedData
–>DecodeJob.notifyEncodeAndRelease
–>DecodeJob.notifyComplete
–>EngineJob.onResourceReady

需要說明的就是在EngineJob中有一個Handler叫MAIN_THREAD_HANDLER。爲了實現在主UI中裝載資源的作用,ok繼續上邊的流程:

–>EngineJob.handleResultOnMainThread
–>SingleRequest.onResourceReady
–>ImageViewTarget.onResourceReady
–>ImageViewTarget.setResource
–>ImageView.setImageDrawable/ImageView.setImageBitmap

數據的裝載過程中有一個很重要的步驟就是decode,這個操作發生在DecodeJob.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);
      exceptions.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

這中間發生了很多轉換主要流程:

–>DecodeJob.decodeFromData
–>DecodeJob.decodeFromFetcher
–>DecodeJob.runLoadPath
–>LoadPath.load
–>LoadPath.loadWithExceptionList
–>LoadPath.decode
–>LoadPath.decodeResource
–>LoadPath.decodeResourceWithList
–>ResourceDecoder.handles
–>ResourceDecoder.decode

這裏講到了decode,那麼encode發生在什麼時候呢?直接通過Encoder接口調用發現,在數據緩存的時候纔會觸發編碼。具體調用在DiskLruCacheWrapper和DataCacheWriter中。一些值得參考的寫法例如BitmapEncoder對Bitmap的壓縮處理。

結束語

總結,glide庫很大,自己在看源碼的時候,也參考了別人對glide的理解,將glide的大概流程的主要源碼看了一下,其中可能會有很多理解不對或者不足的地方,希望大家能給指出來。
下面是是一位大神看完glide之後的結束語,個人覺得還是很不錯的。

1、總體來說代碼寫的挺漂亮的,單從使用者角度來說入手是比較容易的。
2、源碼使用了大量的工廠方法來創建對象,就像String.valueof(…)方法一樣,這也體現編碼的優雅。
3、不過想要對這個庫進行改造,可能並非易事,筆者在跟蹤代碼的過程中發現很多地方有Callback這樣的接口,來來回回查找幾次很容易就暈頭轉向了。。。
另外一個感覺難受的地方就是構造方法帶入參數太多,就拿SingleRequest來說就是12個構造參數。
4、單例的使用感覺還是有些模糊,就比如GlideContext,有些時候通過Glide.get(context).getGlideContext()獲取,而有些類中採用構造傳入。個人覺得既然讓Glide作爲單例,那麼還這樣傳入參數是不是有點多餘?代碼的編寫都是可以折中考慮,不過如果整個項目擬定好了一個規則的話,我想最好還是遵循它。另外再吐槽一下單例,很多開發人員喜歡用單例,如果你是有代碼潔癖的開發者,那麼你肯定很討厭這樣,單例很容易造成代碼的散落和結構不清晰。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章