Glide源碼分析-生命周期管理

聲明:我目前分析的源碼是最新的Glide源碼:4.12.0版本

implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

1. Glide簡介

Glide是一個支持拉取,解碼和展示視頻快照冬筒,圖片,和GIF動畫的框架憎蛤,正如它的名字翻譯一樣:滑翔。讓用戶加載任何形式的圖片列表盡可能地變得更快而柑、更平滑寻馏。

Glide 使用簡明靈活的流式語法API,允許你在大部分情況下一行代碼搞定需求:

Glide.with(fragment)
     .load(url)
     .into(imageView);

默認(rèn)情況下杖爽,Glide使用的是一個定制化的基于HttpUrlConnection的棧,但同時也提供了與Google Volley和Square OkHttp快速集成的工具庫。

2. 性能

Glide 充分考慮了Android圖片加載性能的兩個關(guān)鍵方面:

  • 圖片解碼速度

  • 解碼圖片帶來的資源壓力

為了讓用戶擁有良好的App使用體驗(yàn)慰安,圖片不僅要快速加載腋寨,而且還不能因?yàn)檫^多的主線程I/O或頻繁的垃圾回收導(dǎo)致頁面的閃爍和抖動現(xiàn)象。Glide使用了多個步驟來確保在Android上加載圖片盡可能的快速和平滑:

  • 自動化焕、智能地下采樣(downsampling)和緩存(caching)萄窜,以最小化存儲開銷和解碼次數(shù);
  • 積極的資源重用撒桨,例如字節(jié)數(shù)組和Bitmap查刻,以最小化昂貴的垃圾回收和堆碎片影響;
  • 深度的生命周期集成元莫,以確保僅優(yōu)先處理活躍的Fragment和Activity的請求,并有利于應(yīng)用在必要時釋放資源以避免在后臺時被殺掉蝶押。

3. 源碼分析

關(guān)于Glide的詳細(xì)使用介紹踱蠢,參見前面鏈接即可,但是分析源碼也要有個開始吧棋电,那我們就從最基本的用法入手茎截,Glide最基本的用法就是三段論:with、load和into赶盔。那我們現(xiàn)在就先從with開始企锌。

3.1 生命周期的作用域(1.Application, 2.Activity于未, 3.Fragment)

//com.bumptech.glide.Glide.java
public static RequestManager with(@NonNull Context context) {
    // getRetriever(context)返回RequestManagerRetriever撕攒,下面的生命周期關(guān)聯(lián)會用到
    return getRetriever(context).get(context);
}

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

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

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

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

從Glide.with()靜態(tài)方法中,我們可以看到方法可以傳入5個參數(shù):Context烘浦、Activity抖坪、FragmentActivity、Fragment或者View闷叉。按照作用域分擦俐,我們可以把它分成兩類:Application類和非Application類。

  • Application類:它的生命周期是全局的握侧,沒有空白Fragment就綁定Activity/Fragment蚯瞧。
  • 非Application類,它的生命周期跟隨Activity和Fragment的生命周期品擎,專門有一個空白Fragment綁定Activity/Fragment埋合。

從上面with的重載方法看出都會調(diào)用到getRetriever()方法,他其實(shí)最終就是返回RequestManagerRetriever對象萄传,緊接著饥悴,我們看看RequestManagerRetriever的get方法。這里先分兩個來看:

  • Activity、FragmentActivity西设、Fragment和View
  • Context
3.1.1 Activity類的get()
public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
       //如果不是UI線程瓣铣,生命周期劃分到Application
    return get(activity.getApplicationContext()); 
  } else if (activity instanceof FragmentActivity) {
    return get((FragmentActivity) activity);
  } else {
    assertNotDestroyed(activity);
    frameWaiter.registerSelf(activity);
    android.app.FragmentManager fm = activity.getFragmentManager();
    // 不管是Activity、FragmentActivity贷揽、Fragment和View的get()棠笑,最終都會調(diào)用到fragmentGet()
    return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}
//最終with返回綁定了代理Fragemnt的RequestManager
private RequestManager fragmentGet(
      @NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    //創(chuàng)建一個沒有UI的空Fragment來監(jiān)控用戶自定義的Activity生命周期
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      //創(chuàng)建Glide實(shí)例
      Glide glide = Glide.get(context);
      //綁定Glide和空白的Fragemnt
      //注意!禽绪!:這里創(chuàng)建RequestManager的時候會傳入Lifecycle,后面會根據(jù)這個lifecycle來透傳fragment的生命周期給Glide
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      
      if (isParentVisible) {
        requestManager.onStart();
      }
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
3.1.2 Context類的get()
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)) {
    // 如果在UI線程蓖救,并且是3.1.1小節(jié)的組件,就調(diào)用Activity類的get()印屁,返回綁定了空Fragment的requestManager
    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());
    }
  }
  // 作用域是Application返回的返回綁定了空Fragment的requestManager
  return getApplicationManager(context);
}

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) {
          //生命周期和Application一致
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }
    return applicationManager;
  }
