Glide框架设计<四>-------生命周期管理、Glide框架整合源码分析
接着上一次https://www.cnblogs.com/webor2006/p/12324332.html的源码继续分析。
Glide框架源码分析:
Request.begin():
在上一次中已经分析到了这:
也就是准备要开始发起请求了:
那这个engine是在哪创建的呢?
而这个GlideContext是在构建的:
Engine.load():
好继续回到主流程:
首先从活动资源中来查找,如之前我们手写的流程一要,如果找到,则会回调onResourceReady方法,咱们看一下它回调的处理:
嗯,整个流程则通了,接下来继续回到主流程:
下面具体来看一下:
原来DecodeJob是一个线程,那很明显这个EngineJob肯定有线程池,为啥?
能启动线程基本上都会用线程池的,跟进去验证一下:
妥妥的,接下来直接来看一下加载线程执行的逻辑。
DecodeJob.run():
看一下生成器的构建:
很明显首先是拿磁盘加载器来加载,为啥?
所以开始来使用一下这个生成器:
其中咱们注册了一个Bitmap解码器,从InputStream到Bitmap,如下:
看一下这个解码器的大致内容:
好,回到主流程:
其实它里面就是判断有没有注册解码器:
而它则是我们在注册机中添加的解码器:
好,回主流程继续往下分析:
而此时cb是在它的构造方法中来传过来的:
所以最终的回调会到DecodeJob,如下:
此时再往下回调:
以上是在磁盘缓存中获取到了图片,但是!!假如没有获取呢?接下来则就需要加载原始图了。
SourceGenerator:原数据生成器
此时又回到DecodeJob.run()方法中:
而它里面的逻辑跟DataCacheGenerator差不多,只是加载器不一样了而已,如下:
此时的Loader则为:
接下来的回调流程就跟之前分析的那个是一样的了,就不再分析了。至此整个关于Glide的加载主流程就分析完了,要是手写的话还真得挺难的,先把主流程搞清楚。
生命周期管理:
关于Glide生命周期的管理在上面分析Glide的整体加载流程时也提及到了,它内部其实是用了一个Fragment来进行管理的,但是只是提及了一下分析得不够详细,所以接下来再彻底的分析一下这块,毕境这块也是非常重要的,先贴一下整个Glide生周周期相关的类图:
下面则以此为蓝本进行分析,先来从头来定位到生命周期的源代码处:
我们只来关注是在FragmentActivity中使用情况下的生命周期,好继续往下:
这里有一个很奇怪的代码:
这个在博主这块https://www.jianshu.com/p/14dbc0c609ec有详细说明,这里直接贴出来理解一下:
这里有个问题,为什么需要使用pendingRequestManagerFragments这样一个集合来临时存储一下fragment,然后又马上通过handler发送消息移除?这其实是跟主线程的Looper机制和Fragment的事务机制有关的。我们知道,android中的主线程是一个闭环,通过Handler发送消息到MessageQueue,然后通过Looper轮询获取消息并交给Handler处理。如下面一个常见场景:
Glide.with(this).load(url_1).into(mImageView_1); Glide.with(this).load(url_2).into(mImageView_2);
这段代码通过Glide加载了两张图片并设置到了两个ImageView上,当以上代码块执行时,其所属的代码群的Message刚刚从MessageQueue中取出正在被处理,我们假设这个Message为m1,并且这个MessageQueue中没有其他消息。此时情形是这样的:
当代码执行到getRequestManagerFragment这个方法时,会通过开启事务的方式来绑定这个fragment到activity,相关源码如下,这个方法在FragmentManagerImpl.java中:
public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } 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。
也就是说:
上面的策略也就保证了在FragmentActivity中只有一个供Glide管理生周期的Fragment,不会生成多个,另外对于Fragment提交方式除了上图所用的:
还可以用:
这俩有啥区别呢?对于commit()来说是立马提交,但是如果出现提交失败的情况则会造成应用崩溃;而commitAllowingStateLoss()它允许处理消息丢失,但是不会造成应用崩溃的。
好,接下来看一下SupportRequestManagerFragment:
这个类就是生命周其的监听类,然后它里面就有添加监听的方法:
另外它实现了一个Lifecycle的接口:
而注册生命周期的监听在这:
也就是如果不是在前台了则所有图片的请求都会停止掉,而如果再回到前台时又会恢复请求,真心觉得这块还是挺复杂的,关于Glide的生命周期核心原码就分析到这了。
FAQ:
1、既然你已经看过了Glide的源码,那你分析过来印象最深刻的是啥呢?
缓存机制。
2、那Glide中到底都有哪机缓存机制呢?
三大缓存:弱引用缓存(活动资源)、LRU内存缓存、磁盘缓存。
3、Glide中的各缓存读取图片和写入图片的顺序又是什么样的呢?
写入:弱引用(活动资源)----->LRU内存缓存------>磁盘缓存。
读取:LRU内存缓存----->弱引用(活动资源)------>磁盘缓存。
4、如果叫你来设计Glide这样的框架,打算怎么设计呢?
大致可以这样:
好,关于Glide框架的学习就到这,收获颇多~~