Glide源碼解析之with()

常見(jiàn)用法

fun ImageView.loadPic(drawableId: Int) {
    GlideApp.with(this)
            .load(drawableId)
            .centerCrop()
            .into(this)
}

imageView.loadPic(R.drawable.ic_picture)

一般都把圖片加載的方法封裝起來(lái),一來(lái)是為了使用方便,二來(lái)就算以后的實(shí)際加載方法變了(比如把Glide換成其他圖片Picasso),也不會(huì)影響原來(lái)的代碼。

源碼解析

可以看實(shí)際調(diào)用的第一行代碼傳入了一個(gè)View,實(shí)際上with()方法有很多個(gè)重載方法,傳入Context,Activity,F(xiàn)ragment都是可以的。這里我們從傳入View的情況開(kāi)始分析。

實(shí)際調(diào)用
  public static GlideRequests with(@NonNull View view) {
    return (GlideRequests) Glide.with(view);
  }
  
  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }
  
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    return Glide.get(context).getRequestManagerRetriever();
  }
  
  public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
  }
  

可以看到首先是調(diào)用Glide.with(view)獲取一個(gè)RequestManager再?gòu)?qiáng)轉(zhuǎn)為GlideRequests(GlideRequests繼承于RequestManger)。而獲取RequestManager則是通過(guò)RequestManagerRetriever的get(view)方法,接下來(lái)看下里面做了什么。

  public RequestManager get(@NonNull View view) {
    if (Util.isOnBackgroundThread()) {
      return get(view.getContext().getApplicationContext());
    }

    Preconditions.checkNotNull(view);
    Preconditions.checkNotNull(view.getContext(),
        "Unable to obtain a request manager for a view without a Context");
    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.
    if (activity instanceof FragmentActivity) {
      Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
      return fragment != null ? get(fragment) : get(activity);
    }

    // Standard Fragments.
    android.app.Fragment fragment = findFragment(view, activity);
    if (fragment == null) {
      return get(activity);
    }
    return get(fragment);
  }
  1. 首先判斷當(dāng)前是不是在子線程,如果是,則調(diào)用參數(shù)為ApplicationContext的get方法。因?yàn)镚lide加載圖片的時(shí)候是需要和生命周期綁定的,所以如果是在子線程加載則使用應(yīng)用程序的生命周期。這種情況比較少,我們繼續(xù)往下看。

  2. 然后就是判斷view和view的Context是否為null,如果為null則拋出異常。

  3. 接著調(diào)用findActivity(view.getContext())來(lái)找到這個(gè)View是屬于哪個(gè)Activity的,來(lái)看下具體的實(shí)現(xiàn)。

  private Activity findActivity(@NonNull Context context) {
    if (context instanceof Activity) {
      return (Activity) context;
    } else if (context instanceof ContextWrapper) {
      return findActivity(((ContextWrapper) context).getBaseContext());
    } else {
      return null;
    }
  }

首先判斷這個(gè)Context是不是Activity,如果是的話則直接強(qiáng)轉(zhuǎn)就行了。因?yàn)槔^承于Context的還有Service和Application,甚至還有這種。

  1. 如果調(diào)用findActivity獲取到的Activity為null的話則還是調(diào)用參數(shù)為ApplicationContext的get方法。這種情況也比較少,我們繼續(xù)往下看。
  2. 如果Activity是繼承FragmentActivity的(AppCompatActivity繼承于FragmentActivity),則通過(guò)findSupportFragment方法獲取Fragment,如果獲取的到則調(diào)用參數(shù)為Fragment的get方法,如果獲取不到,則調(diào)用參數(shù)為Activity的get方法。那么來(lái)看下findSupportFragment的具體實(shí)現(xiàn)。
  private Fragment findSupportFragment(@NonNull View target, @NonNull FragmentActivity activity) {
    tempViewToSupportFragment.clear();
    findAllSupportFragmentsWithViews(
        activity.getSupportFragmentManager().getFragments(), tempViewToSupportFragment);
    Fragment result = null;
    View activityRoot = activity.findViewById(android.R.id.content);
    View current = target;
    while (!current.equals(activityRoot)) {
      result = tempViewToSupportFragment.get(current);
      if (result != null) {
        break;
      }
      if (current.getParent() instanceof 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) {
      // getFragment()s in the support FragmentManager may contain null values, see #1991.
      if (fragment == null || fragment.getView() == null) {
        continue;
      }
      result.put(fragment.getView(), fragment);
      findAllSupportFragmentsWithViews(fragment.getChildFragmentManager().getFragments(), result);
    }

首先把tempViewToSupportFragment清空,這個(gè)tempViewToSupportFragment是一個(gè)以View為key,F(xiàn)ragment為value的ArrayMap,從命名可以看出就是一個(gè)臨時(shí)的map,所以每次根據(jù)View去找這個(gè)View所在的Fragment時(shí)都要清空。

接著它調(diào)用了findAllSupportFragmentsWithViews。

  • 假設(shè)當(dāng)前Activity有一個(gè)Fragment,叫做ParentFragment。這個(gè)ParentFragment里面還有一個(gè)Fragment叫做ChildFragment。那么一開(kāi)始該方法傳入的就是只包含ParentFragment的集合,然后遍歷這個(gè)集合,如果里面的Fragment不為null,和Fragment.getView()也不為null則把Fragment.getView()作為key,fragment作為value放進(jìn)tempViewToSupportFragment中。(tip:getView()返回的就是Fragment的onCreateView方法返回的那個(gè)View)
  • 然后再遞歸調(diào)用該方法,因?yàn)镕ragment里面可能還有Fragment,當(dāng)沒(méi)有時(shí)遞歸終止。
  • 此時(shí)就是把ChildFragment.getView()和ChildFragment放進(jìn)tempViewToSupportFragment,然后遞歸完成。
  • 最終tempViewToSupportFragment的鍵值對(duì)為[ParentView,ParentFragment],[ChildView,ChildFragment]

這時(shí)再回到findSupportFragment()方法中,通過(guò)findViewById找到Activity的activityRoot(即是ContentView),然后把target(一開(kāi)始的ImageView)賦值給current,接著開(kāi)始找target所在的Fragment。這里假設(shè)在ChildFragment里面的View結(jié)構(gòu)為<LinearLayout><ImageView/></LinearLayout>。

  • 這里通過(guò)While循環(huán),循環(huán)的條件是!current.equals(activityRoot),因?yàn)槲覀儗?xiě)的View都是在ContentView里面的,如果current如果等于contentView了則代表沒(méi)找到,循環(huán)終止。
  • 通過(guò)tempViewToSupportFragment.get(current)來(lái)獲取Fragment,如果獲取到則while循環(huán)終止,這里繼續(xù)。
  • 然后判斷current的Parent是否繼承于View,這里current的Parent是LinearLayout,所以進(jìn)入if語(yǔ)句。把LinearLayout賦值給current,同時(shí)這個(gè)LinearLayout就是ChildView,接著進(jìn)入下一輪循環(huán)。
  • 這是由于current變成了ChildView,所以直接從tempViewToSupportFragment獲取到ChildFragment,循環(huán)終止,把ChildFragment返回。
  1. 再回看下之前的代碼
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);

