Glide解析三:Glide是如何自動感應(yīng)生命周期的家坎?

Glide解析一:Glide整體流程中牍陌,我們知道RequestManager是主要用來管理、啟動圖片加載請求的滥朱。而它是通過RequestManagerRetriever創(chuàng)建苛白、獲取的。

1焚虱、為什么用RequestManagerRetriever來獲取RequestManager购裙?

RequestManagerRetriever.java
為什么用RequestManagerRetriever來獲取RequestManager?而不直接使用RequestManager的單例鹃栽?我們通過常用的使用方式來分析其原因:

public RequestManager get(@NonNull View view) {
    if (Util.isOnBackgroundThread()) {
      //如果是在非UI線程躏率,則使用綁定Application生命周期的RequestManager
      return get(view.getContext().getApplicationContext());
    }

    Preconditions.checkNotNull(view);
    Preconditions.checkNotNull(view.getContext(),
        "Unable to obtain a request manager for a view without a Context");
    //根據(jù)view拿到activity
    Activity activity = findActivity(view.getContext());
    // The view might be somewhere else, like a service.
    if (activity == null) {
      return get(view.getContext().getApplicationContext());
    }

    // Support Fragments.
    // Although the user might have non-support Fragments attached to FragmentActivity, searching
    // for non-support Fragments is so expensive pre O and that should be rare enough that we
    // prefer to just fall back to the Activity directly.
    //如果使用的是support中的activity
    if (activity instanceof FragmentActivity) {
      //如果是FragmentActivity
      //如果view是support fragment中的躯畴,則根據(jù)view獲取其所在的support fragment,接著調(diào)用get(fragment)
      //如果view是FragmentActivity中薇芝,則調(diào)用get(FragmentActivity)方法
      Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
      return fragment != null ? get(fragment) : get(activity);
    }
     //如果沒有support中的組件蓬抄,其原理與support中的activity一樣,不展開講解
    // Standard Fragments.
    android.app.Fragment fragment = findFragment(view, activity);
    if (fragment == null) {
      return get(activity);
    }
    return get(fragment);
  }

private Fragment findSupportFragment(@NonNull View target, @NonNull FragmentActivity activity) {
    //清空view和fragment關(guān)聯(lián)列表
    tempViewToSupportFragment.clear();
    //從activity的所有fragment中找出fragment的根view夯到,并將根view與fragment進行關(guān)聯(lián)存放view和fragment關(guān)聯(lián)列表tempViewToSupportFragment中
    findAllSupportFragmentsWithViews(
        activity.getSupportFragmentManager().getFragments(), tempViewToSupportFragment);
    Fragment result = null;
    View activityRoot = activity.findViewById(android.R.id.content);
    View current = target;
    while (!current.equals(activityRoot)) {
      //從view和fragment關(guān)聯(lián)列表中獲取view所關(guān)聯(lián)的fragment
      result = tempViewToSupportFragment.get(current);
      if (result != null) {
        break;
      }
      if (current.getParent() instanceof View) {
        //獲取當(dāng)前view的父級view
        current = (View) current.getParent();
      } else {
        break;
      }
    }

    tempViewToSupportFragment.clear();
    return result;
  }
 
private static void findAllSupportFragmentsWithViews(
      @Nullable Collection<Fragment> topLevelFragments,
      @NonNull Map<View, Fragment> result) {
    if (topLevelFragments == null) {
      return;
    }
    for (Fragment fragment : topLevelFragments) {
      //遍歷fragment
      // getFragment()s in the support FragmentManager may contain null values, see #1991.
      if (fragment == null || fragment.getView() == null) {
        continue;
      }
      //將fragment的根view與fragment進行關(guān)聯(lián)
      result.put(fragment.getView(), fragment);
      findAllSupportFragmentsWithViews(fragment.getChildFragmentManager().getFragments(), result);
    }
  }

一般我們都是使用support 中的fragmetactivity嚷缭,所以get(view)要么轉(zhuǎn)到get(Fragment)中要么轉(zhuǎn)到get(FragmentActivity)中。這里先根據(jù)view找到其所在fragment的思路是拿當(dāng)前view的根view與activity的所有fragment的根view進行比較耍贾,如果相等阅爽,則這個fragment就是我們要找的fragment。

1.1荐开、get(Fragment) 的實現(xiàn)邏輯
public RequestManager get(@NonNull Fragment fragment) {
      //...
      //獲取fragment的子FragmentManager
      FragmentManager fm = fragment.getChildFragmentManager();
      //調(diào)用supportFragmentGet
      return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
    }
  }


