Glide實現(xiàn)原理

一.Glide緩存機制

Glide采取的多級緩存機制,能夠較為友好地實現(xiàn)圖片航缀、動圖的加載。其主要有 內(nèi)存緩存+磁盤緩存 堰怨,當(dāng)然他們的作用也有不同芥玉,其中內(nèi)存緩存主要用于防止將重復(fù)的圖讀入內(nèi)存中,磁盤緩存則用于防止從網(wǎng)絡(luò)或者其他地方將重復(fù)下載和數(shù)據(jù)讀取备图。

默認(rèn)情況下灿巧,Glide 會在開始一個新的圖片請求之前檢查以下多級的緩存:

  1. 活動資源 (Active Resources)
  2. 內(nèi)存緩存 (Memory Cache)
  3. 資源類型(Resource Disk Cache)
  4. 原始數(shù)據(jù) (Data Disk Cache)

活動資源:如果當(dāng)前對應(yīng)的圖片資源正在使用,則這個圖片會被Glide放入活動緩存揽涮。
內(nèi)存緩存:如果圖片最近被加載過抠藕,并且當(dāng)前沒有使用這個圖片,則會被放入內(nèi)存中
資源類型: 被解碼后的圖片寫入磁盤文件中蒋困,解碼的過程可能修改了圖片的參數(shù)(如:inSampleSize盾似。inPreferredConfig)
原始數(shù)據(jù): 圖片原始數(shù)據(jù)在磁盤中的緩存(從網(wǎng)絡(luò)、文件中直接獲得的原始數(shù)據(jù))

在調(diào)用into之后家破,Glide會首先從Active Resources查找當(dāng)前是否有對應(yīng)的活躍圖片颜说,沒有則查找內(nèi)存緩存,沒有則查找資源類型汰聋,沒有則查找數(shù)據(jù)來源门粪。

1.活動資源

活動資源中是一個”引用計數(shù)"的圖片資源的弱引用集合。使用一個 Map<Key, WeakReference<EngineResource<?>>> 來存儲的烹困。

此外還有一個引用隊列ReferenceQueue<EngineResource<?>> resourceReferenceQueue;每當(dāng)向 activeResource 中添加一個 WeakReference 對象時都會將 resourceReferenceQueue 和這個 WeakReference 關(guān)聯(lián)起來玄妈,用來跟蹤這個 WeakReference 的 gc,一旦這個弱引用被 gc 掉,就會將它從 activeResource 中移除拟蜻。

ReferenceQueue 具體是在何時去判斷 WeakReference 是否被 gc 了呢?
老版本調(diào)用 MessageQueue#addIdleHandler 添加一個 MessageQueue.IdleHandler 對象绎签,Handler 會在線程空閑時調(diào)用這個方法。
新版本開啟一個線程酝锅,使用handler發(fā)送消息判斷

當(dāng)需要加載某張圖片能夠從內(nèi)存緩存中獲得的時候诡必,在圖片加載時主動將對應(yīng)圖片從內(nèi)存緩存中移除,加入到活動資源中搔扁。
這樣也可以避免因為達到內(nèi)存緩存最大值或者系統(tǒng)內(nèi)存壓力導(dǎo)致的內(nèi)存緩存清理爸舒,從而釋放掉活動資源中的圖片(recycle)。

因為同一張圖片可能在多個地方被同時使用稿蹲,每一次使用都會將引用計數(shù)+1,而當(dāng)引用計數(shù)為0時候扭勉,則表示這個圖片沒有被使用也就是沒有強引用了。這樣則會將圖片從活動資源中移除苛聘,并加入內(nèi)存緩存涂炎。

2.內(nèi)存緩存

內(nèi)存緩存默認(rèn)使用LRU(緩存淘汰算法/最近最少使用算法),當(dāng)資源從活動資源移除的時候,會加入此緩存设哗。使用圖片的時候會主動從此緩存移除唱捣,加入活動資源。LRU在Android support-v4中提供了LruCache工具類熬拒。

3.磁盤緩存