3.1.3 小結(jié)

Glide的with()生命周期總結(jié)如下圖

作用域總結(jié).png
  • 子線程中Glide調(diào)用get()循捺、傳入ApplicationContext和ServiceContext,對應(yīng)的生命周期屬于Application域雄人,生命周期和Application一致
  • Activity从橘、Fragment和View對應(yīng)的生命周期是被代理Fragemnt監(jiān)控的,Glide內(nèi)部邏輯會根據(jù)Activity的生命周期做相應(yīng)調(diào)整

3.2 Glide生命周期感知過程

如3.1.1小節(jié)中分析的础钠,不管with傳入的是fragment恰力、Activity還是View,RequestManagerRetriever都會創(chuàng)建一個沒有UI的Fragment(SupportRequestManagerFragment)來當(dāng)代理,當(dāng)用戶自定義的Activity或者Fragment生命周期發(fā)生變化的時候旗吁,Glide內(nèi)部可以通過代理Fragment知道生命周期發(fā)生的變化踩萎,因此內(nèi)部也做相應(yīng)的處理。例如下載并且加載是一個比較耗時操作很钓,如果下載完成之后香府,Activity已經(jīng)被銷毀了,那就不用加載了码倦,所有這就是Glide感知組件生命周期的意義回还,用戶不用主動調(diào)用clear()方法來釋放資源了。關(guān)聯(lián)關(guān)系如下圖叹洲。

生命周期與組件關(guān)聯(lián).png

3.3 Glide與空Fragment綁定過程

從3.1.3小結(jié)中可以知道柠硕,只有在主線程中調(diào)用with()并且傳入Activity或者Fragment會綁定空Fragment。當(dāng)傳入的activity运提,這個activity上覆蓋的空Fragment是RequestManagerFragment蝗柔;當(dāng)傳入的是fragment,覆蓋在上面的空fragment是SupportRequestManagerFragment民泵。邏輯都是一樣的癣丧,下面以傳入fragment來分析。

/** Pending adds for SupportRequestManagerFragments. 
* 空Fragment(SupportRequestManagerFragment)與FragmentManager的映射關(guān)系
*/
  final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = new HashMap<>();

private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    // 從三個地方去拿空Fragment栈妆,緊接著會分析
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    // 從空Fragment中拿到requestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // 實(shí)例化glide用于在創(chuàng)建requestManager的時候綁定glide
      Glide glide = Glide.get(context);
      // 創(chuàng)建requestManager并且綁定glide
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      //如果父fragment可見胁编,就通知glide的onStart()
      if (isParentVisible) {
        requestManager.onStart();
      }
      current.setRequestManager(requestManager);
    }
    return requestManager;
}

private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
    // 第一個地方:先從FragmentManager中去拿
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      // 第二個地方:從內(nèi)存中去拿
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        //以上兩個地方都沒有,就創(chuàng)建一個空Fragment然后返回
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        //記錄映射關(guān)系在內(nèi)存中厢钧,臨時存的
        pendingSupportRequestManagerFragments.put(fm, current);
        //提交Fragment事務(wù)
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        //如果提交成功之后就刪除臨時存的映射關(guān)系
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

從supportFragmentGet()方法中我們看到了Glide內(nèi)部邏輯和空Fragment的綁定過程。

不知道大家有沒有疑惑:為什么在getSupportRequestManagerFragment()方法中要在pendingSupportRequestManagerFragments中臨時保存空Fragment嬉橙?

其實(shí)就是為了避免 SupportRequestManagerFragment 在一個作用域中重復(fù)創(chuàng)建早直。
因?yàn)閏ommitAllowingStateLoss() 是將事務(wù) post 到消息隊(duì)列中的,也就是說市框,事務(wù)是異步處理的霞扬,而不是同步處理的。假設(shè)沒有臨時保存記錄枫振,一旦在事務(wù)異步等待執(zhí)行時調(diào)用了Glide.with(...) 喻圃,就會在該作用域中重復(fù)創(chuàng)建 Fragment。

3.4 生命周期的詳細(xì)監(jiān)聽過程

從上面小節(jié)分析可以看出粪滤,生命周期的管理斧拍,主要對Activity、Fragment和View(實(shí)質(zhì)也是Activity或Fragment)有效杖小,其他的都是Application域肆汹,沒必須分析。所以后面的分析都是基于Activity或者Fragment的生命周期窍侧,而這兩個邏輯都是一樣的县踢,我們還是以Fragment的分析為例转绷。

