13.Glide源码分析1(生命周期关联)
Glide.with(mContext).load("baidu.com") .centerCrop().into(imageView);
with方法
Glide进行的网络请求可以和当前页面(Activity或者Fragment)生命周期绑定在一起,当在某一个页面中开始请求网络图片时,假设此时突然按了退出键,那么Glide能监听到这个页面(Activity或者Fragment)的onStop方法,它会将当前所有正在进行但是尚未完成的Request请求暂停掉;同样道理,当页面再次回到前台时,它可以监听到页面的onStart方法,然后将所有当前尚未完成的,没有被取消的并且当前不是请求状态中的Request启动起来开始请求。那么Glide是如何左到这一点的?我们从源码角度寻找答案。
可以看到Glide的with方法可以接收五种类型的Context
public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context); } public static RequestManager with(Activity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); } public static RequestManager with(FragmentActivity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public static RequestManager with(android.app.Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment); } public static RequestManager with(Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment); }
我们以参数为Activity 的构造方法为例进行分析,因为他们原理都是相同的,进入这个方法中,我们来看看RequestManagerRetriever 是什么,它获取到了这个类以单利形式创建的对象
public static RequestManager with(Activity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); } private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever(); public static RequestManagerRetriever get() { return INSTANCE; }
进入get方法,这里对当前线程做了判断,我们先从主线程看起,等了解到为什么Glide能和Activity生命周期绑定,你就会知道,为什么子线程中使用Glide无法实现这一效果了。可以看到主线程中请求时,拿到了当前activity的FragmentManager,然后将二者都传入fragmentGet方法
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm); } }
fragmentGet,可以看到,with方法最终返回的是RequestManager,在方法中还创建了一个RequestManagerFragment 继承自Fragment,那么二者有什么关系,为什么要创建一个Fragment出来?
@TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, android.app.FragmentManager fm) { //根据当前activity获取到的fragmentManager创建出一个Fragment RequestManagerFragment current = getRequestManagerFragment(fm); //从这个fragment中获取到RequestManager,可见二者是绑定在一起的 RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { //将fragment中的Lifecycle传递到了RequestManger中,莫非这是和生命周期相关的 requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }
先看看这个Fragment怎么创建的,getRequestManagerFragment
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) { //由FragmentManager通过tag获取fragment,第一次的时候由于没有设置,会返回null RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { //再从缓存集合中获取fragment的,map以fragmentManger为 //key,以fragment为value进行存储,第一次请求也是null current = pendingRequestManagerFragments.get(fm); if (current == null) { //创建出来fragment,把它加入集合并且commit current = new RequestManagerFragment(); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); //可以看下边handleMessage中是把这个fragment又从刚刚 //加入的集合中移除了,不知何故,不管了不是重点 handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; } case ID_REMOVE_FRAGMENT_MANAGER: android.app.FragmentManager fm = (android.app.FragmentManager) message.obj; key = fm; removed = pendingRequestManagerFragments.remove(fm); break;
把这个fragment commit之后,当前activity的生命周期和这个fragment就绑定在了一起,那么它又是怎么回调到Glide的请求中的,我们可以看一下这个fragment创建的时候做了什么,创建fragment同时new了一个ActivityFragmentLifecycle对象,这个对象继承自Lifecycle这个接口。也就是说,当activity的onStart,onStop和onDestroy生命周期方法执行的时候,这个fragment相应的方法也会执行,然后就会调用这个ActivityFragmentLifecycle的onStart onStop onDestroy方法。那么此时可以猜想,应该就是这个LifeCycle将生命周期回调到Glide的请求中去的
public RequestManagerFragment() { this(new ActivityFragmentLifecycle()); } @SuppressLint("ValidFragment") RequestManagerFragment(ActivityFragmentLifecycle lifecycle) { this.lifecycle = lifecycle; } @Override public void onStart() { super.onStart(); lifecycle.onStart(); } @Override public void onStop() { super.onStop(); lifecycle.onStop(); } @Override public void onDestroy() { super.onDestroy(); lifecycle.onDestroy(); }
看一下lifecycle.onStart onStop onDestroy方法的具体实现,可以看到,这里面会通过LifecycleListener 这个接口将相应的方法回调了出去,那么我们只要找到设置这个接口的地方就可以找到接收这个生命周期回调的位置了,二者的关联关系也就清楚了
void onStart() { isStarted = true; for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStart(); } } void onStop() { isStarted = false; for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onStop(); } } void onDestroy() { isDestroyed = true; for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) { lifecycleListener.onDestroy(); } }
再回到fragmentGet方法中,创建fragment之后又创建了RequestManager,并且通过构造函数将lifeCycle传入了RequestManager中,我们重点看这里
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
进入构造方法中
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) { this(context, lifecycle, treeNode, new RequestTracker(), new 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. //在这里找到了给LifeCycle设置监听接口的位置,那么很明显, //这个RequestManager中一定重写了onStart onStop onDestroy方 //法,当fragment生命周期回调时,会调用到这里的方法,从而进行请 //求的开启与暂停 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); }
找到这三个回调方法,不出所料,生命周期的绑定就是这样进行的
/** * Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE * permission is present) and restarts failed or paused requests. */ @Override public void onStart() { // onStart might not be called because this object may be created after the fragment/activity's onStart method. resumeRequests(); } /** * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE * permission is present) and pauses in progress loads. */ @Override public void onStop() { pauseRequests(); } /** * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed * requests. */ @Override public void onDestroy() { requestTracker.clearRequests(); }
最后回到开始时做线程检测的位置,我们看到了主线程中请求网络的逻辑,那么子线程中有何不同
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { //子线程请求 if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm); } } public RequestManager get(Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); }
由于子线程传递的是Context,不满足 instanceof FragmentActivity context instanceof Activity context instanceof ContextWrapper这三个条件,所以最终是进入了getApplicationManager(context)方法中
private RequestManager getApplicationManager(Context context) { // Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { // Normally pause/resume is taken care of by the fragment we add to the fragment or activity. // However, in this case since the manager attached to the application will not receive lifecycle // events, we must force the manager to start resumed using ApplicationLifecycle. applicationManager = new RequestManager(context.getApplicationContext(), new ApplicationLifecycle(), new EmptyRequestManagerTreeNode()); } } } return applicationManager; }
我们可以看到这里也是创建了RequestManager,但是这个RequestManager和之前的不同之处就是传递到构造方法中的lifecycle实现类不同。源码的注释上也说的很清楚,由于在子线程中只能通过getApplicationContext获取到Context,而生命周期又是通过fragment进行关联的,如此一来,就无法获取到fragmentmanger,也无法创建fragment进行生命周期管理,所以在子线程中使用Glide或者在主线程中通过getApplicationContext获取上下文的情况都无法实现和Activity生命周期的绑定
总结一下
1.Glide要和当前请求的页面Activity或者Fragment生命周期绑定,必须传入当前Activity或者Fragment的实例,不能用Application的上文
2.Glide请求之所以能和页面的生命周期绑定,是通过创建一个和这个页面绑定的fragment实现的
3.当页面的生命周期执行时,fragment相应的生命周期也会执行,然后通过一个生命周期管理类,将其回调到RequestManger中,从而进行Request请求得开启和暂停
4.补充:有一点需要注意,虽然with方法允许在子线程中运行,但是into方法不可以,with方法在子线程多用于下载图片缓存到磁盘,并不能在子线程设置给控件进行展示
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
iOS快速清除全部的消息推送
前言 越来越多的应用,请求用户推送权限,一般情况下,普通应用我是不允许的,除了通讯应用、支付宝(银行app有很多不羊毛的活动推送,不允许)、GTD(Things、Due)、健身(Kepp、SixPack),其它的像淘宝天猫、京东,每次打开app,都要反复问我还要不要打开推送,你们就不能先调查一下我用的iOS系统多久吗?请问一个用了4年以上的用户,如果他还想打你们家的app推送,难道不知道在那里开吗?脑子进水吧。 iOS10 以上 如果是iOS10以上,并且是iPhone 6S、iPhone 6S plus ,就可以利用3D Touch一键清除通知。 IMG_C28272FF6B00-1.jpeg 如果是点击推送的 x ,就会出现 Clear,点击后会清除当前区的推送 IMG_24407F30BCFE-1.jpeg 如果长压(3D Touch),就出现 Clear All Notifications,点击就会清楚全部的推送 IMG_2184A87A7C29-1.jpeg iOS 9 如果是iOS 9 和更老的机型,首先按住锁屏上方的把手把通知中心拉下来,拖到底,松手,然后再向上把它拖回去...
- 下一篇
使用kotlin实现一个智能聊天机器人「图灵机器人,Android,kotlin」
推荐一部关于Ai的系列漫画,叫做代码的深渊 相关代码已经上传到Github的仓库kotlinRobot 先来看一下实现的效果图 智能机器人效果预览~1.gif 文章思路参考自刘桂林前辈的带领新手快速开发APP ,聊天界面代码参考于郭神的第一行代码第二版第三章3.7节,由衷感谢。自己也写过一篇讲解kotlin几个基础小知识点的文章,kotlin初体验,文中不足之处,希望能得到您的指正,十分感谢。 布局文件 创建我们的项目,勾选kotlin支持,并在build.gradle下填入依赖 implementation 'com.android.support:design:26.1.0' 然后我们进去res-values-styles下,把主题修改成没有标题的样式 <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Hadoop3单机部署,实现最简伪集群
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库