錦囊篇|一文摸懂Glide

前言

和以前的文章會有必定的不一樣,這主要是由於Glide自身的源碼量致使的問題,由於我是最後寫的前言,你會發如今文章剛開始時會代碼複製的比較徹底,後面就比較零散,並且一部分我直接用本身話去進行了表述。若是真的要看懂,建議仍是對着Glide的源碼進行查看,這樣會幫助你更好去理解GLide的它的實現流程。java

使用方法

(1)資源引入android

repositories {
  mavenCentral()
  google()
}

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.11.0'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
複製代碼

(2)方法使用git

// 實現單張圖片加載
@Override public void onCreate(Bundle savedInstanceState) {
  ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
  // 若是是最新版的系統是不容許http來進行請求的
  // 去百度隨便拿一張圖片的地址來改一下就行了
  Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}

// 實現圖片列表加載
@Override public View getView(int position, View recycled, ViewGroup container) {
  final ImageView myImageView;
  if (recycled == null) {
    myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
  } else {
    myImageView = (ImageView) recycled;
  }

  String url = myUrls.get(position);

  Glide
    .with(myFragment)
    .load(url)
    .centerCrop()
    .placeholder(R.drawable.loading_spinner)
    .into(myImageView);

  return myImageView;
}
複製代碼

源碼分析

在源碼使用中,其實基礎的在上面的使用方法中已經講述到了,一共能夠分爲三個步驟:github

  1. with(Context)
  2. load(ImageURL)
  3. into(ImageView)

咱們的分析流程也將圍繞這三個函數來進行展開。算法

with(Context)

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

  @NonNull
  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }

  @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

  @NonNull
  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getContext()).get(fragment);
  }

  @SuppressWarnings("deprecation")
  @Deprecated
  @NonNull
  public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }

  @NonNull
  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }
複製代碼

悄咪咪數了數,Oh my Gosh!!! 居然高達有6個重載方法。不過呢想必你也發現這些方法都直接調用了getRetriever().get()的方法,那目的就很是明顯了,咱們進到這個方法去一探究竟了。緩存

@NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    return Glide.get(context).getRequestManagerRetriever(); // 1 -->
  }
  
  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      // 使用了context.getApplicationContext()是爲了防止內存泄漏的發生
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          // 對glide總體地進行初始化
          // 其中就包含了對RequestManagerRetriever的初始化流程
          // 代碼量比較大就不作介紹了
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }
    return glide;
  }
複製代碼

既然是一堆的初始化操做,最後咱們的目標又是RequestManagerRetriever這個類,那天然是有必要對這個類進行探究的。markdown

public class RequestManagerRetriever implements Handler.Callback {

  public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
    this.factory = factory != null ? factory : DEFAULT_FACTORY;
    handler = new Handler(Looper.getMainLooper(), this /* Callback */);
  }
  
  private static final RequestManagerFactory DEFAULT_FACTORY =
      new RequestManagerFactory() {
        @NonNull
        @Override
        public RequestManager build( @NonNull Glide glide, @NonNull Lifecycle lifecycle, @NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
          return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
        }
      };
  
  // getRetriever()的get()方法
  // 對標上面的6個重載方法的調用,這裏只取其一
  @NonNull
  public RequestManager get(@NonNull FragmentActivity activity) {
    // 若是當前的線程是在後臺線程中,則進入
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext()); // 1-->
    } else {
      assertNotDestroyed(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); // 2 -->
    }
  }
}
複製代碼

(1)經過構造函數咱們可以猜想的內容是通訊的工具是Handler,而Looper使用的是MainLooper也就是主線程的,那說明最後異步通訊也就直接扔到主線程完成了。網絡

(2)經過get()函數,能夠發現其實分爲兩個部分。一是再一層的get()方法;二是supportFragmentGet()或者是FragmentGet()方法。app

他們最後的任務都是爲了建立出一個RequestManager,可是咱們得關注一下它的建立方式。異步