資源類型緩存的是經(jīng)過解碼后的圖片爷光,如果再使用就不需要再去進行解碼配置(BitmapFactory.Options),加快獲得圖片速度。比如原圖是一個100x100的ARGB_8888圖片澎粟,在首次使用的時候需要的是50x50的RGB_565圖片,那么Resource將50x50 RGB_565緩存下來欢瞪,再次使用此圖片的時候就可以從 Resource 獲得活烙。不需要去計算inSampleSize(縮放因子)。
原始數(shù)據(jù)緩存的則是圖像原始數(shù)據(jù)遣鼓。


Bitmap復(fù)用池

如果緩存都不存在啸盏,那么會從源地址獲得圖片(網(wǎng)絡(luò)/文件)。而在解析圖片的時候會需要可以獲得BitmapPool(復(fù)用池)骑祟,達到復(fù)用的效果回懦。復(fù)用并不能減少程序正在使用的內(nèi)存大小。Bitmap復(fù)用次企,解決的是減少頻繁申請內(nèi)存帶來的性能(抖動怯晕、碎片)問題。

BitmapPool是Glide中的Bitmap復(fù)用池,同樣適用LRU來進行管理缸棵。在每次解析一張圖片為Bitmap的時候(磁盤緩存舟茶、網(wǎng)絡(luò)/文件)會從其BitmapPool中查找一個可被復(fù)用的Bitmap。當(dāng)一個Bitmap從內(nèi)存緩存 被動 的被移除(內(nèi)存緊張、達到maxSize)的時候并不會被recycle吧凉。而是加入這個BitmapPool隧出,只有從這個BitmapPool 被動的被移除的時候,Bitmap的內(nèi)存才會真正被recycle釋放。

Bitmap復(fù)用方式為在解析的時候設(shè)置Options的inBitmap屬性阀捅。

  1. Bitmap的inMutable需要為true胀瞪。
  2. Android 4.4及以上只需要被復(fù)用的Bitmap的內(nèi)存必須大于等于需要新獲得Bitmap的內(nèi)存,則允許復(fù)用此Bitmap饲鄙。
  3. 4.4以下(3.0以上)則被復(fù)用的Bitmap與使用復(fù)用的Bitmap必須寬凄诞、高相等并且使用復(fù)用的Bitmap解碼時設(shè)置的inSampleSize為1,才允許復(fù)用傍妒。

二.Glide生命周期管理

Glide在Glide.with(context)中就實現(xiàn)了生命周期管理幔摸,with根據(jù)傳入的參數(shù)有不同的實現(xiàn)。

  //傳入一個Context
  public static RequestManager with(@NonNull Context context)
  //傳入一個activity
  public static RequestManager with(@NonNull Activity activity)
  //傳入一個FragmentActivity
  public static RequestManager with(@NonNull FragmentActivity activity)
  //傳入一個Fragment
  public static RequestManager with(@NonNull Fragment fragment)
  //傳入一個View
  public static RequestManager with(@NonNull View view)

雖然有這么多類型颤练,但其實可以分為兩類的既忆。

  • 傳入一個ApplicationContext,Glide的生命周期就相當(dāng)于綁定了整個應(yīng)用嗦玖,只要應(yīng)用不退出患雇,任何時候都能夠加載,也可以理解為不對Glide生命周期進行管理宇挫。

  • 傳入activity苛吱、FragmentActivity 、Fragment 及View 器瘪,這樣就會創(chuàng)建一個看不見的fragment翠储,Glide的生命周期就隨著該Fragment的變化而變化。

當(dāng)傳入?yún)?shù)為ApplicationContext時橡疼,代碼實現(xiàn)如下援所。

  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
  //由于傳入?yún)?shù)是ApplicationContext,所以最終調(diào)用getApplicationManager方法欣除。
  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)) {
      if (context instanceof FragmentActivity) {
        //判斷context類型是不是FragmentActivity
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        //判斷context類型是不是Activity
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        //判斷context類型是不是ContextWrapper
        return get(((ContextWrapper) context).getBaseContext());
      }
    }
    //context類型屬于ApplicationContext
    return getApplicationManager(context);
  }

由于傳入?yún)?shù)是ApplicationContext住拭,所以最終調(diào)用getApplicationManager方法。

  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(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }

