美文网首页
Glide源码解析之with()

Glide源码解析之with()

作者: 断了谁的弦 | 来源:发表于2020-04-15 11:44 被阅读0次

    常见用法

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

    一般都把图片加载的方法封装起来,一来是为了使用方便,二来就算以后的实际加载方法变了(比如把Glide换成其他图片Picasso),也不会影响原来的代码。

    源码解析

    可以看实际调用的第一行代码传入了一个View,实际上with()方法有很多个重载方法,传入Context,Activity,Fragment都是可以的。这里我们从传入View的情况开始分析。

    实际调用
      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;
      }
      
    

    可以看到首先是调用Glide.with(view)获取一个RequestManager再强转为GlideRequests(GlideRequests继承于RequestManger)。而获取RequestManager则是通过RequestManagerRetriever的get(view)方法,接下来看下里面做了什么。

      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. 首先判断当前是不是在子线程,如果是,则调用参数为ApplicationContext的get方法。因为Glide加载图片的时候是需要和生命周期绑定的,所以如果是在子线程加载则使用应用程序的生命周期。这种情况比较少,我们继续往下看。

    2. 然后就是判断view和view的Context是否为null,如果为null则抛出异常。

    3. 接着调用findActivity(view.getContext())来找到这个View是属于哪个Activity的,来看下具体的实现。

      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;
        }
      }
    

    首先判断这个Context是不是Activity,如果是的话则直接强转就行了。因为继承于Context的还有Service和Application,甚至还有这种。

    1. 如果调用findActivity获取到的Activity为null的话则还是调用参数为ApplicationContext的get方法。这种情况也比较少,我们继续往下看。
    2. 如果Activity是继承FragmentActivity的(AppCompatActivity继承于FragmentActivity),则通过findSupportFragment方法获取Fragment,如果获取的到则调用参数为Fragment的get方法,如果获取不到,则调用参数为Activity的get方法。那么来看下findSupportFragment的具体实现。
      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清空,这个tempViewToSupportFragment是一个以View为key,Fragment为value的ArrayMap,从命名可以看出就是一个临时的map,所以每次根据View去找这个View所在的Fragment时都要清空。

    接着它调用了findAllSupportFragmentsWithViews。

    • 假设当前Activity有一个Fragment,叫做ParentFragment。这个ParentFragment里面还有一个Fragment叫做ChildFragment。那么一开始该方法传入的就是只包含ParentFragment的集合,然后遍历这个集合,如果里面的Fragment不为null,和Fragment.getView()也不为null则把Fragment.getView()作为key,fragment作为value放进tempViewToSupportFragment中。(tip:getView()返回的就是Fragment的onCreateView方法返回的那个View)
    • 然后再递归调用该方法,因为Fragment里面可能还有Fragment,当没有时递归终止。
    • 此时就是把ChildFragment.getView()和ChildFragment放进tempViewToSupportFragment,然后递归完成。
    • 最终tempViewToSupportFragment的键值对为[ParentView,ParentFragment],[ChildView,ChildFragment]

    这时再回到findSupportFragment()方法中,通过findViewById找到Activity的activityRoot(即是ContentView),然后把target(一开始的ImageView)赋值给current,接着开始找target所在的Fragment。这里假设在ChildFragment里面的View结构为<LinearLayout><ImageView/></LinearLayout>。

    • 这里通过While循环,循环的条件是!current.equals(activityRoot),因为我们写的View都是在ContentView里面的,如果current如果等于contentView了则代表没找到,循环终止。
    • 通过tempViewToSupportFragment.get(current)来获取Fragment,如果获取到则while循环终止,这里继续。
    • 然后判断current的Parent是否继承于View,这里current的Parent是LinearLayout,所以进入if语句。把LinearLayout赋值给current,同时这个LinearLayout就是ChildView,接着进入下一轮循环。
    • 这是由于current变成了ChildView,所以直接从tempViewToSupportFragment获取到ChildFragment,循环终止,把ChildFragment返回。
    1. 再回看下之前的代码
    Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
    return fragment != null ? get(fragment) : get(activity);
    

    因为这里我们成功获取到了Fragment,所以最终会调用到参数为Fragment的get方法。

    1. 那么接下来看下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)方法已经检查过一遍了,但是使用Glide的时候除了get(view)方法也是可以直接使用get(fragment)方法的,所以这里还是要检查一遍线程。

    1. 接着又调用了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;
      }
    
    • 可以看到首先通过getSupportRequestManagerFragment()获取一个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;
      }
    

    首先通过findFragmentByTag()在FragmentManager中找是否之前已经有SupportRequestManagerFragment了。如果没有,就从pendingSupportRequestManagerFragments中获取,pendingSupportRequestManagerFragments是一个以FragmentManager为key,SupportRequestManagerFragment为value的HashMap。如果也没有,则创建一个SupportRequestManagerFragment,并且把参数Fragment作为它的父Fragment。

    如果父Fragment是可见的,则调用SupportRequestManagerFragment的onStart生命周期。接着把刚创建的SupportRequestManagerFragment放进pendingSupportRequestManagerFragments,然后调用FragmentManager把这个SupportRequestManagerFragment添加进去,最后用handler发送消息把这个SupportRequestManagerFragment从pendingSupportRequestManagerFragments删除。

    为什么放进了pendingSupportRequestManagerFragments又要马上删除呢?从命名可以看出里面是放待添加的Fragment,主要是为了FragmentManager还没把Fragment添加完成,而又调用了该方法。因为如果添加成功就可以调用findFragmentByTag获取到,如果没添加成功则可以通过pendingSupportRequestManagerFragments获取到,这样避免了在Fragment中添加多个SupportRequestManagerFragment。最后删掉是为了避免持有Fragment而导致不能释放引起内存泄漏。

    1. 回到supportFragmentGet()方法
      首先获取RequestManager,新创建的SupportRequestManagerFragment是没的,所以接着通过RequestManagerFactory创建了一个,并传入给SupportRequestManagerFragment,然后就把requestManager返回。因为Glide.with()方法返回的就是一个requestManager,所以到这里流程就分析完了。

    2. 其实get(activity)的方法和get(fragment)差不多,只是如果是FragmentActivity则最终调用的还是supportFragmentGet()方法,只不过这里就是直接往FragmentActivity添加SupportRequestManagerFragment就行了。如果是Activity则调用的是fragmentGet()方法,添加的是RequestManagerFragment,区别在于这个Fragment是继承于Fragment还是support.v4包的Fragment。

    总结

    除了调用get(application)外,其他get()方法都是在Activity或者Fragment中添加一个Fragment,而这个Fragment里面有一个ActivityFragmentLifecycle变量,用来感应生命周期,从而使得图片的加载过程可以和生命周期相呼应。

    所以如果是在Activity或者Fragment中加载,最好还是调用对应的get()方法,因为参数为View的还要去找一下这个View是在哪个Activity或者Fragment中。

    相关文章

      网友评论

          本文标题:Glide源码解析之with()

          本文链接:https://www.haomeiwen.com/subject/sgaqvhtx.html