get()

對於這個方法而言就是對context的斷定是否爲Application,而後給出相應的結果。

(1)不是Application且是在主線程中時

if (Util.isOnMainThread() && !(context instanceof Application)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }
複製代碼

而他們的歸宿,最後仍是回到咱們上方的重載方法。

(2)是Application或是再也不主線程時

getApplicationManager(context); // 1 -->
  
  // 使用DCL的方式來建立了單例
  @NonNull
  private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(), // 2
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }
    return applicationManager;
  }
複製代碼

經過工廠來自建了一個RequestManager,註釋2處他直接使用了ApplicationLifecycle緣由是由於某些狀況下會接受不到生命週期的事件,這裏是作的強制性的操做是爲了生命週期變化時可以正常相應。

FragmentGet()

瞟了一下,這是要一個廢棄的方法了,可是和supportFragmentGet()的方法相比其實也差不太多。

private RequestManager fragmentGet( @NonNull Context context, @NonNull android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) {
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); // 1 -->
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      // 將requestManager和Fragment相掛鉤
      // 用以完成生命週期的監聽
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
  
// 1 -->
// 獲取對應的Fragment
private RequestManagerFragment getRequestManagerFragment( @NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) {
    // 尋找的方式是經過設置的TAG
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    // 先去等待隊列中進行查詢
    // 這一步的做用是防止Fragment的重複添加
    // 由於添加的Fragment的所謂的生命週期有必定的延時性
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      // 若是等待隊列建立一個新的TAG
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }
複製代碼

總結

  1. 初始化Glide的同時在內部完成了RequestManagerRetriever的建立
  2. 獲取到的RequestManagerRetriever調用get()方法,獲取到RequestManager,獲取方式分爲如下兩種:
    • ContextApplication時, 經過getApplicationManager()方法建立RequestManager完成,將生命週期的監聽與Application強制綁定用於接收。
    • Context不爲Application時, 經過supportFragmentGet()方法建立RequestManager完成,生命週期的監聽是與Fragment進行綁定實現。

建立對應TAG的一個很是直接的好處,咱們的圖片像RecyclerView會放置中不容易出現錯位的現象。

load(ImageURL)

整體來講上面的就是一個初始化和必要變量獲取的操做,那接下從函數方法來看咱們彷佛是要去得到的圖片了呢。

public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string); // 1 -->
  }
複製代碼

註釋1處,咱們經過觀察能夠知道他最後會選擇將獲取的數據轉化變成一個Drawable的類而後再在咱們對應的ImageView上來進行顯示。那咱們就對asDrawable()先進行一段源碼的分析。

// asDrawable()不斷深刻能發現調用到的函數
// 是完成一個類RequestBuilder的對象建立
public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) { // Drawable.class
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
複製代碼

那接下來的問題就要進入到這個類中,由於在前面咱們的探索其實算是並無什麼收穫的,而若是隻是建立一個類顯然是不會讓這句話顯得這麼重要,那關鍵點必定會出如今這個類的構造中了。

protected RequestBuilder( @NonNull Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context) {
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();

    initRequestListeners(requestManager.getDefaultRequestListeners()); // 1 -->
    // 這段代碼過長,不作展現,它的主要任務就是一些策略開關
    // 各類選項的開啓裝置,好比錯誤提示、優先級、磁盤緩存策略、固定寬高等等
    apply(requestManager.getDefaultRequestOptions()); 
  }
  
  // 1-->
  // 從某種意義上講就是對生命週期的監聽
  private void initRequestListeners(List<RequestListener<Object>> requestListeners) {
    for (RequestListener<Object> listener : requestListeners) {
      addListener((RequestListener<TranscodeType>) listener);
    }
  }
複製代碼

而若是回到load(string)方法。

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

他最後的差事也就是將URI的值放到了model這個變量中,那整個load()函數做用其實最後只是建立了一個RequestBuilder的事例,那最後的獲取和加載工做確定是在into()函數中才進行了操做的。

into(ImageView)

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // 經過scaleType,對圖片的屬性進行設定
      switch (view.getScaleType()) {
        case CENTER_CROP:
        // 。。。。。。
        default:
        // Do nothing.
      }
    }
    // 正式將圖片數據塞入
    // 1 -->
    return into(
        // 2 -->
        glideContext.buildImageViewTarget(view, transcodeClass), // 深度調用能夠知道也就是將View進行了賦值 
        /*targetListener=*/ null,
        requestOptions,
        // 可以看到線程池的影子,後面的圖片的獲取和處理咱們猜想就是經過池來進行處理
        Executors.mainThreadExecutor()); 
  }
  
  // 1 --> 將數據塞入
  private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {
    // 正常狀況構建SingleRequest的請求
    // 由於thumbnail通常須要額外的需求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    // 當前請求和最新的同樣
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      // 若是請求完成,從新啓動會保證結果送達並觸動目標
      // 若是請求失敗,會給出機會去再次完成
      // 若是請求正在運行,不會打斷
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target); 
    target.setRequest(request); // 置換最新的請求
    requestManager.track(target, request); // 3 -->

    return target;
  }
