跟著源碼學設計:Glide 框架及源碼解析(一)

前言
近期研究了一下Glide的圖片加載框架,在這里和大家分享一下。由于代碼研讀有限,難免有錯誤的地方,了解的童鞋還望指正。如果這篇文章對大家學習Glide有幫助,還望大家多多轉載。學習小組QQ群: 193765960。

本篇是Glide框架及源碼解析的第一篇,更多文章敬請關注后續文章。版權歸作者所有,如有轉發,請注明文章出處:http://www.lxweimin.com/u/d43d948bef39

相關文章:

跟著源碼學設計:Glide框架及源碼解析(一)
跟著源碼學設計:Glide框架及源碼解析(二)
跟著源碼學設計:Glide框架及源碼解析(三)
跟著源碼學設計:Glide框架及源碼解析(四)
跟著源碼學設計:Glide框架及源碼解析(五)

1. Glide 簡介

Glide是一個性能優良的第三方網絡圖片加載框架,在節省內存和快速流暢加載方面具有較好體現。究其內部機制,發現其優良性能得益于以下幾點:

  • 與使用環境生命周期相綁定:RequestManagerFragment & SupportRequestManagerFragment
  • 內存的三級緩存池:LruMemoryResources, ActiveResources, BitmapPool
  • 內存復用機制:BitmapPool

更多的關于Glide的介紹網上資料很多,在這里不再贅述,下文中將針對Glide的內部機制展開說明。

1.1 為什么要綁定生命周期(有什么優點)?

  • 可以實現網絡請求根據生命周期而暫停、執行、恢復、釋放等
  • 可以實現資源比如圖片的自動釋放
  • 降低了內存的壓力
  • 降低了內存泄漏的風險

1.2 綁定原理

  • 原理的知識基礎:FragmentManager(簡稱fm)中的所有fragment(通過fm.add()添加進來)都與fm所處的context生命周期綁定。例如:我們的activity中的fragment的生命周期自動通過activity的fm和activity的生命周期綁定。
  • Glide定義了RequestManagerFragment 和 SupportRequestManagerFragment兩種fragment。該兩類Fragment不具有任何的界面和其他功能,通過入口傳入的context獲取到的fm綁定生命周期到context上。
  • Glide內部的生命周期綁定機制進一步通過基于xxxxRequestManagerFragment 的生命周期接口的回調實現。

2. Glide生命周期回調示意圖

Glide生命周期回調示意圖

3. Glide生命周期綁定機制類圖

Glide生命周期綁定機制類圖

3.1 RequestManagerRetriever(單例模式)

  • 根據context獲取fm;
  • 獲取xxxxRequestManagerFragment實例
  • 獲取RequestManager實例
  • 相互綁定xxxxRequestManagerFragment和RequestManager
  • xxxxRequestManagerFragment綁定到context生命周期
 /**
  * 獲取RequestManager實例
  */
  @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);
          
          //獲取fm
          android.app.FragmentManager fm = activity.getFragmentManager();
          return fragmentGet(activity, fm);
      }
  }
  
 /**
  * Note: new RequestManager(context,lifecycle,RequestManagerTreeNode)中,綁定了lifecycle;
  * current.setRequestManager(requestManager):requestManager綁定到fragment
  */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  RequestManager fragmentGet(Context context, FragmentManager fm) {
      //獲取fragment實例:與fm綁定生命周期
      RequestManagerFragment current = getRequestManagerFragment(fm);
      //獲取之前綁定的RequestManager
      RequestManager requestManager = current.getRequestManager();
      if (requestManager == null) {
          requestManager = new RequestManager(context, current.getLifecycle(),current.getRequestManagerTreeNode());
          
          //requestManager綁定到fragment
          current.setRequestManager(requestManager);
      }
      return requestManager;
  }
   
 /**
  * Note: fm.beginTransaction().add(current, FRAGMENT_TAG): fragment綁定context生命周期
  */
  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
      RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
      if (current == null) {
          current = pendingRequestManagerFragments.get(fm);
          if (current == null) {
              current = new RequestManagerFragment();
              pendingRequestManagerFragments.put(fm, current);
              //將fragment與fm綁定
              fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
              handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
          }
      }
      return current;
  }

3.2 xxxxRequestManagerFragment(綁定context的生命周期)

  • 內部綁定一個RequestManager對象;
  • 內部綁定一個ActivityFragmentLifecycle對象
  • 在生命周期的回調中調用ActivityFragmentLifecycle的相應生命周期方法
  • RequestManager通過注冊到ActivityFragmentLifecycle的lifecycleListeners集合實現生命周期綁定
private final ActivityFragmentLifecycle lifecycle;
private RequestManager requestManager;
 
public RequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
}
 
RequestManagerFragment(ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
}
  
/**
* Sets the current {@link com.bumptech.glide.RequestManager}.
* @param requestManager The request manager to use.
*/
public void setRequestManager(RequestManager requestManager) {
    this.requestManager = requestManager;
}
 
/**
* 生命周期:Glide通過xxxxRequestManagerFragment的生命周期回調實現內部生命周期回調
*/
@Override
public void onStart() {
    super.onStart();
    lifecycle.onStart();
}
 
@Override
public void onStop() {
    super.onStop();
    lifecycle.onStop();
}
 
@Override
public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
}

3.3 ActivityFragmentLifecycle

  • 管理同一個xxxxRequestManagerFragment分支下的所有LifecycleListener(具有生命周期)
  • 被xxxxRequestManagerFragment生命周期接口回調
  • 遍歷回調所有LifecycleListener生命周期接口
/**
 * A {@link com.bumptech.glide.manager.Lifecycle} implementation for tracking and notifying listeners of
 * {@link android.app.Fragment} and {@link android.app.Activity} lifecycle events.
 */
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(LifecycleListener listener) {
        lifecycleListeners.add(listener);

        if (isDestroyed) {
            listener.onDestroy();
        } else if (isStarted) {
            listener.onStart();
        } else {
            listener.onStop();
        }
    }
  
    void onStart() {
        isStarted = true;
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onStart();
        }
    }
  
    void onStop() {
        isStarted = false;
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onStop();
        }
    }
  
    void onDestroy() {
        isDestroyed = true;
        for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
            lifecycleListener.onDestroy();
        }
    }
}

3.4 RequestManager

  • RequestManager被綁定于xxxxRequestManagerFragment
  • RequestManager實現了LifecycleListener接口
  • RequestManager注冊給xxxxRequestManagerFragment的ActivityFragmentLifecycle
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
        this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
    }
 
 RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
 RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
    this.context = context.getApplicationContext();
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.glide = Glide.get(context);
    this.optionsApplier = new OptionsApplier();
 
    ConnectivityMonitor connectivityMonitor = factory.build(context,
  new RequestManagerConnectivityListener(requestTracker));
 
    if (Util.isOnBackgroundThread()) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                lifecycle.addListener(RequestManager.this);
            }
        });
    } else {
        lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);
}

4. Glide生命周期綁定機制時序圖

Glide生命周期綁定機制時序圖

(本篇是Glide框架及源碼解析的第一篇,更多文章敬請關注后續文章。版權歸作者所有,如有轉發,請注明文章出處:原文鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容