Ⅰ Android-ViewModel原理解析
在这四个方法中,其实唯一的区别就是要不要传Factory,当没有传自定义的Factory的时候,则会传入默认的Factory,我们看ViewModelProvider构造器的源码和部分of方法的源码:
在ViewModelProvider中需要传入一个VieModelStore对象,这个对象是由ViewModelStoreOwner来提供的,而在Activity或者Fragment中,是由Activity和Fragment来提供的,因为ViewModelStoreOwner是一个接口,而AppCompatActivity的祖父ComponentActivity和Fragment均实现了ViewModelStoreOwner接口。
但是ViewModelProviders在新的lifecycle-extensions库中,已经是属于被弃用的。新版的API直接使用ViewModelProvider,而不是ViewModelProviders。
比如:
可以如下的方式在baseActivity中添加,由子类Activity调用:
创建ViewModel对象,首先就需要先初始化一个ViewModelProvider对象
可以看出,ViewModelProvider构造函数其实最终都是需要两个参数,一个是ViewModelStoreOwner对象,一个是Factory。而ViewModelStoreOwner其实就是用来获取一个ViewModelStore对象来保存ViewModel对象的。而Factory就是用来创建ViewModel对象的。
这个接口的主要实现的作用就是返回一个ViewModelStore对象。在Android中,Activity和Fragment都会实现该接口,并且实现getViewModelStore()方法。
比如Activity就是在FragmentActivity的父类ComponentActivity中实现了ViewModelStoreOwner接口
Fragment的ViewModelStore其实是由FragmentManager进行管理获取
每个FragmentActivity都会有一个自己的FragmentManager对象,所以每个FragmentManagerViewModel对象,管理的是一个FragmentActivity中的所有的Fragment对应的ViewModel。具体看FragmentManagerViewModel的getViewModelStore方法
从这里可以看出,每个Fragment都会有自己的ViewModelStore对象,而ViewModelStore对象,是根据每个Fragment的唯一标识进行创建的。
ViewModelStore类对象,是每个Activity或者Fragment都有一个,目的是用于保存该页面的ViewModel对象,方便ViewModel的管理
从ViewModelProvider的get方法中,可以看出,get方法传入的是一个ViewModel.class的Class类型,然后通过这个类型,得到ViewModel的规范名称。将ViewModel对象缓存在ViewModelStore中的HashMap中。而ViewModel的创建,其实是通过ViewModelProvider.Factory来实现的
ViewModelProviders的of方法,用于返回一个ViewModelProvider对象
从这里我们可以看到,如果传入的Activity或者Fragment有方法实现,而factory为null的时候,则会通过创建对应的Factory,而如果没有的实现,那么就会调用NewInstanceFactory来创建对应的Factory,而NewInstanceFactory其实就是创建AndroidViewModelFactory对象。
最终ViewModel对象,其实就是通过AndroidViewModelFactory的create的方法实现来创建。一般就是通过Class.newInstance或者Class.getConstructor来创建对象。
而ViewModelProvider的第一个参数,其实最终传入的是ViewModelStore对象,这个对象内部是通过一个HashMap来保存ViewModel对象
而新版的源码,ViewModelStore对象是通过Fragment和FragmentActivity对象的getViewModelStore方法来获取,而原先的HolderFragment的功能都移植到了Fragment中
HolderFragment通过设置setRetainInstance(true),使得自身能够不受到屏幕旋转等configuration
changes影响而存活,直到依附的Activity正常结束。
因为HolderFragment的生命周期,ViewModelStore对象保存在HolderFragment中,而ViewModel又存储在ViewModelStore中,这就是为什么我们说ViewModel类能够让数据在屏幕旋转等配置信息改变导致UI重建的情况下不被销毁。
ViewModelProvider的get方法:
在ViewModel中,有两种Factory,Factory是的类型是由ViewModelProvider在初始化的时候创建的,所以是由ViewModelProvider决定Factory的类型。在ViewModelProvider中,有两种Factory,一种是默认的Factory,默认的Factory是通过在ComponentActivity或者Fragment中实现接口,然后在()方法中初始化一个SavedStateViewModelFactory对象;另一种Factory则是NewInstanceFactory,这种是通过NewInstanceFactory.getInstance()的单例方式获取。
其实就是通过ViewModel的Class对象,然后通过反射创建ViewModel对象,然后保存到ViewModelStore中的Map集合中
从ViewModelProvider的get方法可以看出,在ViewModelProvider的get方法中会根据Factory的类型,进行不同方法的调用。SavedStateViewModelFactory是实现了ViewModelProvider.KeyedFactory接口的,所以在创建ViewModel的时候,调用的是SavedStateViewModelFactory的create方法。
AndroidViewModel和ViewModel的构造器参数Class
ViewModel保存和恢复数据
ComponentActivity和Fragment都将数据的保存和恢复逻辑转发给了SavedStateRegistryController。在在onCreate方法里通过调用performRestore来恢复数据,在onSaveInstanceState方法里通过调用performSave来保存数据。而SavedStateRegistryController中的SavedStateRegistry对象,就是实际进行数据的保存和恢复的,在SavedStateRegistry通过唯一的key获取到一个SavedStateProvider,而SavedStateProvider其实就是返回需要保存的数据,将对应的需要缓存的数据一一返回,然后保存在系统缓存时的回调到onSaveInstanceState的方法参数Bundle中进行保存。
SavedStateRegistry.performSave()
该方法是由ComponentActivity的onSaveInstanceState方法触发调用SavedStateRegistryController的performSave,进而调用的
在SavedStateRegistry恢复数据的时候,会把恢复后的数据都交给SavedStateHandle。希望保留的数据,可以通过两种方式向mRegular保存数据。
在ComponentActivity恢复数据的时候,会通过SavedStateRegistryController.performSave在Activity的onSaveInstanceState方法中进行数据的保存,然后在ComponentActivity的onCreate方法中,通过调用SavedStateRegistryController.performRestore方法进行数据的恢复,这些恢复的数据都会保存在SavedStateHandleController对象中的SavedStateHandle属性中,然后在Activity重新创建的时候,会通过反射创建对应的ViewModel对象的时候,将SavedStateHandleController中的SavedStateHandle赋值给对应的ViewModel进行数据恢复。
这块的源码分析可以参考:
从源码看 Jetpack(7)-SavedStateHandle源码详解
这里其实就是直接使用Class的newInstance直接创建对象。Activity和Fragment一般都是使用SavedStateViewModelFactory创建ViewModel对象。
ViewModel的销毁,要分为Activity和Fragment两部分。
首先看下ViewModel在销毁的时候做的事情
而ViewModel的clear()方法的调用,是在ViewModelStore中
Activity的销毁,是通过Lifecycle监听生命周期回调,当生命周期执行到onDestroy的时候,调用ViewModelStore的clear()方法进行ViewModel的销毁。
看ComponentActivity中构造器中的实现:
Fragment的生命周期管理,如下:
Fragment的生命周期,首先会依次增大,然后在从onResume变成onPause的时候,就开始状态码减小。即先升再降的一个状态变化。在当前状态码变成CREATED的时候,就会执行onDestroy。即调用
FragmentStateManager.destroy
在这里就会调用nonConfig.clearNonConfigState方法,nonConfig其实就是FragmentManagerViewModel对象。
FragmentManagerViewModel.clearNonConfigState
按照上面的逻辑,在Activity重建时会执行destory生命周期事件,那么为什么ViewModel没有销毁呢?
其实就是在屏幕旋转的时候,AMS通过Binder回调Activity的()方法,这个时候就会进行数据的保存,保存到一个NonConfigurationInstances对象;而在屏幕翻转结束之后,会再一次调用ViewModelProvider的构造函数,此时就会调用owner.getViewModelStore(),接着就会调用(),这里就会通过Activity中的NonConfigurationInstances对象取出保存的ViewModelStore对象。
所以数据保存就是通过()方法保存在NonConfigurationInstances对象,而再一次使用取出ViewModel的数据的时候,就是从nc对象中取出ViewModelStore对象,而ViewModelStore对象保存有ViewModel集合
通过对ComponentActivity的getViewModelStore()方法进行分析。可以找到这个问题的答案。
当mViewModelStore为null的时候,会从NonConfigurationInstances中获取ViewModelStore对象。
其实在ComponentActivity和Activity中都会有一个NonConfigurationInstances类,而Activity中的NonConfigurationInstances类结构如下:
这里的Object activity其实就是保存的ComponentActivity中的NonConfigurationInstances类对象,看Activity的下面的方法:
activity这个Object对象,其实是通过()方法返回值赋值,而()方法的实现是在ComponentActivity中。
看ComponentActivity中的下面方法:
因为这里会在ComponentActivity中的NonConfigurationInstances类对象中保存ViewModelStore对象,所以这也是Activity重建时不会销毁ViewModel的原因。
()方法除了被Activity的()调用以外,还会被LocalActivityManager的()方法调用
在分析ViewModel的销毁过程时,我们看到Activity与Fragment存储VieModel是分离的,那么同一个Activity下的Fragment是如何共享ViewModel的呢?
其实共享的是Activity的ViewModel。
而具体的实现逻辑,其实就是在FragmentViewModelLazy.kt中的:
在Fragment中可以直接调用,这是一个Fragment的扩展函数,通过实现requireActivity().viewModelStore,获取到了Activity的ViewModelStore对象后,这样就可以实现了Fragment共用Activity的ViewModel,从而实现了Fragment之间共享ViewModel。
Fragment之间共享ViewModel,需要引入
Ⅱ Android大厂面试经验分享(OPPO,字节,华为,阿里)
我是从小公司跳出来的,最终入职OPPO,说实话这段时间的经历让我深深地感受到,我们为跳槽做的一些临时抱佛脚的提升跟那些大佬的沉淀比起来太渺小了。我们都知道找资料学习、刷面试题,但也许只能应付这一次的面试,后面还是会技术发愁,那些短时间背下来的东西迟早会忘掉, 大家还是做好长期提升自己的准备,好好沉淀的东西最后才是属于自己的。
说说当时的面试过程,我是内推获得的面试机会,很感谢当时帮我内推的兄弟,总共三轮面试,两轮技术,一轮HR面,当天面试结束。
我10:10分到的公司,10:30开始面试,第一轮面试将近一个小时,聊的点我基本上都答得上来,自我感觉良好。然后面试官让我等一下,他去叫他们老大来给我二面,我等了有二十几分钟吧,二面有一个多小时,这次问的比较深,有些地方答的有些嗑吧,总体来说我自己是满意的。HR面约到下午了,整个流程下来每轮面试官都让人感觉很不错,我自己做的准备也让我面试感觉下来很爽。
我把面试遇到过的以及自己学习用到过相关内容都整理到一起了,方便自己进行复盘和后续的查漏补缺:
一、 java基础
1.1 静态内部类和非静态内部类的比较
1.2 多态的理解与应用
1.3 java方法的多态性理解
1.4 java中接口和继承的区别
1.5 线程池的好处,详解,单例(绝对好记)
1.6 线程池的优点及其原理
1.7 线程池的优点(重点)
1.8 为什么不推荐通过Executors直接创建线程池
1.9 不怕难之BlockingQueue及其实现
1.10 深入理解ReentrantLock与Condition
1.11 Java多线程:线程间通信之Lock
1.12 Synchronized 关键字原理
1.13 ReentrantLock原理
1.14 HashMap中的Hash冲突解决和扩容机制
1.14 Java并发
1.15 Java虚拟机
1.16 JVM常见面试题
1.17 JVM内存结构
1.18 类加载机制/双亲委托
二、 Android基础
2.1 Activity知识点(必问)
2.2 Fragment知识点
2.3 Service知识点
2.4 Intent知识点
2.5 数据存储
三、UI控件篇
3.1 屏幕适配
3.2 主要控件优化
3.3 事件分发与嵌套滚动
3.4 动态化页面构建方案
四、网络通信篇
4.1 网络协议
五、架构设计篇
5.1 MVP架构设计
5.2 组件化架构
六、性能优化篇
6.1 启动优化
6.2 内存优化
6.3 绘制优化
6.4 安装包优化
七、源码流程篇
7.1 开源库源码分析
7.2 Glide源码分析
7.3 day 20 面试题:Glide面试题
7.4 聊一聊关于Glide在面试中的那些事
7.5 面试官:简历上如果写Glide,请注意以下几点…
7.6 Glide OOM问题解决方法汇总
7.7 LeakCanary源码分析
7.8 OkHttp源码分析
7.9 okhttp连接池复用机制
7.10 okhttp 流程和优化的实现
7.11 一篇让你受用的okhttp分析
7.12 OkHttp面试之–OkHttp的整个异步请求流程
7.13 OkHttp面试之–HttpEngine中的sendRequest方法详解
7.14 OkHttp解析大总结
7.15 Okhttp任务队列工作原理
7.16 Android高频面试专题 - 架构篇(二)okhttp面试必知必会
7.17 Android 网络优化,使用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成
7.18 Retrofit源码分析
7.19 RxJava源码分析
7.20 RxJava原理与源码分析
7.21 RxJava如何进行线程切换的?
7.22 Rxjava内存泄漏防止方案——RxLifecycle,AutoDispose,RxLife框架
7.23 Tinker源码分析
7.24 ARouter源码分析
7.25 Android框架层源码解析
7.26 算法设计
八、新技术篇
8.1 实战问题篇
九、面试篇
9.1 开源文档
9.2 面试文献
以上就是我的学习和面试积累,有自己面试经历过的,也有整理的一些大厂面试题,篇幅有限,具体内容就不展示了,我已经整理成文档了。
还是开头说的,仅靠面试期间临时抱佛脚和刷题对自身发展不是长久之计,做好长期提升的规划,好好沉淀每一次的学习和面试经历,把这些最终都转化成属于自己的东西才是实质上对自己最有用的。
Ⅲ 如何设计app的架构
想要设计App的整体框架,首先要清楚我们做的是什么
一般我们与网络交互数据的方式有两种:主动请求(http),长连接推送
结合网络交互数据的方式来说一下我们开发的App的类型和特点:
数据展示类型的App:特点是页面多,需要频繁调用后端接口进行数据交互,以http请求为主;推送模块,IM类型App的IM核心功能以长连接为主,比较看重电量、流量消耗。
手机助手类App:主要着眼于系统API的调用,达到辅助管理系统的目的,网络调用的方式以http为主。
游戏:一般分为游戏引擎和业务逻辑,业务脚本化编写,网络以长连接为主,http为辅。
一般我们做的App都是类型1,简要来说这类app的主要工作就是
把服务端的数据拉下来给用户展示
把用户在客户端修改的数据上传给服务端处理
所以这类App的网络调用相当频繁,而且需要考虑到网络差,没网络等情况下,App的运行,成熟的商业应用的网络调用一般是如下流程:
UI发起请求 - 检查缓存 - 调用网络模块 - 解析返回JSON / 统一处理异常 - JSON对象映射为Java对象 - 缓存 - UI获取数据并展示
这之中可以看到很明显职责划分,即:数据获取;数据管理;数据展示
确定了职责,就可以进入正题了
1. 传统的Android App架构
Android最原生也是最基础的架构,可以理解为MVC,Controller即是Activity和Fragment,但是这两者掌握了Android系统中绝大多数的资源,并且在内部直接控制View,因此传统的Android App一般是以Activity和Fragment为核心,将网络模块,数据库管理模块,文件管理模块,常用工具类等分离成若干工具类包,供Activity和Fragment使用。