複製代碼

下面的內容將主要對上述代碼中的註釋2和註釋3進行講解。

glideContext.buildImageViewTarget(view, transcodeClass)

從字面意思,相比你也可以進行理解了,就是要構建一個存放的目標。

@NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass); // 1 -->
  }
  
  // 1-->
  // 根據不一樣的數據類型選擇存儲是以Drawable仍是Bitmap構建
  public <Z> ViewTarget<ImageView, Z> buildTarget( @NonNull ImageView view, @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      // 以Bitmap構建
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view); // 2-->
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      // 以Drawable構建
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view); // 2 -->
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }

// 2-->
// 兩個註釋最後深度調用以後都會調用到這段代碼
// 若是單看這段代碼的時候其實
public ViewTarget(@NonNull T view) {
    this.view = Preconditions.checkNotNull(view);
    // 若是隻看這個構造函數,確實沒什麼東西
    // 不行你能夠直接看註釋3的代碼處
    sizeDeterminer = new SizeDeterminer(view); // 3 -->
  }
// 很是簡單的就只是對view進行了一個賦值操做
SizeDeterminer(@NonNull View view) {
      this.view = view;
    }
複製代碼

那若是就只有上面那麼一點不就完了??其實並不,若是你觀察了一下 DrawableImageViewTargetBitmapImageViewTarget的其餘方法,能發現這樣的一個特徵。

protected void setResource(Bitmap resource) {
    view.setImageBitmap(resource);
  }
  
protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }
複製代碼

沒錯!! 賦值操做,這個操做說明最後其實的結束點確定是在這裏的,而調用他的函數最後也就是onResourceReady()這個方法,也就意味着圖片獲取成功了,不過呢這個請求完成確定是和數據的獲取相互關聯的,也就是下面部分的內容了。

requestManager.track(target, request)

// 以同步的方式完成數據的請求
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target); // 對當前的目標的生命週期有一個追蹤
    requestTracker.runRequest(request); // 2 --> 執行操做正式開啓
  }
// 2 -->
public void runRequest(@NonNull Request request) {
    requests.add(request);
    // 會對當前的全部請求作一個判斷處理
    // 會根據當前的狀態肯定是否要進行數據加載的操做
    // 通常來講對應的就是生命週期
    if (!isPaused) {
      request.begin();
    } else {
      request.clear();
      pendingRequests.add(request);
    }
  }
複製代碼

那上面一段代碼說明咱們正常運行的時候,網絡傳輸的操做確定是已經在正常運行了的,而其實正常沒有設置時調用的會是SingleRequest的類,很少逼逼,瞅瞅它的begin()方法有什麼特殊之處了。

