在框架中,经常会遇到内存泄漏问题,这就涉及到怎么去感知观测到Activity / Fragment 的生命周期,从而根据该周期进行内存的管理。(大部分情况下这两种组合视图构件,能应对比较多的场景) ,对Glide中,提供了不错的监控方案。
一、场景的上下文
在Glide中是需要传入所需要感知的上下文信息,比如 with(Context)
,with(Fragment)
等,这里的上下文不局限于 Context,包括
- Application
- Fragment
- Activity
- View
其中对于View的感知是借助于 View 所提供的上下文
View.getContext
(这就要求View必须是Attached后的情况了),Glide将最终转化为Application
或Activity
的感知,而对于嵌套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
中进行驻留。可以实现两个好处:
- 依赖系统提供回调,可以对上下文对象的生命周期进行观察
- 依赖
FragmentManager
、ChildFragmentManager
对Fragment
的管理,监视器Fragment
不需要自己维护,可跟随上下文对象生命周期的结束自动释放,无需担心内存泄漏
2.2 Activity
有了以上的大概描述,就不难理解Activity是怎么追踪的,因为在Actvity 的FragmentManager
(SupportFragmentActivity
)的 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 绑定,维护父子节点的的关联关系(rootRequestManagerFragment
、childRequestManagerFragments
)
基本上 SupportRequestManagerFragment
是作为该上下文中所独有内容的贮存的存在,并且维护着该上下文可能存在的关联关系,比如多个Fragment 嵌套时的关联沟通。属于承上启下适配器角色。
网友评论