Android Jetpack 是一套组件、工具和指导,可以帮助您快速构建出色的 Android 应用。
Google在17年的I/O大会上推出了架构组件(Architecture Component)。
随后在18年I/O大会上发布了 Android Jetpack,Jetpack 是Android开发组件工具集,旨在帮助我们轻松构建更稳定、更健壮、以及更可维护的应用程序。
紧接着Google推出AndroidX,将许多Google认为是正确的方案和实践集中起来了。
AndroidX 是对support library的重大改进。
AndroidX中的所有软件包名都以字符串androidx.开头,位于一致的命名空间中。
与support支持库不同,AndroidX中各个组件可单独维护和更新。
所有新的支持库开发都将在AndroidX库中进行。
目前很多组件库新版本都迁移到了androidx。比如Lifecycle2.0.0+、Paging2.0.0+、ViewPager2等,非官方库也积极响应,比如lottie2.8.0+版本之后就使用了androidx实现。由此可以得出,官方在已有组件新版本实现和全新组件的开发都将只支持androidx,所以尽快将自己的项目迁移到androidx吧。
Jetpack主要分为4个部分,基础、架构、行为、界面。从图中得知,Jetpack并不全是些新东西,只要是能够帮助开发者更好更方便地构建应用程序的组件,Google都将其归纳入了Jetpack,可以看出Google对jetpack很重视,对开发者很上心。
刚刚结束的Google I/O 2019大会上,Jetpack又迎来了新组件CameraX
、SavedStateViewModel
、Jetpack Compose
等等,提出Kotlin first ,并强调大部分新的Jetpack Apis和功能将会优先提供 Kotlin 版本。参考文章
每个 Jetpack 组件均可单独采用,并且它们可以流畅地协作。Android开发请一定关注和使用Jetpack+Kotlin。
本文重点是在架构组件的使用上,我们先来看看官方推荐的架构实现。
界面和交互:使用 Android 界面插桩测试。基于此架构只需mock 一个ViewModel
即可完成界面测试。
ViewModel:使用 JUnit 测试。只需mock一个类,即 Repository
。
Repository:使用 JUnit 测试。只需mock两个类,XxxDao,XxxService;由于XxxDao,XxxService都是接口,还可以创建虚拟实现来完成复杂测试用例。
XxxDao:可以使用插桩测试来测试 DAO 类。这里注意对于每个测试,都请创建内存中数据库以确保测试没有任何副作用(例如更改磁盘上的数据库文件)。
XxxService:就Retrofit而言可以使用MockWebServer模拟本地服务器。
评价一个架构好不好主要看三点:稳定性、易维护、可测试程度。
提到架构组件库,不得不说的是Lifecycle
库。文章后面部分就分别从该库中的Lifecycle、ViewModel、LiveData这三个类来简要分析其实现原理以及使用建议。
Lifecycle是一个类,它包含组件(Activity或Fragment)生命周期状态的信息,并允许其他对象观察此状态。
那lifecycle是如何跟踪组件生命周期的呢?
上图中states表示组件状态,events表示组件生命周期事件。其实Lifecycle代码内部使用了两个主要枚举(Event、State)来跟踪其关联组件的生命周期状态。
STARTED
和 RESUMED
为活跃状态,配合LiveData使用时,只有组件处于活跃状态才能接受到数据更新通知。实践示例:工具类LifecycleHandler,一个具有生命周期感知的Handler。
ViewModel 是用来保存应用UI数据的类,它会在配置变更(Configuration Change)后继续存在。
关于ViewModel的生命周期就一句话:在Activity、Fragment等组件整个生命周期过程中,ViewModel的实例有且只有一个。
可用ViewModel存储数据,它能安全度过手机旋转等配置变更场景。
ViewModel能很好的实现多个Fragment之间的数据共享。
如果界面和业务都比较复杂,ViewModel会不会爆掉?对于这种场景,官方也给出了解决思路:单一责任原则。
上图为官方中文视频截图,从单一责任原则考虑提出了实现建议。
Actvity或Fragment只显示UI和接收互动。
为避免ViewModel臃肿,可创建presenter处理UI数据。
Repository 数据源操作入口。(便于单元测试)
还可配合其它架构组件使用。
如何时候都不要将Context传入ViewModel。
如果要在ViewModel中使用Application实例,请使用AndroidViewModel类。
ViewModel+LiveData+Databinding 可构建反应式UI。(请查看文末提供的参考资料)
ViewModel与onSaveInstanceState要配合使用。
ViewModel | onSaveInstanceState |
---|---|
能度过配置变更 | 能度过配置变更和进程关闭 |
能存储大量数据 | 只可存储少量数据 |
xx | 必须序列化 |
其实ViewModel和onSaveInstanceState是相辅相成的,当进程被关闭时,ViewModel会被销毁,而onSaveInstanceState不会受影响,所以可用onSaveInstanceState存储少量关键数据(如userId),并在该场景恢复时用来加载页面数据。
在使用ViewModel时,如果页面仅仅是简单的展示数据没什么交互,一个LiveData就能轻松搞定,但实际情况是大多数页面复杂且交互多,就想着怎样更好的处理ViewModel和View之间的通信,直到看到了这篇文章,参考之后得出了下图实现。
该模型如何响应用户事件的?比如点击某个按钮,需要提交信息给server,并在成功响应后刷新UI,这个过程中ViewModel和View是如何通信的?这里简单描述下该过程,首先是Activity将更新事件传递给ViewModel,ViewModel有将其委托给Presenter处理,Presenter将处理状态和结果,通过给图中指定的LiveData设置数据,liveData就能将新数据回调给Activity,这样页面上所有操作就都能通过数据来驱动了。
LiveData是一个具有生命周期感知特性的可观察的数据保持类。
STARTED
or RESUMED
)的Observer更新,并在 DESTROYED
状态时自动移除Observers,来避免内存泄漏。用过RxJava的朋友都知道,它可以很方便地在Observable之间转换。LiveData也提供了类似的功能。
map : 将一种数据类型的LiveData
转换为另一种类型的 LiveData
// 示例代码:观察将被转换LiveData,待其数据源变更后转换为LiveData并通知订阅者。
LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
switchMap : 和map类似。差别在于triggerLiveData变更后,会触发和等待另外一个LiveData获取数据。
// 示例代码:将addressInputLiveData转换为postalCodeLiveData.
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveData addressInput = new MutableLiveData();
public final LiveData postalCode =
Transformations.switchMap(addressInput, (address) -> {
return repository.getPostCode(address);
});
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository
}
// addressInputLiveData变更时触发repository.getPostCode,
// 待其回去成功后,再将数据设置给postalCodeLiveData。
private void setInput(String address) {
addressInput.setValue(address);
}
}
以上为使用示例代码,其内部使用的MediatorLiveData
实现,较简单,感兴趣的朋友请自行查阅源码。
以上结论是我分别从lifecycle库1.x和2.x版本源码分析后得出。关于ViewModel的生命周期实现原理,各个版本实现略有不同,其中lifecycle2.x版本已改用Androidx实现,所以ViewModel的缓存实现还和androidx组件版本有关系。感兴趣的朋友,请自行查阅源码。
分析出现的原因
由于Fragment默认实现是在onDestroy才通知liveData 移除observers,而我们每次在onCreateView都会add新的observer实例,这样就会导致数据更新时,LiveData会同时通知多个Observer,界面就会快速刷新多次。
解决方案
onActivityCreated
方法中添加LiveData.observer(LifecycleOwner owner, Observer observer)
时,第一个参数使用Fragment.getViewLifecycleOwner()
方法返回值。(如果你没有找到该方法,请更新你依赖的support包版本,Google已在新版本中提供该方法)LiveData.observer(LifecycleOwner owner, Observer observer)
放在onCreate回调中,在Fragment显示时手动触发数据刷新,当然最好还是更新support版本来解决。Android Jetpack是一套组件开发工具集,旨在帮助我们轻松构建更稳定、更健壮、以及更可维护的应用程序。对于Google而言,推出Jetpack可以更好的管理和维护组件库;对开发者而言,使用Jetpack可以快速开发出高质量应用,也能看到官方在不同技术方案上的选择,以及新技术发展方向。从目前Jetpack组件布局来看,AndroidX、kotlin是需要我们使用和掌握的。
本文后半部分介绍了架构组件中lifecycle库的一些原理和最佳实践,但还不够全面深入,后面我会一一从源码角度分析各种组件实现原理,敬请关注。
既然是最佳实践,怎么能没有代码,这里分享下作者使用架构组件实现的项目代码,重点关注wanandroid
模块,其实现使用到了ViewModel+LiveData+Lifecycle+Room,按照官方推荐的架构实现,并完成各个组件独立的单元测试。
限于作者水平有限,文中定有错误和疏漏之处,恳请指出,与君共勉;若有不明白之处,欢迎随时评论交流,也可加我个人微信(jiantaocd),记得填写备注哦。