Glide 生命周期感知

在框架中,经常会遇到内存泄漏问题,这就涉及到怎么去感知观测到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 嵌套时的关联沟通。属于承上启下适配器角色。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容