Glide 生命周期感知

2019-11-16  本文已影响0人  bevis0

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

一、场景的上下文

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

其中对于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 中进行驻留。可以实现两个好处:

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 需要维护几个内容

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

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

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

上一篇下一篇

猜你喜欢

热点阅读