深入解析Glide生命周期管理

文章详细分析了Glide库中Fragment绑定的过程,强调了消息队列、Handler、RequestManagerFragment的角色,以及如何通过Lifecycle管理和网络/内存状态监控确保请求的唯一性和资源管理效率。
摘要由CSDN通过智能技术生成

}

synchronized (this) {

if (mDestroyed || mHost == null) {

throw new IllegalStateException(“Activity has been destroyed”);

}

if (mPendingActions == null) {

mPendingActions = new ArrayList();

}

mPendingActions.add(action);

if (mPendingActions.size() == 1) {

mHost.getHandler().removeCallbacks(mExecCommit);

mHost.getHandler().post(mExecCommit);

}

}

}

这里的mHost其实就是activity创建的,并且持有activity以及mMainHandler的引用,根据上述代码可以知道,其实绑定fragment的操作最终是通过主线程的handler发送消息处理的,我们假设这个消息为m2。然后**handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();这句代码发送的消息为m3。那么当Glide.with(this).load(url_1).into(mImageView_1);**这句代码执行这里时,消息队列有了变化:

但是m2这个消息并不会马上被处理,这是因为m1还有代码还没有执行完毕,也就是说这个fragment并不会马上被绑定,此时m1继续向下执行到第二句代码**Glide.with(this).load(url_2).into(mImageView_2);**当这句代码走到getRequestManagerFragment时,如果在m1时,我们不将fragment临时存储在pendingRequestManagerFragments中,由于m2还没有被处理,那么

RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

必然是找不到这个fragment的,那么就会导致重新创建一个新的重复的fragment,并开启事务绑定,这显然是不合情理的,因为Glide需要保证rootFragment的唯一性,rootFragment即fragment依附或者没有fragment依附的activity所创建的最上层RequestManagerFragment。

接着往下看RequestManagerFragment的构造方法做了什么。

public RequestManagerFragment() {

this(new ActivityFragmentLifecycle());

}

直接创建一个ActivityFragmentLifecycle,这个类实际是一个生命周期回调的管理类,实现了Lifecycle接口。所有的LifecycleListener会添加到一个集合中,当RequestManagerFragment生命周期方法触发时,会调用ActivityFragmentLifecycle相应生命周期方法,这个方法然后再遍历调用所有LifecycleListener的生命周期方法,以onStart生命周期方法为例,RequestManagerFragment中:

public void onStart() {

super.onStart();

lifecycle.onStart();

}

然后ActivityFragmentLifecycle中:

void onStart() {

isStarted = true;

for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {

lifecycleListener.onStart();

}

}

2.0.2 rootRequestManagerFragment

上面UML图上,可以知道RequestManagerFragment还有一个rootRequestManagerFragment的成员变量,Glide每创建一个RequestManagerFragment,都会尝试实例化rootRequestManagerFragment,这个fragment即顶级的Activity所创建的RequestManagerFragment,相关代码:

public void onAttach(Activity activity) {

super.onAttach(activity);

rootRequestManagerFragment = RequestManagerRetriever.get()

.getRequestManagerFragment(getActivity().getFragmentManager());

if (rootRequestManagerFragment != this) {

rootRequestManagerFragment.addChildRequestManagerFragment(this);

}

}

@Override

public void onDetach() {

super.onDetach();

if (rootRequestManagerFragment != null) {

rootRequestManagerFragment.removeChildRequestManagerFragment(this);

rootRequestManagerFragment = null;

}

}

可以看到,不管当前的RequestManagerFragment是通过何种方式创建的,都会在OnAttach时,拿到当前所绑定的Activity的FragmentManager来初始化一个RequestManagerFragment,这个RequestManagerFragment有可能是自身,有可能已经被初始化过了,比如是通过with(Activity activity)的方式初始化的,那么很显然

RequestManagerRetriever.get().getRequestManagerFragment(getActivity().getFragmentManager());

这句代码拿到的会是自己本身,而如果是通过with(Fragment fragment)的形式创建的,rootRequestManagerFragment将指向当前fragment绑定到Activity所绑定的RequestManagerFragment,如果该Activity没有绑定过,那么会开启事务绑定一个RequestManagerFragment。并且如果自己不是rootRequestManagerFragment的话,那么将会把自己保存到rootRequestManagerFragment中的一个集合:

private void addChildRequestManagerFragment(RequestManagerFragment child) {

childRequestManagerFragments.add(child);

}

简而言之,Glide会为Activity创建一个RequestManagerFragment做为rootFragment,并保存该Activity底下所有Fragment(如果有的话)所创建的RequestManagerFragment。

