zoukankan      html  css  js  c++  java
  • 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框架的学习就到这,收获颇多~~

  • 相关阅读:
    Luogu P4727 [HNOI2009]图的同构记数
    ARC 101 E
    JSOI2019 Round2 游记
    JSOI2019 Round1(十二省联考)游记
    Technocup 2019
    Codeforces Round #533 (Div. 2)比赛总结
    学习链接
    2018.12.29-2018.1.9安师大附中集训
    关于考试
    NOIP2018提高组 游记
  • 原文地址:https://www.cnblogs.com/webor2006/p/12324357.html
Copyright © 2011-2022 走看看