這里就直接創(chuàng)建一個ApplicationLifecycle來管理生命周期历帚,但ApplicationLifecycle并不受控制滔岳,所以就無法對Glide生命周期進行管理。
?以傳入?yún)?shù)類型為Activity為例挽牢,代碼實現(xiàn)如下谱煤。

  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
  public RequestManager get(@NonNull Activity activity) {
    //如果在子線程,則不對Glide生命周期就那些管理
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      //拿到FragmentManager對象
      android.app.FragmentManager fm = activity.getFragmentManager();
      //獲取fragment對象卓研,并返回一個RequestManager 對象
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

如果當(dāng)前是在子線程趴俘,則不需要對Glide生命周期進行管理睹簇,否則將通過fragmentGet方法創(chuàng)建一個fragment。

  private RequestManager fragmentGet(@NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    //創(chuàng)建一個fragment對象
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      //防止Glide對象被異沉壬粒回收
      Glide glide = Glide.get(context);
      //創(chuàng)建一個RequestManager對象
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

在該方法中太惠,通過getRequestManagerFragment來獲得一個Fragment對象。然后拿到該Fragment對應(yīng)的RequestManager 對象疲憋,如果該對象為null則創(chuàng)建一個RequestManager對象并將fragment中的ActivityFragmentLifecycle對象傳遞給RequestManager凿渊。先來看getRequestManagerFragment方法的實現(xiàn)。

  private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    //查找tag為FRAGMENT_TAG的fragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      //從HashMap中取出fm
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        //創(chuàng)建fragment對象
        current = new RequestManagerFragment();
        //當(dāng)fragment嵌套fragment時才會使用缚柳,否則parentHint是null
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          //開始執(zhí)行請求
          current.getGlideLifecycle().onStart();
        }
        //將fm添加到HashMap中埃脏,防止fragment的重復(fù)創(chuàng)建
        pendingRequestManagerFragments.put(fm, current);
        //添加fragment
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        //從HashMap集合從移除fm
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

從fm中去查找tag為FRAGMENT_TAG的fragment是否存在,如果不存在就從pendingRequestManagerFragments這個HashMap中去取秋忙,如果沒有就創(chuàng)建一個fragment彩掐,添加到pendingRequestManagerFragments并且將該fragment綁定到activity,綁定成功后則從pendingRequestManagerFragments移除fragment灰追。這里的pendingRequestManagerFragments主要是防止fragment重復(fù)創(chuàng)建(Glide生命周期管理)堵幽,因為每個activity必須對應(yīng)一個唯一的fragment。來看一下這個fragment的實現(xiàn)RequestManagerFragment弹澎。

public class RequestManagerFragment extends Fragment {
  private final ActivityFragmentLifecycle lifecycle;
  public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
  ...
  @NonNull
  ActivityFragmentLifecycle getGlideLifecycle() {
    return lifecycle;
  }
  ...
  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }
  ...
}

再回到fragmentGet方法朴下,fragment創(chuàng)建成功后,在創(chuàng)建RequestManager時會傳入fragment中的ActivityFragmentLifecycle苦蒿,再來看RequestManager的實現(xiàn)殴胧。

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
  private final Runnable addSelfToLifecycle = new Runnable() {
    @Override
    public void run() {
      lifecycle.addListener(RequestManager.this);
    }
  };
  public RequestManager(
      @NonNull Glide glide, @NonNull Lifecycle lifecycle,
      @NonNull RequestManagerTreeNode treeNode, @NonNull Context context) {
    this(
        glide,
        lifecycle,
        treeNode,
        new RequestTracker(),
        glide.getConnectivityMonitorFactory(),
        context);
  }

  // Our usage is safe here.
  @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
  RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.context = context;
    ...
    if (Util.isOnBackgroundThread()) {
      //當(dāng)在子線程時通過Handler將當(dāng)前對象注冊到ActivityFragmentLifecycle
      mainHandler.post(addSelfToLifecycle);
    } else {
      //將當(dāng)前對象注冊到ActivityFragmentLifecycle
      lifecycle.addListener(this);
    }
    //網(wǎng)絡(luò)變化監(jiān)聽
    lifecycle.addListener(connectivityMonitor);
    ...
  }
  //開始加載
  @Override
  public synchronized void onStart() {
    resumeRequests();
    //如果有動畫則開始動畫
    targetTracker.onStart();
  }
  //停止加載
  @Override
  public synchronized void onStop() {
    pauseRequests();
    //如果有動畫則動畫停止
    targetTracker.onStop();
  }
  //銷毀
  @Override
  public synchronized void onDestroy() {
    //如果有動畫則動畫結(jié)束并銷毀
    targetTracker.onDestroy();
    ...
  }
  //開始請求數(shù)據(jù)
  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
 ...
}

