Glide源码学习笔记一:with方法做了什么?

Glide源码学习笔记一:with方法做了什么?


前言

在面试过程中,经常会问到Glide原理相关问题,问的人多了,就说明代码风格或者设计风格都有值得学习的地方。于是想记录自己在探索Glide源码遇到的问题以及想法,写下了这几篇文章。所有源码的版本是4.13.1

一、Glide的结构?

Glide的用法大家都很熟悉,最简单的如下三个步骤即可加载图片:

Glide.with(context).load(url).into(imageView);
Glide.with(Context).apple(BaseRequestOptions)..into(Object)
该方法返回的是一个RequestManager对象,这一步主要是做一个生命周期的绑定,加载图片的动作和生命周期保持一致该方法返回的是一个RequestBuilder,在这里需要确定图片尺寸,图片类型,是否使用占位图等等一系列加载参数。使得加载图片更加灵活的满足开发需要。图片加载的入口,在前两步只是最基本的操作,加载,解码,网络请求,缓存等等核心问题都是从该方法开始,可以说是Glide最核心的方法。

二、Glide加载三部曲之Glide.with

Glide.with有如下六种重载:
在这里插入图片描述

Glide.with(@NonNull Context context) =>RequestManager

跟进一下,会发现有如下几种重载:

 @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
  @NonNull
  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
  @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }
  @NonNull
  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getContext()).get(fragment);
  }
 @NonNull
  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

这个时候有一个疑问,为什么要区分页面(上下文)呢?结合我们在使用过程中的经验,有时候是在Fragment中加载,有时候在Activity加载,还有时候在自定义的View中加载,区分场景处理有什么好处呢?带着问题继续分析:
先看getRetriever方法,

Glide.getRetriever(@Nullable Context context)=>RequestManagerRetriever

@NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }

该方法首先做了一个上下文的空指针校验,返回值由两个方法构成,先看第一个Glide.get(context):

Glide.get(context)=>Glide

  @NonNull
  // Double checked locking is safe here.
  @SuppressWarnings("GuardedBy")
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }

    return glide;
  }

在这里做了一个double check,返回值变量glide就是Glide的实例,这个方法是不是和我们写单例的double check很像?
这个初始化的详细方法,在下一篇文章中来学习。回到之前的RequestManagerRetriever .get()方法,和Glide.with()的重载方法一一对应:

RequestManagerRetriever.get(Context)=>RequestManager

 @NonNull
  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)) {//线程判断和Application判断
      if (context instanceof FragmentActivity) {//上下文类型判断
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {//上下文类型判断
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
          // Context#createPackageContext may return a Context without an Application instance,
          // in which case a ContextWrapper may be used to attach one.
          //翻译:只有当baseContext有一个不为空的application context才可以打开ConextWrapper,
          //Context的createPackageContext方法可能在没有Application实例的情况下返回一个Context,
          //在这种情况下,一个ContextWrapper可能被建立和Glide的关系
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {//上下文类型判断
        return get(((ContextWrapper) context).getBaseContext());
      }
    }
	//兜底的RequestManager 
    return getApplicationManager(context);
  }

从代码中可以发现,如果传入的是Context,他可能来自当前页面(Activity/FragmentActivity),在这里做一个类型区分,在调用相应的重载方法。接着往下看:

RequestManagerRetriever.get(FragmentActivity)=>RequestManager

 @NonNull
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      frameWaiter.registerSelf(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

传入的参数是FragmentActivity,首先做一个判断,如果在后台,更改为ApplicationContext。这个时候我们好像知道了什么,是不是RequestManager持有的上下文,需要和当前加载图片的场景保持一致?接着看else里面的方法。首先判断当前的FragmentActivity有没有被销毁,如果销毁了就抛出一个异常。frameWaiter这个类不是很常见,从字面意思解读框架服务员?为什么activity要在这里注册他自己?我们看代码很容易找到是在RequestManagerRetriever构造函数中new出来的,具体有什么用,我们先记下来,打一个todo,看完真个流程的的Glide代码应该就可以知道了。接下来获取FragmentManager,这个类我们经常会在ViewPager中切换,获取Fragment用到,再看返回方法:

RequestManager.supportFragmentGet(Context ,FragmentManager,Fragment ,boolean)=>RequestManager

@NonNull
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,//在这里传递的是null
      boolean isParentVisible) {
    //获取一个自定义的Fragment
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    //获取与次fragment绑定的requestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      //request为控,重新使用工厂方法构造,绑定生命周期和fragment
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      // This is a bit of hack, we're going to start the RequestManager, but not the
      // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
      // Lifecycle might trigger memory leaks. See b/154405040
      //这有点像黑客,我们将启动RequestManager,但是不符合生命周期,启动RequestManager安全,但是启动生命周期
      //可能引起内存泄漏
      if (isParentVisible) {
        requestManager.onStart();
      }
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
 @NonNull
  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
    // If we have a pending Fragment, we need to continue to use the pending Fragment. Otherwise
    // there's a race where an old Fragment could be added and retrieved here before our logic to
    // add our pending Fragment notices. That can then result in both the pending Fragmeng and the
    // old Fragment having requests running for them, which is impossible to safely unwind.
    //如果准备好的Fragment就用,没有就创建一个
    SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
    if (current == null) {
      current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        //发送了一条handler消息
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

上述代码我们可以知道,一个RequestManager是和一个SupportRequestManagerFragment互相绑定的关系,在当前传入的Fragment中添加一个SupportRequestManagerFragment,实现了生命周期的绑定。之前那么多重载方法,也是为了找到一个页面进行添加Fragment。

总结

with方法做的事情

Glide.with()方法主要做的事情就是添加一个Fragment在需要加载图片的界面,区分了多种页面类型,例如:Activity,FragmentActivity,Fragment,View,在构造Fragment的时候,会先从一个Map里面根据FragmentManager作为Key来取,减少了创建次数。在持有了加载界面的生命周期之后,就可以避免在页面销毁后还在加载图片的情况。遗留了一个问题:frameWaiter的作用是什么?

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值