因?yàn)檫@里我們成功獲取到了Fragment,所以最終會(huì)調(diào)用到參數(shù)為Fragment的get方法。

  1. 那么接下來(lái)看下get(fragment)方法
  public RequestManager get(@NonNull Fragment fragment) {
    Preconditions.checkNotNull(fragment.getActivity(),
          "You cannot start a load on a fragment before it is attached or after it is destroyed");
    if (Util.isOnBackgroundThread()) {
      return get(fragment.getActivity().getApplicationContext());
    } else {
      FragmentManager fm = fragment.getChildFragmentManager();
      return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
    }
  }

首先還是檢查是不是在子線程,雖然get(view)方法已經(jīng)檢查過(guò)一遍了,但是使用Glide的時(shí)候除了get(view)方法也是可以直接使用get(fragment)方法的,所以這里還是要檢查一遍線程。

  1. 接著又調(diào)用了supportFragmentGet()方法
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    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);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
  • 可以看到首先通過(guò)getSupportRequestManagerFragment()獲取一個(gè)SupportRequestManagerFragment,那么看下getSupportRequestManagerFragment()的代碼。
  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

首先通過(guò)findFragmentByTag()在FragmentManager中找是否之前已經(jīng)有SupportRequestManagerFragment了。如果沒(méi)有,就從pendingSupportRequestManagerFragments中獲取,pendingSupportRequestManagerFragments是一個(gè)以FragmentManager為key,SupportRequestManagerFragment為value的HashMap。如果也沒(méi)有,則創(chuàng)建一個(gè)SupportRequestManagerFragment,并且把參數(shù)Fragment作為它的父Fragment。

如果父Fragment是可見(jiàn)的,則調(diào)用SupportRequestManagerFragment的onStart生命周期。接著把剛創(chuàng)建的SupportRequestManagerFragment放進(jìn)pendingSupportRequestManagerFragments,然后調(diào)用FragmentManager把這個(gè)SupportRequestManagerFragment添加進(jìn)去,最后用handler發(fā)送消息把這個(gè)SupportRequestManagerFragment從pendingSupportRequestManagerFragments刪除。

為什么放進(jìn)了pendingSupportRequestManagerFragments又要馬上刪除呢?從命名可以看出里面是放待添加的Fragment,主要是為了FragmentManager還沒(méi)把Fragment添加完成,而又調(diào)用了該方法。因?yàn)槿绻砑映晒涂梢哉{(diào)用findFragmentByTag獲取到,如果沒(méi)添加成功則可以通過(guò)pendingSupportRequestManagerFragments獲取到,這樣避免了在Fragment中添加多個(gè)SupportRequestManagerFragment。最后刪掉是為了避免持有Fragment而導(dǎo)致不能釋放引起內(nèi)存泄漏。

  1. 回到supportFragmentGet()方法
    首先獲取RequestManager,新創(chuàng)建的SupportRequestManagerFragment是沒(méi)的,所以接著通過(guò)RequestManagerFactory創(chuàng)建了一個(gè),并傳入給SupportRequestManagerFragment,然后就把requestManager返回。因?yàn)镚lide.with()方法返回的就是一個(gè)requestManager,所以到這里流程就分析完了。

  2. 其實(shí)get(activity)的方法和get(fragment)差不多,只是如果是FragmentActivity則最終調(diào)用的還是supportFragmentGet()方法,只不過(guò)這里就是直接往FragmentActivity添加SupportRequestManagerFragment就行了。如果是Activity則調(diào)用的是fragmentGet()方法,添加的是RequestManagerFragment,區(qū)別在于這個(gè)Fragment是繼承于Fragment還是support.v4包的Fragment。

總結(jié)

除了調(diào)用get(application)外,其他get()方法都是在Activity或者Fragment中添加一個(gè)Fragment,而這個(gè)Fragment里面有一個(gè)ActivityFragmentLifecycle變量,用來(lái)感應(yīng)生命周期,從而使得圖片的加載過(guò)程可以和生命周期相呼應(yīng)。

所以如果是在Activity或者Fragment中加載,最好還是調(diào)用對(duì)應(yīng)的get()方法,因?yàn)閰?shù)為View的還要去找一下這個(gè)View是在哪個(gè)Activity或者Fragment中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容