2.0.3 RequestManagerTreeNode

RequestManagerFragment初始化时,还会初始化RequestManagerTreeNode,顾名思义,这个类是用来保存请求树节点的,比如一个Activity采用Viewpager + Fragment的形式,而里面的Fragment又是一个ViewPager + Fragment的形式,这个时候,假设其中一个RequestManagerFragment生命周期方法走了,怎么知道哪些RequestManagerFragment绑定的LifeCycle应该得到调用呢?理想的情况是,应该让绑定该RequestManagerFragment的Fragment所有的子Fragment的RequestManagerFragment的生命周期得到调用,比如如下场景中,Activity中各有两个Fragment,两个Fragment又各有两个子Fragment,在所有Fragment中,均通过with(this)的方式来加载图片,经过之前的分析我们可以知道的是,ROOT RMF 中会保存有6个RMF(RMF即RequestManagerFragment):

当如果F1 RMF生命周期做出反应时,因为RequestManagerFragment是无界面的,所以可以理解为F1的生命周期做出反应。我们希望F11和F12所绑定的RequestManagerFragment也要立即做出反应。但是F2以及其底下的RequestManagerFragment则不应响应对应生命周期事件,我们知道任何一个RequestManagerFragment可以通过rootRequestManagerFragment拿到这6个RMF,继而拿到其所对应的RequestManager,那么怎么去确定F11 RMF 和 F12 RMF呢?这就是RequestManagerTreeNode干的事情了,RequestManagerFragment中的非静态内部类FragmentRequestManagerTreeNode实现了RequestManagerTreeNode:

private class FragmentRequestManagerTreeNode implements RequestManagerTreeNode {

@Override

public Set getDescendants() {

Set descendantFragments = getDescendantRequestManagerFragments();

HashSet descendants =

new HashSet(descendantFragments.size());

for (RequestManagerFragment fragment : descendantFragments) {

if (fragment.getRequestManager() != null) {

descendants.add(fragment.getRequestManager());

}

}

return descendants;

}

}

这个类做的事情比较简单,调用外部类RequestManagerFragment的方法getDescendantRequestManagerFragments拿到所有的“后裔”Fragment,然后再取出它的RequestManager,然后集合装起来返回,这里的后裔在前面的例子中,指的就是F11 RMF 和 F12 RMF,看看getDescendantRequestManagerFragments是怎么拿到的F11和F12:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)

public Set getDescendantRequestManagerFragments() {

//如果自己是rootFragment,那么直接返回childRequestManagerFragments

if (rootRequestManagerFragment == this) {

return Collections.unmodifiableSet(childRequestManagerFragments);

} else if (rootRequestManagerFragment == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {

// Pre JB MR1 doesn’t allow us to get the parent fragment so we can’t introspect hierarchy, so just

// return an empty set.

return Collections.emptySet();

} else {

HashSet descendants = new HashSet();

for (RequestManagerFragment fragment

//遍历取出rootFragment中的RMF,并获取到其parentFragment,找出后裔。
rootRequestManagerFragment.getDescendantRequestManagerFragments()) {

if (isDescendant(fragment.getParentFragment())) {

descendants.add(fragment);

}

}

return Collections.unmodifiableSet(descendants);

}

}

看看isDescendant方法是如何判断的:

private boolean isDescendant(Fragment fragment) {

Fragment root = this.getParentFragment();

while (fragment.getParentFragment() != null) {

if (fragment.getParentFragment() == root) {

return true;

}

fragment = fragment.getParentFragment();

}

return false;

}

依上面的例子,当遍历到F11 RMF时,参数传递过来的是F11,root 则为F1,F11再拿到parent,也是F1,返回true,F12 RMF类似也返回true;

当遍历到F21 RMF时,参数传入F21,root仍是F1,此时F21再怎么拿Parent也不可能是root,返回false。

简而言之,RequestManagerTreeNode用来获取绑定该RequestManagerFragment的Fragment的所有子Fragment所绑定的RequestManagerFragment所绑定的RequestManager

2.0.4 RequestManager

上面一直在说RequestManagerFragment,下面回到FragmentGet方法中,再贴一次,免得上翻麻烦:

RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {

//获取RequestManagerFragment,并获取绑定到这个fragment的RequestManager

RequestManagerFragment current = getRequestManagerFragment(fm);

RequestManager requestManager = current.getRequestManager();

if (requestManager == null) {

//如果获取RequestManagerFragment还没有绑定过RequestManager,那么就创建RequestManager并绑定到RequestManagerFragment

requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());

current.setRequestManager(requestManager);

}

return requestManager;

}

