Chinaunix首页 | 论坛 | 博客
  • 博客访问: 22670
  • 博文数量: 35
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 356
  • 用 户 组: 普通用户
  • 注册时间: 2019-03-08 17:29
个人简介

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。

文章分类
文章存档

2020年(6)

2019年(29)

我的朋友

分类: Android平台

2019-12-11 10:22:53

本文首发于 vivo互联网技术 微信公众号 
链接: https://mp.weixin.qq.com/s/uTv44vJFFJI_l6b5YKSXYQ
作者:连凌能

Android App中图片的展示是很基本也很重要的一个功能,在Android平台上有很多的图片加载解决方案,但是官方认可的是Glide。Android App的页面是有生命周期的,Glide比较好的一个功能就是具有生命周期管理功能,能够根据页面和APP的生命周期来管理图片的加载和停止,也开放接口供用户在内存紧张时手动进行内存管理。本文重点是生命周期源码的分析,不会从简单的使用着手。

一、综述

这是Glide源码分析的第二篇文章,第一篇是 《Glide缓存流程》,从资源的获取流程对源码进行分析。本篇会聚焦于生命周期模块的原理。开始之前先思考下面这几个问题:

  • Glide怎么实现页面生命周期?

  • Glide为什么对Fragment做缓存?

  • Glide如何监听网络变化?

  • Glide如何监测内存?


二、Glide生命周期传递

先来看with函数的执行, 会构造glide单例,而 RequestManagerRetriever在initializeGlide中会进行构造。

构造完成RequestManagerRetriever通过get返回一个 RequestManager, 如果不在主线程,默认会传入 getApplicationContext,也就是不进行生命周期管理:

  • 在getRequestManagerFragment中先查看当前Activity中有没有FRAGMENT_TAG这个标签对应的Fragment,如果有就直接返回

  • 如果没有,会判断pendingRequestManagerFragments中有没有,如果有就返回

  • 如果没有,就会重写new一个,然后放入到pendingRequestManagerFragments中,然后添加到当前Activity,再给Handler发送一条移除的消息



这里面需要注意一个问题,就是如果with()函数中传进来的不是Activity,而是Fragment,那么也会去创建一个没有界面的RequestManagerFragment,而它的父Fragment就是传进来的Fragment。

上面为什么需要pendingRequestManagerFragments先进行缓存呢?这个放到下面第二个问题中说明。先接着往下看生命周期的传递。

RequestManagerFragment是一个很重要的类,Glide就是通过它作为生命周期的分发入口,RequestManagerFragment的默认构造函数会实例化一个ActivityFragmentLifecycle,在每个生命周期onStart/onStop/onDestroy中会调用ActivityFragmentLifecycle:

RequestManagerFragment里面有一个实例RequestManager,在前面的fragmentGet,RequestManagerFragment拿到以后会尝试获取它的RequestManager,第一次获取肯定是没有,就会重新构造一个, 通过RequestManagerRetriever构造时传入的RequestManagerFactory工厂类实例化一个RequestManager, 把RequestManagerFragment中的ActivityFragmentLifecycle传进去:


很明显生命周期的关键就在ActivityFragmentLifecycle, 在RequestManagerFragment中相应生命周期中会回调它,那么猜测它肯定是在里面维护了一个观察者列表,相应事件发生的时候进行通知, 看下它的源码:

所以RequestManagerFragment把这个传给RequestManager后,肯定会注册观察者,看一下RequestManager的相关代码,在构造函数里面lifecycle.addListener(this);,把自己注册为观察者:


在看下RequestManager对应的生命周期里面, 在这里面分别启动,停止和销毁请求:


三、Glide为什么对Fragment做缓存?

再贴一次RequestManagerRetriever中获取Fragment的代码,前面留了一个疑问,为什么这里会需要一个pendingRequestManagerFragments对Fragment进行缓存。

我们看一个情况:

Android开发应该都知道主线程有一个Handler机制,会往消息队列中放消息,通过Looper按顺序取出来执行。那么主线程中的执行顺序和消息队列中的执行顺序关系是什么?看个栗子:

在start中打印的顺序和它里面的Handler中的信息哪个先打印?start中handler的信息和Next函数中的信息打印顺序是怎样的?看下打印结果:

Handler中的顺序会在主线程之后,Handler中的消息执行顺序就是队列先进先出。

上面执行到task1的时候,在下面这两行代码,add操作会往消息队列放一个消息,这里标记为msg1:

那么如果不把task1中构造的RequestManagerFragment放到pendingRequestManagerFragments中,那么在执行task2的时候也会再重新构造一个RequestManagerFragment,并且往主线程中放一个消息msg2,这个时候就会出现重复add的情况。

所以在前面new 出来一个RequestManagerFragment,随后就把它放到pendingRequestManagerFragments中,那么task2再进来的时候从缓存中能取到,就不会再重新new和add了。

那么下一个问题来了,为什么会出现下面这行代码,add后又需要马上发一个消息remove掉?在前面阻止掉task2重复new和add的操作后,就把这个缓存删掉,可以避免内存泄漏和内存压力:

四、Glide如何监听网络变化

从上面页面生命周期的分析部分知道,对于任务的控制都是通过RequestManager,还是到它里面去看,实现网络变化监听的就是ConnectivityMonitor:

所以也是把它注册为ActivityFragmentLifecycle的观察者,ConnectivityMonitor通过ConnectivityMonitorFactory进行构造,提供了默认实现类DefaultConnectivityMonitorFactory:

接着就往下看DefaultConnectivityMonitor, 在onStart中registerReceiver监听手机网络状态变化的广播,然后在connectivityReceiver中调用isConnect进行网络状态确认,根据网络状态是否变化,如果有变化就回调监听ConnectivityMonitor.ConnectivityListener:


ConnectivityMonitor.ConnectivityListener是在RequestManager中传入,有网络重新连接后重启请求:


五、Glide如何监测内存

在Glide构造的时候会调用registerComponentCallbacks进行全局注册, 系统在内存紧张的时候回调onTrimMemory,然后根据系统内存紧张级别进行memoryCache/bitmapPool/arrayPool的回收:


六、总结

再回顾前面的四个问题,我相信聪明的你已经有了答案,文章的各小节标题就是根据问题来进行分析的,这么就不再赘述了,要不有凑字数的嫌疑。Glide的源码是比较庞大而且高质量的,所以一两篇文章是说不清楚的,后面对于Glide的源码分析还会有后续的文章,欢迎关注。

阅读(48) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册