從前面看出伟件,每個Activity或者Fragment域都會創(chuàng)建一個空Fragment蓋在上面。下來我們就從這個空Fragment開始分析Glide的生命周期是如何受影響的议经。Fragment蓋的空Fragment是SupportRequestManagerFragment.java斧账,那我們就從這里開始。

public class SupportRequestManagerFragment extends Fragment {
  private final ActivityFragmentLifecycle lifecycle;
    
  public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }

  @VisibleForTesting
  @SuppressLint("ValidFragment")
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
    
  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

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

從3.1.1小節(jié)可以看到煞肾,創(chuàng)建RequestManager時是通過工廠創(chuàng)建的咧织,那我們在看看這個工廠

//RequestManagerRetriever.java中
//創(chuàng)建Glide實(shí)例
Glide glide = Glide.get(context);
requestManager =factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);

//factory接口
public interface RequestManagerFactory {
    @NonNull
    RequestManager build(
        @NonNull Glide glide,
        @NonNull Lifecycle lifecycle,
        @NonNull RequestManagerTreeNode requestManagerTreeNode,
        @NonNull Context context);
  }

//默認(rèn)的RequestManager工廠
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);
    }
};

RequestManager工廠在創(chuàng)建RequestManager的時候,傳入了glide和lifecycle籍救,在RequestManager中完成綁定习绢。

public class RequestManager
    implements ComponentCallbacks2, LifecycleListener,...{
    
    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()) {
        Util.postOnUiThread(addSelfToLifecycle);
      } else {
        //lifecycle的生命周期回調(diào)加入到RequestManager中
        lifecycle.addListener(this);
      }

      ......
    }
    
    public synchronized void onStart() {
        resumeRequests();
        // targetTracker維持著Traker列表,每個Traker屬于Glide內(nèi)部需要監(jiān)聽生命周期的邏輯
        targetTracker.onStart();
    }
    
    public synchronized void onStop() {
        pauseRequests();
        targetTracker.onStop();
    }
}

至此,生命周期的詳細(xì)監(jiān)聽過程就通了蝙昙。

總結(jié):調(diào)用with()創(chuàng)建RequestManager闪萄,創(chuàng)建RequestManager傳入Lifecycle對象(這個對象是在無界面 Fragment 中創(chuàng)建的),RequestManager通過Lifecycle通知Glide內(nèi)部的各個業(yè)務(wù)Traker奇颠。

當(dāng) Fragment 的生命周期變化時败去,無界面 Fragment會感知到生命周期變化,然后通過Lifecycle 對象將事件分發(fā)到 RequestManager烈拒,RequestManager的onStart()將事件分發(fā)給targetTracker圆裕,targetTracker分發(fā)給Glide內(nèi)部邏輯广鳍。

參考文檔:

1.Glide簡介

2.郭霖關(guān)于Glide的源碼分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吓妆,隨后出現(xiàn)的幾起案子赊时,更是在濱河造成了極大的恐慌,老刑警劉巖耿战,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛋叼,死亡現(xiàn)場離奇詭異,居然都是意外死亡剂陡,警方通過查閱死者的電腦和手機(jī)狈涮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸭栖,“玉大人歌馍,你說我怎么就攤上這事≡稳担” “怎么了松却?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溅话。 經(jīng)常有香客問我晓锻,道長,這世上最難降的妖魔是什么飞几? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任砚哆,我火速辦了婚禮,結(jié)果婚禮上屑墨,老公的妹妹穿的比我還像新娘躁锁。我一直安慰自己,他們只是感情好卵史,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布战转。 她就那樣靜靜地躺著,像睡著了一般以躯。 火紅的嫁衣襯著肌膚如雪槐秧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天忧设,我揣著相機(jī)與錄音刁标,去河邊找鬼。 笑死见转,一個胖子當(dāng)著我的面吹牛命雀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斩箫,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼吏砂,長吁一口氣:“原來是場噩夢啊……” “哼撵儿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狐血,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤淀歇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后匈织,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪默,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年缀匕,在試婚紗的時候發(fā)現(xiàn)自己被綠了纳决。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡乡小,死狀恐怖阔加,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情满钟,我是刑警寧澤胜榔,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站湃番,受9級特大地震影響夭织,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吠撮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一尊惰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纬向,春花似錦择浊、人聲如沸戴卜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽投剥。三九已至师脂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間江锨,已是汗流浹背吃警。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啄育,地道東北人酌心。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像挑豌,于是被迫代替她去往敵國和親安券。 傳聞我的和親對象是個殘疾皇子墩崩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

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