Glide解析三:Glide是如何自动感应生命周期的?

Glide解析一:Glide整体流程中,我们知道RequestManager是主要用来管理、启动图片加载请求的。而它是通过RequestManagerRetriever创建、获取的。

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");
    //根据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中的,则根据view获取其所在的support fragment,接着调用get(fragment)
      //如果view是FragmentActivity中,则调用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关联列表
    tempViewToSupportFragment.clear();
    //从activity的所有fragment中找出fragment的根view,并将根view与fragment进行关联存放view和fragment关联列表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关联列表中获取view所关联的fragment
      result = tempViewToSupportFragment.get(current);
      if (result != null) {
        break;
      }
      if (current.getParent() instanceof View) {
        //获取当前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进行关联
      result.put(fragment.getView(), fragment);
      findAllSupportFragmentsWithViews(fragment.getChildFragmentManager().getFragments(), result);
    }
  }

一般我们都是使用support 中的fragmetactivity,所以get(view)要么转到get(Fragment)中要么转到get(FragmentActivity)中。这里先根据view找到其所在fragment的思路是拿当前view的根view与activity的所有fragment的根view进行比较,如果相等,则这个fragment就是我们要找的fragment。

1.1、get(Fragment) 的实现逻辑
public RequestManager get(@NonNull Fragment fragment) {
      //...
      //获取fragment的子FragmentManager
      FragmentManager fm = fragment.getChildFragmentManager();
      //调用supportFragmentGet
      return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
    }
  }


private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    //根据FragmentManager获取已经存在的SupportRequestManagerFragment 或者创建一个新的SupportRequestManagerFragment 
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    //获取与fragment关联的RequestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      //RequestManager为空
      Glide glide = Glide.get(context);
      //构建一个新的RequestManager
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      //将fragment与RequestManager进行关联
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

get(Fragment)获取SupportRequestManagerFragment 的逻辑我们看下其代码实现:

private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    //根据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 
        //创建新的SupportRequestManagerFragment
        current = new SupportRequestManagerFragment();
        //将新创建的fragment加到其父级fragment列表中
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          //如果父级可见,回调生命周期的onStart
          current.getGlideLifecycle().onStart();
        }
        //将创建的fragment加入等待FragmentManager添加fragment的队列中
        pendingSupportRequestManagerFragments.put(fm, current);
        //将创建的fragment加入FragmentManager中
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        //发送消息,从等待FragmentManager添加fragment的队列中删除fragment
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

根据上面的代码分析可以归纳RequestManagerRetriever创建或者获取RequestManager的流程:

  • 如果在子线程,则创建一个生命周期与Application一样的RequestManager
  • 根据view先拿到其所属的fragment或者activity
  • 从当前framgnet或者activity中获取RequestManagerFragment,如果不为空则返回与之关联的RequestManager;如果为空那么创建一个新的RequestManagerFragment,创建的情况下顺便创建RequestManager,并进行关联,并返回该RequestManager
    根据这段代码有2个问题,
    问题1:为什么使用pendingSupportRequestManagerFragments来添加RequestManagerManager?fm添加RequestManagerFragment之后又发送消息删除该RequestManagerFragment呢?
    答案:因为fm添加fragment不是调用add方法就代表添加完成的,它有相关的生命周期是异步进行的,所以如果add之后立马又在相同fragment或者activity环境中调用get方法,那么就很有可能又创建一个新的RequestManagerFragment,而pendingSupportRequestManagerFragments就是为了杜绝重复创建RequestManagerFragment而准备的。
    问题2:对于glide的get使用有什么优化的地方吗?
    答案:
  • 尽量不要在子线程中调用,因为子线程调用意味着生命周期是全局的,不能跟activity或fragment的生命周期同步
  • get的时候最好传activity、或者fragment,因为这样可以减少查找具体的fragment或者activity的步骤
    问题3:为什么要用RequestManagerFragment并与RequestManager进行关联呢?
    答案:是为了进行自动感应组件的生命周期
2、为什么要用RequestManagerFragment并与RequestManager进行关联?

步骤1中分析了get的流程中,fragment和activity最终是通过创建RequestManagerFragment并和RequestManager进行关联
我们看下RequestManagerFragment的几个关键生命周期函数:

@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的生命周期相关的函数调用lifecycle对象对应的方法,而lifecycle是在SupportRequestManagerFragment构造函数数中创建的:

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

ActivityFragmentLifecycle内部维持了生命周期的监听者列表:

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

当RequestManagerFragment生命周期变化时,调用ActivityFragmentLifecycle的对应生命周期方法,其实现就是遍历生命周期监听列表,并调用监听者对应的生命周期方法:

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

那么什么时候添加监听者到ActivityFragmentLifecycle呢?答案是创建RequestManager的时候,我们看下RequestManager构成函数:

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()) {
      //如果是自线程,则切换到主线程监听生命周期
      mainHandler.post(addSelfToLifecycle);
    } else {
      //监听生命周期
      lifecycle.addListener(this);
    }
    //生命周期变化时,相应的监听网络状态和取消监听网络装
    lifecycle.addListener(connectivityMonitor);

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

    glide.registerRequestManager(this);
  }

ok,最后我们总结下Glide是如何自动感应生命周期的:

  • 如果是在子线程,这Glide使用的RequestManager与Application一样的生命周期
  • 如果是fragment、activity,则使用FragmentManagerFragment与RequestManager进行关联,通过FragmentManagerFragment的生命周期变化来调度RequestManager对图片加载请求Request采取暂停、重新开始、停止等操作。
    因为Android有fragment、support包中的fragment、activity、surport中的activity,因为使用FragmentManagerFragment来实现RequestManager感应生命周期,这几个fragmengt、activity使用的FragmentManager各自不同,如果是fragment在用ChildFragmentManager或者support中的ChildFragmentManager;如果是activity则用FragmentManager或者support中的FragmentManager。所以RequestManagerRetriever主要用来根据framgnet、activity来创建对应的RequestManager。
    问题:Glide中最多有几个RequestManager?
    答案:5个,分别是:
  • 与Application生命周期一致的RequestManager
  • 与app包下activity的FragmentManager关联的RequestManager
  • 与app包下fragment的ChildFragmentManager关联的RequestManager
  • 与support包下activity的FragmentManager关联的RequestManager
  • 与support包下fragment的ChildFragmentManager关联的RequestManager
    问题:Glide的get操作有哪些优化点?
    答案:
  • 在UI线程中调用,可以避免RequestManager生命周期与Application的一直
  • 尽量采用support报下的fragment/activity;一是因为兼容性;二是如果统一使用support包下的可以避免创建于与app包下FragmentManager管理的RequestManager
  • get尽量传递fragment或者activity,这样可以减少通过view找到具体的fragment或者activity的步骤
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容