可以看見在RequestManager的構(gòu)造函數(shù)將RequestManager注冊到ActivityFragmentLifecycle中,再來看看ActivityFragmentLifecycle的實現(xiàn)佩迟。

class ActivityFragmentLifecycle implements Lifecycle {
  private final Set<LifecycleListener> lifecycleListeners =
      Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
  private boolean isStarted;
  private boolean isDestroyed;
  @Override
  public void addListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.add(listener);

    if (isDestroyed) {
      listener.onDestroy();
    } else if (isStarted) {
      listener.onStart();
    } else {
      listener.onStop();
    }
  }

  @Override
  public void removeListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.remove(listener);
  }
  //每個RequestManager對應(yīng)一個LifecycleListener
  void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }
  //每個RequestManager對應(yīng)一個LifecycleListener
  void onStop() {
    isStarted = false;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStop();
    }
  }
  //每個RequestManager對應(yīng)一個LifecycleListener
  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }
}

由于ActivityFragmentLifecycle對象是在fragment中創(chuàng)建并且它的onStart团滥、onStop、onDestory方法與fragment一一對應(yīng)报强,這樣就將RequestManager的生命周期就與fragment關(guān)聯(lián)起來了惫撰,也就與當(dāng)前activity關(guān)聯(lián)起來√衫裕總體流程如下:

在這里插入圖片描述

當(dāng)fragment生命周期發(fā)生變化時,通過ActivityFragmentLifecycle將變化告訴給RequestManager與DefaultConnectivityMonitor扼雏。而RequestManager又將此變化告訴給ImageViewTarget坚嗜。
?至于傳入?yún)?shù)為其他類型的實現(xiàn)基本上與activity的類似,就不在敘述诗充。

相關(guān)文章:
glide實現(xiàn)原理解析 https://blog.csdn.net/hxl517116279/article/details/99639520
glide原理解析 http://www.reibang.com/p/bce65d04d831
Android glide原理解析 https://blog.csdn.net/qq_15827013/article/details/97893860

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苍蔬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蝴蜓,更是在濱河造成了極大的恐慌碟绑,老刑警劉巖俺猿,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異格仲,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門撑碴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝙场,“玉大人,你說我怎么就攤上這事侮东∪” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵悄雅,是天一觀的道長驱敲。 經(jīng)常有香客問我,道長宽闲,這世上最難降的妖魔是什么众眨? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮便锨,結(jié)果婚禮上围辙,老公的妹妹穿的比我還像新娘。我一直安慰自己放案,他們只是感情好姚建,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吱殉,像睡著了一般掸冤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上友雳,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天稿湿,我揣著相機與錄音,去河邊找鬼押赊。 笑死饺藤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的流礁。 我是一名探鬼主播涕俗,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼神帅!你這毒婦竟也來了再姑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤找御,失蹤者是張志新(化名)和其女友劉穎元镀,沒想到半個月后绍填,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡栖疑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年讨永,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔽挠。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡住闯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澳淑,到底是詐尸還是另有隱情比原,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布杠巡,位于F島的核電站量窘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏氢拥。R本人自食惡果不足惜蚌铜,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫩海。 院中可真熱鬧冬殃,春花似錦、人聲如沸叁怪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奕谭。三九已至涣觉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間血柳,已是汗流浹背官册。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留难捌,地道東北人膝宁。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像根吁,于是被迫代替她去往敵國和親昆汹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容