public void begin() {
    synchronized (requestLock) {
      // 。。。。。
    
      // 若是正在運行就拋出異常
      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }

      // 從緩存中直接拿出數據
      if (status == Status.COMPLETE) {
        onResourceReady(resource, DataSource.MEMORY_CACHE);
        return;
      }
      // ==============重中之重==============
      // 由於在上述文章中講到過了圖片的大小問題
      // 在Glide中這裏就是給出解決方案的地方,兩種方案:
      // 1. 給出了固定長寬
      // 2. 沒有設置時
      status = Status.WAITING_FOR_SIZE;
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        target.getSize(this);
      }
      // 使用一個佔位符先頂替
      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
    }
  }
複製代碼

那接下來要講述的內容就應該是他的一些大小設置問題了,Glide究竟是用什麼樣的方式完成大小的設置的呢?

onSizeReady(overrideWidth, overrideHeight)

public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      // .....
      status = Status.RUNNING;
      // 對長寬從新進行預估
      float sizeMultiplier = requestOptions.getSizeMultiplier();
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

      loadStatus =
          engine.load(各類參數);
      // .....
    }
  }
複製代碼

經過觀察對onSizeReady()函數發現,他使用的方案其實又是一個名叫作engine.load()的方式。

public <R> LoadStatus load(各類參數) {
    
    EngineResource<?> memoryResource;
    synchronized (this) {
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

      if (memoryResource == null) {
        return waitForExistingOrStartNewJob(各類參數);
      }
    }
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }
複製代碼

上述是我省略事後的代碼,他表達意思其實很是之簡單:

(1)內存裏有數據,你就從我內存裏拿。

(2)內存裏沒數據,那你就本身建立一個。

這樣想來,問題又要往下繼續延伸了,正常來講咱們沒有數據啊,那就要去調用這個waitForExistingOrStartNewJob()方法,來完成圖片數據的獲取了。

private <R> LoadStatus waitForExistingOrStartNewJob(各類參數) {
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      return new LoadStatus(cb, current);
    }
    
    EngineJob<R> engineJob =
        engineJobFactory.build(各類參數);

    DecodeJob<R> decodeJob =
        decodeJobFactory.build(各類參數);

    jobs.put(key, engineJob); // 對當前驅動工做進行緩存操做

    engineJob.addCallback(cb, callbackExecutor);
    engineJob.start(decodeJob); // 開啓圖片獲取工做

    return new LoadStatus(cb, engineJob);
}
複製代碼

可以注意到有出現兩個新的類EngineJobDecodeJob,轉換成中文去理解就是工做驅動器和解碼工做,而且EngineJob內部與線程池搭噶,最後確定用於完成最後的圖片獲取工做,而DecodeJob做爲被運行的工做,處理邏輯就應該在其之中。

public void run() {
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      // .....
      runWrapped(); // 1 -->
    } catch (Exception e) {
      // .....
    } finally {
      // .....
    }
  }
// 1 -->
private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        // 獲取 Stage.INITIALIZE 的下一步操做,也就是選擇三種方案
        // 1. 資源緩存:ResourceCacheGenerator
        // 2. 數據緩存:DataCacheGenerator
        // 3. 網絡資源:SourceGenerator
        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);
    }
  }
複製代碼

其實上述內容中已經開始講述到咱們常見的三大緩存了,也就是網絡緩存、磁盤緩存和內存緩存,這段代碼中其實已經開始涉及網絡緩存和磁盤緩存了。

SourceGenerator:關於網絡緩存

private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled
        && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) { // 1 -->
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();
      // 只對Source這個枚舉類型相應
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    // 已完成就通知失敗
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }
  }
// 1-->
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()) {
      // 從映射表找出可以對應上的圖片類型的ModelLoader
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // 完成數據的加載
        startNextLoad(loadData);
      }
    }
    return started;
  }
複製代碼

