美文网首页
Glide 生命周期感知

Glide 生命周期感知

作者: bevis0 | 来源:发表于2019-11-16 19:50 被阅读0次

    在框架中,经常会遇到内存泄漏问题,这就涉及到怎么去感知观测到Activity / Fragment 的生命周期,从而根据该周期进行内存的管理。(大部分情况下这两种组合视图构件,能应对比较多的场景) ,对Glide中,提供了不错的监控方案。

    一、场景的上下文

    在Glide中是需要传入所需要感知的上下文信息,比如 with(Context)with(Fragment) 等,这里的上下文不局限于 Context,包括

    • Application
    • Fragment
    • Activity
    • View

    其中对于View的感知是借助于 View 所提供的上下文 View.getContext(这就要求View必须是Attached后的情况了),Glide将最终转化为 ApplicationActivity的感知,而对于嵌套Fragment无法获知,情况比较无解(因为无法从View中直接获得 Fragment的上下文信息,其他办法或许可以,比如标记后遍历获取ViewTree),建议更多的是根据实际情况选择传入的上下文对象。

    二、监控和监控器管理

    2.1 Fragment 为例

    with(Fragment) 开始入手,通过代码跟着可以进入到实际的管理类RequestManagerRetriever get(Fragment)中。

      @NonNull
      public RequestManager get(@NonNull Fragment fragment) {
        Preconditions.checkNotNull(
            fragment.getContext(),
            "You cannot start a load on a fragment before it is attached or after it is destroyed");
        if (Util.isOnBackgroundThread()) {
          return get(fragment.getContext().getApplicationContext());
        } else {
          FragmentManager fm = fragment.getChildFragmentManager();
          return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
        }
      }
      
    

    对于异步下去进行上下文 with 处理,由于可能已经发生泄漏,所以为了避免这个情况,Glide 默认将上下文重置为 Application 上。

    这里继续 supportFragmentGet

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

    两结合一起看,比较直观的可以知道 SupportRequestManagerFragment 是作为一个监控器的存在,寄宿于 FragmentMananger 中,负责对 Fragment 的生命周期进行监控。

    由于 commit 操作可能不是同步执行(因为commit 只会在主线程中执行,如果在异步中则post回主线程后执行),因此这里暂时暂存在 pendingSupportRequestManagerFragments ,防止重复创建添加 Fragment,需要等待主线程的 handler 完成回调才能从 pendingSupportRequestManagerFragments 移除(因为这时候已经commit 成功了)。

    Glide 在 SupportRequestManagerFragment 也提供了一套生命周期订阅器, 如果当前 Fragment 已展示,则补充对该订阅者们的 onStart()事件

    另外针对非 Support 下 Fragment 的 兼容,可以忽律,目前google也不再维护,只作为补充兼容来看,具体内容以下:

      private android.app.Fragment findFragment(@NonNull View target, @NonNull Activity activity) {
        tempViewToFragment.clear();
        findAllFragmentsWithViews(activity.getFragmentManager(), tempViewToFragment);
    
        android.app.Fragment result = null;
    
        View activityRoot = activity.findViewById(android.R.id.content);
        View current = target;
        while (!current.equals(activityRoot)) {
          result = tempViewToFragment.get(current);
          if (result != null) {
            break;
          }
          if (current.getParent() instanceof View) {
            current = (View) current.getParent();
          } else {
            break;
          }
        }
        tempViewToFragment.clear();
        return result;
      }
    

    简单的说,Glide 通过将自己实现的Fragment 添加到相应目标下的 FragmentManager 中进行驻留。可以实现两个好处:

    • 依赖系统提供回调,可以对上下文对象的生命周期进行观察
    • 依赖FragmentManagerChildFragmentManagerFragment的管理,监视器Fragment不需要自己维护,可跟随上下文对象生命周期的结束自动释放,无需担心内存泄漏

    2.2 Activity

    有了以上的大概描述,就不难理解Activity是怎么追踪的,因为在Actvity 的FragmentManagerSupportFragmentActivity)的 Fragment也是可以感知当前宿主的生命周期,所以同理,在对应的Manager 中 add 如自己的监控器SupportRequestManagerFragment

    2.3 Application

    另外这里面比较特殊的是 Application 上下文对象的处理,由于跟随的是整个 App 的周期,因此无需担心内存泄漏问题。因此在全局的RequestManagerRetriever 中单独维护了一个全局的 RequesterManager

    public class RequestManagerRetriever implements Handler.Callback {
     ...
     private volatile RequestManager applicationManager;
     ...
    }
    

    三、监控器中的维护

    SupportRequestManagerFragment 需要维护几个内容

    • 维护自身及子Fragment的响应,RequestManagerTreeNode 提供自身向下SupportRequestManagerFragment 子节点的 RequestMananger 的沟通,对于经常看到的场景,我们在移除父Fragment后,同时也需要影响子 RequestManager的内容
    • 提供ActivityFragmentLifecycle观察者注册,暴露周期观察接口,为其他功能观察当前上下文的生命周期提供入口。

    SupportRequestManagerFragment 并不直接负责 RequestManger的调用,只是提供构造和存在。

    SupportRequestManagerFragment 会在Fragment 发生 Attach 后,执行 root Fragment 绑定,维护父子节点的的关联关系(rootRequestManagerFragmentchildRequestManagerFragments

    基本上 SupportRequestManagerFragment 是作为该上下文中所独有内容的贮存的存在,并且维护着该上下文可能存在的关联关系,比如多个Fragment 嵌套时的关联沟通。属于承上启下适配器角色。

    相关文章

      网友评论

          本文标题:Glide 生命周期感知

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