Jetpack笔记
Posted fakerXuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack笔记相关的知识,希望对你有一定的参考价值。
Jetpack笔记
什么是Jetpack
Jetpack是众多优秀组件的集合。是谷歌推出的一套引领android开发者逐渐统一开发规范的架构
Jetpack的优势
- 基于生命周期感知的能力,可以减少NPE崩溃、内存泄露、模板代码。为我们开发出健壮且高质量的程序提供强力保障
- 组件可以单独使用,也可以搭配使用,搭配Kotlin语言特性可以进一步加速开发。
Jetpack组件库介绍
- Navigation
- Lifecycle
它持有组件(如Activity Fragment)生命周期状态的信息,并且允许其他对象观察此状态- 添加依赖
- 添加依赖
- ViewModel:具备生命周期感知能力的数据存储组件
页面配置更改数据不丢失、生命周期感应、数据共享
- ViewModel的数据存储、生命周期感知
- ViewModel的数据共享
- LiveData:具备生命周期感知能力的数据订阅、分发组件
- Room:轻量级ORM数据库,本质上是一个SQLite抽象层
- Room数据库读写
- DataBinding:databinding只是一种工具,它解决的是View和数据之间的双向绑定。MVVM是一种结构模式,两者是有本质区别的。
- 布局中绑定数据
- WorkManager:新一代后台管理组件,功能十分强悍。Service能做的事情它都能做
- 执行任务
使用Jetpack架构开发模式
LIfecycle架构组件原理解析
Lifecycle是什么?
持有组件(如Activity Fragment)生命周期状态的信息的组件,并且允许其他对象观察此状态
Lifecycle如何使用
- FullLIfeCycle人Observer
- LifecycleEventObserver
- Fragment实现LIfecycle
- LIfecycleOwner、LIfecycle、LIfecycleRegistry的关系
- Activity实现LIfecycle
- ReportFragment核心源码
- LIfecycleRegistry
- 宿主生命周期与宿主状态模型图
- 添加observer时,完整的生命周期事件分发
LIveData架构组件原理解析
什么是LiveData
- Handler与LiveData对比
LiveData的几种用法
- MutableLiveData
- MediatorLivedata
- Transformations.map操作符
LiveData核心方法
LiveData实现原理
- 黏性消息分发流程
- 普通消息分发流程
LiveData实战与应用
- 需求分析:基于LiveData打造一款不会内存泄露,不用反注册的消息总线。且支持黏性事件
- 疑难解惑
- 控制黏性事件的突破口在于观察者的version字段
- 支持黏性事件订阅、分发的StickyLiveData
- 代码实现
import androidx.lifecycle.*
import java.util.concurrent.ConcurrentHashMap
object HiDataBus {
private val eventMap = ConcurrentHashMap<String, StickyLiveData<*>>()
fun <T> with(eventName: String): StickyLiveData<T> {
//基于事件名称 订阅、分发消息,
//由于 一个 livedata 只能发送 一种数据类型
//所以 不同的event事件,需要使用不同的livedata实例 去分发
var liveData = eventMap[eventName]
if (liveData == null) {
liveData = StickyLiveData<T>(eventName)
eventMap[eventName] = liveData
}
return liveData as StickyLiveData<T>
}
//通过一堆的反射,获取livedata当中的mversion字段,来控制黏性数据的分发与否,但是我们认为这种反射不够优雅。
class StickyLiveData<T>(private val eventName: String) : LiveData<T>() {
internal var mStickyData: T? = null
internal var mVersion = 0
fun setStickyData(stickyData: T) {
mStickyData = stickyData
setValue(stickyData)
//就是在主线程去发送数据
}
fun postStickyData(stickyData: T) {
mStickyData = stickyData
postValue(stickyData)
//不受线程的限制
}
override fun setValue(value: T) {
mVersion++
super.setValue(value)
}
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
observerSticky(owner, false, observer)
}
fun observerSticky(owner: LifecycleOwner, sticky: Boolean, observer: Observer<in T>) {
//允许指定注册的观察则 是否需要关心黏性事件
//sticky =true, 如果之前存在已经发送的数据,那么这个observer会受到之前的黏性事件消息
owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
//监听 宿主 发生销毁事件,主动把livedata 移除掉。
if (event == Lifecycle.Event.ON_DESTROY) {
eventMap.remove(eventName)
}
})
super.observe(owner, StickyObserver(this, sticky, observer))
}
}
class StickyObserver<T>(
val stickyLiveData: StickyLiveData<T>,
val sticky: Boolean,
val observer: Observer<in T>
) : Observer<T> {
//lastVersion 和livedata的version 对齐的原因,就是为控制黏性事件的分发。
//sticky 不等于true , 只能接收到注册之后发送的消息,如果要接收黏性事件,则sticky需要传递为true
private var lastVersion = stickyLiveData.mVersion
override fun onChanged(t: T) {
if (lastVersion >= stickyLiveData.mVersion) {
//就说明stickyLiveData 没有更新的数据需要发送。
if (sticky && stickyLiveData.mStickyData != null) {
observer.onChanged(stickyLiveData.mStickyData)
}
return
}
lastVersion = stickyLiveData.mVersion
observer.onChanged(t)
}
}
}
ViewModel架构组件原理解析
什么是ViewModel
- 具有宿主生命周期感知能力的数据存储组件
- ViewModel保存的数据,在页面配置变更导致页面销毁重建之后依然存在
- 配置变更:横竖撇切换、分辨率调整、权限变更、系统字体样式变更
ViewModel的用法
- 常规用法:存储的数据,仅仅只能当页面因为配置变更导致的销毁再重建时可复用。复用的是ViewModel的实例对象整体
- 进阶用法
这段依赖是我手写的可能会有拼写错误!
api 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
- 跨页面的数据共享
配置变更ViewModel复用实现原理
ViewModel数据复用进阶SavedState
- SavedStateRegistry模型。一个总Bundle,key-value存储这每个ViewModel对应的子Bundle
总结
Room架构组件原理解析
什么是Room
Room高频用法
- Room数据库创建
数据库创建实现原理
- Room数据抽象设计
- 数据库创建流程
Room与LIveData巧妙结合,数据变更监听
- Room与LiveData巧妙结合
- 数据变更监听
实战:基于Room封装APP离线缓存框架HiStorage
需求分析
疑难解惑
- 持久化时,需要把object数据转换成二进制。body以及内嵌对象需要实现Serializeable
- 定义数据库操作对象
- 定义缓存数据库CacheDataBase
//获取操作数据库数据的dao对象
abstract val getCacheDao: CacheDao
}
- HiStorage封装
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
object HiStorage {
fun <T> saveCache(key: String, body: T) {
val cache = Cache()
cache.key = key
cache.data = toByteArray(body)
CacheDatabase.get().cacheDao.saveCache(cache)
}
fun <T> getCache(key: String): T? {
val cache = CacheDatabase.get().cacheDao.getCache(key)
return (if (cache?.data != null) {
toObject(cache.data)
} else null) as? T
}
fun deleteCache(key: String) {
val cache = Cache()
cache.key = key
CacheDatabase.get().cacheDao.deleteCache(cache)
}
private fun <T> toByteArray(body: T): ByteArray? {
var baos: ByteArrayOutputStream? = null
var oos: ObjectOutputStream? = null
try {
baos = ByteArrayOutputStream()
oos = ObjectOutputStream(baos)
oos.writeObject(body)
oos.flush()
return baos.toByteArray()
} catch (e: Exception) {
e.printStackTrace()
} finally {
baos?.close()
oos?.close()
}
return ByteArray(0)
}
private fun toObject(data: ByteArray?): Any? {
var bais: ByteArrayInputStream? = null
var ois: ObjectInputStream? = null
try {
bais = ByteArrayInputStream(data)
ois = ObjectInputStream(bais)
return ois.readObject()
} catch (e: Exception) {
e.printStackTrace()
} finally {
bais?.close()
ois?.close()
}
return null
}
}
实战:基于Historage实现APP接口缓存
需求分析
疑难解惑
- 定义缓存策略
- 接口声明缓存能力
- HiRestful搭配HiStorage实现缓存能力
实战:基于LiveData实现登录结果通知和账户信息管理
需求分析
- 收拢登录页面的拉起动作,方便管理
- 使用LIveData转发登录结果
- 收拢账户信息的存储和获取
疑难解惑
- 定义全局AccountManager暴露拉起登录与获取结果的方法
- 把获取userProfile接口请求的动作转移到AccountManager
总结
-
Lifecycle组件,Activity是如何实现宿主能力的?
ReportFragment注入,目的是为了兼容直接继承自Activity和自定义LifecycleOwner的场景 -
LiveData与传统消息订阅、分发组件的不同?
观察者不活跃(宿主不可见)不分发数据,避免无用的后台资源抢占
基于声明周期,可避免内存泄露、NPE等问题 -
ViewModel是如何实现复用的?
本质是ViewModelStroe对象以NonConfigurationInstances的形式被存储到ActivityClientRecord中,在页面重建后,又被传递到新的Activity对象中 -
savedState组件是如何实现数据复用的?
利用了onSaveInstanceState的被触发的时机,最终会存储到ActivityRecord对象中。页面重建后,又被传递到新的ViewModel对象中。 -
Room数据库如何设计一款通用的数据缓存组件?
把缓存数据二进制序列化,相比于JSON序列化效率更高,读取缓存时能直接还原成原本类型 -
ViewPager刷新不起作用的解决方案以及背后原理
getItemPosition(Object object)刷新前后,让我们自行决定object在Viewpager中的去留
getItemId(Int position)最好返回一个与位置无关的独一无二的id。可避免复用问题 -
首页模块还可以优化的地方
以上是关于Jetpack笔记的主要内容,如果未能解决你的问题,请参考以下文章
Android Jetpack导航,另一个主机片段内的主机片段
使用 Jetpack 的 Android 导航组件销毁/重新创建的片段
Jetpack Navigation Drawer 总是重新创建片段
Android Jetpack Navigation:如何在 OnNavigatedListener 中获取目的地的片段实例?