根据上面的UML图,可以知道RequestManager是一个非常核心的类,并且还实现了LifecycleListener来处理请求的生命周期。上述代码在创建RequestManager时,传递了3个参数,分别是context,前面分析过的初始化RequestManagerFragment所创建的LifeCycle和RequestManagerTreeNode。直接看RequestManager的构造函数:

public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {

this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());

}

调用的另一个构造方法,并增加了两个新的参数RequestTracker和ConnectivityMonitorFactory。

RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,

RequestTracker requestTracker, ConnectivityMonitorFactory factory) {

this.context = context.getApplicationContext();

this.lifecycle = lifecycle;

this.treeNode = treeNode;

this.requestTracker = requestTracker;

this.glide = Glide.get(context);

this.optionsApplier = new OptionsApplier();

ConnectivityMonitor connectivityMonitor = factory.build(context,

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()) {

new Handler(Looper.getMainLooper()).post(new Runnable() {

@Override

public void run() {

lifecycle.addListener(RequestManager.this);

}

});

} else {

lifecycle.addListener(this);

}

lifecycle.addListener(connectivityMonitor);

}

RequestTracker即所有请求操作的真正处理者,所有Request的暂停取消执行操作都由RequestTracker来完成,如RequestManager暂停请求的实现:

public void pauseRequests() {

Util.assertMainThread();

requestTracker.pauseRequests();

}

2.0.5 网络状态监测

请求生命周期的实现细节后面再说,暂时埋坑,先来看看ConnectivityMonitorFactory这个工厂生产了什么。

public class ConnectivityMonitorFactory {

public ConnectivityMonitor build(Context context, ConnectivityMonitor.ConnectivityListener listener) {

final int res = context.checkCallingOrSelfPermission(“android.permission.ACCESS_NETWORK_STATE”);

final boolean hasPermission = res == PackageManager.PERMISSION_GRANTED;

if (hasPermission) {

return new DefaultConnectivityMonitor(context, listener);

} else {

return new NullConnectivityMonitor();

}

}

}

很简单,接收一个ConnectivityListener根据是否有监控网络状态的权限来创建相应的网络监控器。

DefaultConnectivityMonitor也比较简单,就是内部定义了一个广播接收者,并且也实现了lifeCycleListener。在上面RequestManager的构造方法中,创建了一个RequestManagerConnectivityListener:

private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener {

private final RequestTracker requestTracker;

public RequestManagerConnectivityListener(RequestTracker requestTracker) {

this.requestTracker = requestTracker;

}

@Override

public void onConnectivityChanged(boolean isConnected) {

if (isConnected) {

requestTracker.restartRequests();

}

}

}

这个listener很简单,收到网络状态连接就重启请求。然后通过工厂创建出了DefaultConnectivityMonitor,并把它添加到了lifecycle中。到这里,Glide监测网络状态来重启请求的实现方式就呼之欲出了,大体步骤如下:

在相应的生命周期方法中,会调用lifecycle的生命周期方法,lifecycle会调用DefaultConnectivityMonitor所实现的相应生命周期方法来注册及解除注册网络状态的广播接收者,收到广播后,会回调之前传递的参数ConnectivityListener的onConnectivityChanged方法来处理Request。

2.0.6 内存状态监测

RequestManager中还存有Glide这个入口类的实例,构造方法中直接获取到的,用来对内存状态的变更作出处理,比较简单,看看流程便可以了,以onTrimMemory为例,

当RequestManagerFragment的onTrimMemory被调用时,会调用其绑定的RequetManager的相应方法来处理:

@Override

public void onTrimMemory(int level) {

// If an activity is re-created, onTrimMemory may be called before a manager is ever set.

// See #329.

if (requestManager != null) {

requestManager.onTrimMemory(level);

}

}

然后RequestManager再调用Glide入口类的trimMemory来释放更多内存:

public void onTrimMemory(int level) {

glide.trimMemory(level);

}

2.0.7 生命周期回调流程总结

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

面试复习路线,梳理知识,提升储备

自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。

关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

  • 架构师筑基必备技能
  • Android高级UI与FrameWork源码
  • 360°全方面性能调优
  • 解读开源框架设计思想
  • NDK模块开发
  • 微信小程序
  • Hybrid 开发与Flutter

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

Android开发七大模块核心知识笔记

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

个知识点进行了完美的总结:

[外链图片转存中…(img-kf5LDneK-1713680234649)]

《960全网最全Android开发笔记》

[外链图片转存中…(img-4LTzYN1x-1713680234650)]

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

[外链图片转存中…(img-gLmOjKht-1713680234650)]

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值