針對註釋1進行探討,這裏的話,咱們就儘可能不放代碼查看了,由於看了這麼多代碼,你確定也累了。其實你可以猜想到它的下一步是網絡請求,那若是獲取成功了,是要直接進行圖片數據的顯示嗎?那三次緩存應該在什麼時機進行操做呢?由於代碼量的緣由,這裏咱們用圖來展現流程。

從圖中能夠知道,其實從網絡獲取的資源最後仍是要被放到磁盤中進行緩存的,而磁盤緩存成功以後,接下來要乾的事情就是要去通知View把獲取的數據進行解碼。這裏你是否有必定的疑問了?

你沒有聽錯,就是解碼,若是你去各個官方查看圖片的時候,不少給的都是後期從新被編碼過的數據,自己數據其實並不可以在直接運行的,而解碼就是讓圖片正式可視化的必經之路,也就是DecodeJob的本質工做了,對應的函數就是decodeFromRetrievedData()方法,把磁盤中拷貝出來的數據要進行解碼操做。

private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    // 將網絡獲取的數據進行解碼操做
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource); // 1 -->
    } catch (GlideException e) {
    }
    // 解碼完成,發出通知
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource); // 2-->
    } else {
      runGenerators();
    }
  }
複製代碼

你可以發現我在這段代碼中打了兩個註釋,其實對應的就是兩大操做解碼完成,通知能夠顯示了。

decodeFromData(currentFetcher, currentData, currentDataSource);

下面會給出一段很長的深度調用代碼。

1)resource = decodeFromData(currentFetcher, currentData, currentDataSource);
(2)Resource<R> result = decodeFromFetcher(data, dataSource);
(3return path.load(
          rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
(4return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
(5)result = path.decode(rewinder, width, height, options, decodeCallback);
(6public Resource<Transcode> decode( DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    // 這裏會有不少轉化的方法,通常來講對圖片最多見的就是轉成Bitmap
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    return transcoder.transcode(transformed, options);
  }
複製代碼

decode()這個函數其實作了三件事:

  1. decodeResource 將原始數據轉換成咱們原始圖片的過程;
  2. callback.onResourceDecoded()是當獲得了原始圖片以後對圖片繼續處理過程;
  3. transcoder.transcode()會使用BitmapDrawableTranscoder進行再包裝

decodeResource中能夠轉化的部分解碼器

那這個時候你就正式拿到了一張圖片了,那下一步還須要幹嗎???顯示啊!!!廢了這麼大週摺,還不趕忙拿去顯示,不是作了一大堆無用功嘛?

notifyEncodeAndRelease(resource, currentDataSource);

只看少許代碼來完成這項工做

1)notifyComplete(result, dataSource);
(2private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }
複製代碼

你是否有注意到這樣的問題,onResourceReady()是否是有點眼熟,在很上面的glideContext.buildImageViewTarget(view, transcodeClass),也就是構建放置的目標中咱們就已經講到過這個方法了,最後會經過一個set()的方法來將數據進行放置。

那基本上來講,上面就是一個比較詳細的Glide的源碼分析,由於代碼是在太多了,因此我這裏刪去了不少。

一些思考

其實你會發現我並無正式講完三級緩存,還差一個內存緩存沒講不是?

其實這是一個很早就被用到的方法,他對應的位置就在SingleRequest被調用了OnSizeReady()方法的時候有個engine.load(),裏面就包含了第三級緩存內存緩存,裏面對應的是這樣的一段代碼memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);以及他深度調用後的EngineResource<?> active = loadFromActiveResources(key);從活躍的的資源數據中進行尋找。一樣是一個很是簡單的實現手法。

final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
複製代碼

就是經過一個Map對數據進行了保存,這樣從複用的角度上來看就被比較好的繼承了。

但若是不是活躍資源數據呢?

不要着急,還有一些數據還會在cache的變量中被保存着,這個方法裏就是用了咱們常常會說起到的LRUCache的一個淘汰算法,這裏的詳細請查看個人另一篇文章:Glide都在用的LruCache,你學會了嗎?

相關文章
相關標籤/搜索