private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    //根據(jù)FragmentManager獲取已經(jīng)存在的SupportRequestManagerFragment 或者創(chuàng)建一個新的SupportRequestManagerFragment 
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    //獲取與fragment關(guān)聯(lián)的RequestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      //RequestManager為空
      Glide glide = Glide.get(context);
      //構(gòu)建一個新的RequestManager
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      //將fragment與RequestManager進行關(guān)聯(lián)
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

get(Fragment)獲取SupportRequestManagerFragment 的邏輯我們看下其代碼實現(xiàn):

private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    //根據(jù)tag名稱從FragmentManager中查找SupportRequestManagerFragment 
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      //如果FragmentManager沒有SupportRequestManagerFragment 
      //從等待FragmentManager添加Fragment完成列表中獲取SupportRequestManagerFragment 
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        ////如果沒有SupportRequestManagerFragment 
        //創(chuàng)建新的SupportRequestManagerFragment
        current = new SupportRequestManagerFragment();
        //將新創(chuàng)建的fragment加到其父級fragment列表中
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          //如果父級可見付翁,回調(diào)生命周期的onStart
          current.getGlideLifecycle().onStart();
        }
        //將創(chuàng)建的fragment加入等待FragmentManager添加fragment的隊列中
        pendingSupportRequestManagerFragments.put(fm, current);
        //將創(chuàng)建的fragment加入FragmentManager中
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        //發(fā)送消息,從等待FragmentManager添加fragment的隊列中刪除fragment
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

根據(jù)上面的代碼分析可以歸納RequestManagerRetriever創(chuàng)建或者獲取RequestManager的流程:

  • 如果在子線程晃听,則創(chuàng)建一個生命周期與Application一樣的RequestManager
  • 根據(jù)view先拿到其所屬的fragment或者activity
  • 從當(dāng)前framgnet或者activity中獲取RequestManagerFragment百侧,如果不為空則返回與之關(guān)聯(lián)的RequestManager;如果為空那么創(chuàng)建一個新的RequestManagerFragment能扒,創(chuàng)建的情況下順便創(chuàng)建RequestManager,并進行關(guān)聯(lián)佣渴,并返回該RequestManager
    根據(jù)這段代碼有2個問題,
    問題1:為什么使用pendingSupportRequestManagerFragments來添加RequestManagerManager初斑?fm添加RequestManagerFragment之后又發(fā)送消息刪除該RequestManagerFragment呢辛润?
    答案:因為fm添加fragment不是調(diào)用add方法就代表添加完成的,它有相關(guān)的生命周期是異步進行的越平,所以如果add之后立馬又在相同fragment或者activity環(huán)境中調(diào)用get方法频蛔,那么就很有可能又創(chuàng)建一個新的RequestManagerFragment,而pendingSupportRequestManagerFragments就是為了杜絕重復(fù)創(chuàng)建RequestManagerFragment而準(zhǔn)備的秦叛。
    問題2:對于glide的get使用有什么優(yōu)化的地方嗎晦溪?
    答案:
  • 盡量不要在子線程中調(diào)用,因為子線程調(diào)用意味著生命周期是全局的挣跋,不能跟activity或fragment的生命周期同步
  • get的時候最好傳activity三圆、或者fragment,因為這樣可以減少查找具體的fragment或者activity的步驟
    問題3:為什么要用RequestManagerFragment并與RequestManager進行關(guān)聯(lián)呢避咆?
    答案:是為了進行自動感應(yīng)組件的生命周期
2舟肉、為什么要用RequestManagerFragment并與RequestManager進行關(guān)聯(lián)?

步驟1中分析了get的流程中查库,fragment和activity最終是通過創(chuàng)建RequestManagerFragment并和RequestManager進行關(guān)聯(lián)
我們看下RequestManagerFragment的幾個關(guān)鍵生命周期函數(shù):

@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();
  }

RequestManagerFragment的生命周期相關(guān)的函數(shù)調(diào)用lifecycle對象對應(yīng)的方法路媚,而lifecycle是在SupportRequestManagerFragment構(gòu)造函數(shù)數(shù)中創(chuàng)建的:

public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
 }

ActivityFragmentLifecycle內(nèi)部維持了生命周期的監(jiān)聽者列表:

class ActivityFragmentLifecycle implements Lifecycle {
  private final Set<LifecycleListener> lifecycleListeners =
      Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
  //...
}

當(dāng)RequestManagerFragment生命周期變化時,調(diào)用ActivityFragmentLifecycle的對應(yīng)生命周期方法樊销,其實現(xiàn)就是遍歷生命周期監(jiān)聽列表整慎,并調(diào)用監(jiān)聽者對應(yīng)的生命周期方法:

void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }

那么什么時候添加監(jiān)聽者到ActivityFragmentLifecycle呢脏款?答案是創(chuàng)建RequestManager的時候,我們看下RequestManager構(gòu)成函數(shù):

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;

    connectivityMonitor =
        factory.build(
            context.getApplicationContext(),
            new RequestManagerConnectivityListener(requestTracker));

    // If we're the application level request manager, we may be created on a background thread.
    // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
    // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
    // This should be entirely safe.
    if (Util.isOnBackgroundThread()) {
      //如果是自線程裤园,則切換到主線程監(jiān)聽生命周期
      mainHandler.post(addSelfToLifecycle);
    } else {
      //監(jiān)聽生命周期
      lifecycle.addListener(this);
    }
    //生命周期變化時撤师,相應(yīng)的監(jiān)聽網(wǎng)絡(luò)狀態(tài)和取消監(jiān)聽網(wǎng)絡(luò)裝
    lifecycle.addListener(connectivityMonitor);

    setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());

    glide.registerRequestManager(this);
  }

ok,最后我們總結(jié)下Glide是如何自動感應(yīng)生命周期的:

  • 如果是在子線程拧揽,這Glide使用的RequestManager與Application一樣的生命周期
  • 如果是fragment剃盾、activity,則使用FragmentManagerFragment與RequestManager進行關(guān)聯(lián),通過FragmentManagerFragment的生命周期變化來調(diào)度RequestManager對圖片加載請求Request采取暫停淤袜、重新開始痒谴、停止等操作。
    因為Android有fragment饮怯、support包中的fragment闰歪、activity嚎研、surport中的activity蓖墅,因為使用FragmentManagerFragment來實現(xiàn)RequestManager感應(yīng)生命周期,這幾個fragmengt临扮、activity使用的FragmentManager各自不同,如果是fragment在用ChildFragmentManager或者support中的ChildFragmentManager杆勇;如果是activity則用FragmentManager或者support中的FragmentManager蚜退。所以RequestManagerRetriever主要用來根據(jù)framgnet、activity來創(chuàng)建對應(yīng)的RequestManager蚂且。
    問題:Glide中最多有幾個RequestManager杏死?
    答案:5個捆交,分別是:
  • 與Application生命周期一致的RequestManager
  • 與app包下activity的FragmentManager關(guān)聯(lián)的RequestManager
  • 與app包下fragment的ChildFragmentManager關(guān)聯(lián)的RequestManager
  • 與support包下activity的FragmentManager關(guān)聯(lián)的RequestManager
  • 與support包下fragment的ChildFragmentManager關(guān)聯(lián)的RequestManager
    問題:Glide的get操作有哪些優(yōu)化點品追?
    答案:
  • 在UI線程中調(diào)用肉瓦,可以避免RequestManager生命周期與Application的一直
  • 盡量采用support報下的fragment/activity银还;一是因為兼容性洁墙;二是如果統(tǒng)一使用support包下的可以避免創(chuàng)建于與app包下FragmentManager管理的RequestManager
  • get盡量傳遞fragment或者activity,這樣可以減少通過view找到具體的fragment或者activity的步驟
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捺弦,一起剝皮案震驚了整個濱河市列吼,隨后出現(xiàn)的幾起案子苦始,更是在濱河造成了極大的恐慌,老刑警劉巖陌选,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咨油,死亡現(xiàn)場離奇詭異役电,居然都是意外死亡,警方通過查閱死者的電腦和手機冀膝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氓扛,“玉大人采郎,你說我怎么就攤上這事蒜埋≌荩” “怎么了待错?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵火俄,是天一觀的道長犯建。 經(jīng)常有香客問我,道長瓜客,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任谱仪,我火速辦了婚禮,結(jié)果婚禮上疯攒,老公的妹妹穿的比我還像新娘嗦随。我一直安慰自己敬尺,他們只是感情好枚尼,可當(dāng)我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筷转,像睡著了一般姑原。 火紅的嫁衣襯著肌膚如雪呜舒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天,我揣著相機與錄音般婆,去河邊找鬼乡范。 笑死啤咽,一個胖子當(dāng)著我的面吹牛晋辆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳞青,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼霸饲,長吁一口氣:“原來是場噩夢啊……” “哼为朋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起厚脉,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤习寸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后傻工,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體融涣,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年精钮,在試婚紗的時候發(fā)現(xiàn)自己被綠了威鹿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡轨香,死狀恐怖忽你,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臂容,我是刑警寧澤科雳,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站脓杉,受9級特大地震影響糟秘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜球散,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一尿赚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蕉堰,春花似錦凌净、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至皿渗,卻和暖如春斩芭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乐疆。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工划乖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诀拭。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓迁筛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子细卧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,922評論 2 361

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