在实际开发中,我们经常会遇到一些字段在应用中需要反复使用。
过去最常见的做法就是在 Application 中申明一个属性,使用它来暂存这个字段,这是非常常见的内存持久化方案 ,这些暂存内容会在应用退出后丢失,属于短期持久化 。
另一个场景就是使用 MMKV 这样的键值对持久化工具,将需要持久化的内容保存到本地文件,这些内容退出后不会丢失,再次打开应用时会重新读取加载,属于长期持久化 。
在 Compose 中需要我们可以使用 junerver /ComposeHooks 中的 usePersistent
来轻松做到这一点。
默认内存持久化-简单的全局状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Composable private fun DefaultPersistent () { var count by usePersistent(key = "count" , -1 ) Column { Text(text = "DefaultPersistent : exit app will lose state" ) TButton(text = "+1" ) { count += 1 } SubShowCount() } } @Composable private fun SubShowCount () { val count by usePersistent(key = "count" , -1 ) Text(text = "persistent: $count " ) }
它开箱即用,你只需要调用 usePersistent(key = "count", -1)
,传入一个 key
与默认值即可,默认使用内存进行持久化保存。
在应用全局同一个 key 对应同一个对象,一处修改、处处生效。
自定义持久化-使用 mmkv 除了内存持久化,usePersistent
同样支持自定义持久化方案,例如使用 mmkv:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 val mmkv = MMKV.defaultMMKV()fun mmkvSave (key: String , value: Any ?) { when (value) { is Int -> mmkv.encode(key, value) is Long -> mmkv.encode(key, value) is Double -> mmkv.encode(key, value) is Float -> mmkv.encode(key, value) is Boolean -> mmkv.encode(key, value) is String -> mmkv.encode(key, value) is ByteArray -> mmkv.encode(key, value) is Parcelable -> mmkv.encode(key, value) } notifyDefaultPersistentObserver(key) } fun mmkvGet (key: String , value: Any ) : Any { return when (value) { is Int -> mmkv.decodeInt(key, value) is Long -> mmkv.decodeLong(key, value) is Double -> mmkv.decodeDouble(key, value) is Float -> mmkv.decodeFloat(key, value) is Boolean -> mmkv.decodeBool(key, value) is String -> mmkv.decodeString(key, value) is ByteArray -> mmkv.decodeBytes(key, value) is Parcelable -> mmkv.decodeParcelable(key, value.javaClass) else -> error("wrong type of default value!" ) } as Any } fun mmkvClear (key: String ) { mmkv.remove(key) notifyDefaultPersistentObserver(key) } PersistentContext.Provider( value = Triple( first = ::mmkvGet, second = ::mmkvSave, third = ::mmkvClear ) ) { val (hideKeyboard) = useKeyboard() var token by usePersistent(key = "token" , "" ) val (state, setState) = useGetState("" ) Column { Text(text = "MMKVPersistent : exit app will NOT lose state" ) Text(text = "token: $token " ) OutlinedTextField(value = state.value, onValueChange = setState) TButton(text = "saveToken" ) { hideKeyboard() token = state.value setState("" ) println("now you can exit app,and reopen" ) } MMKVPersistentSub() } }
自定义持久化时需要使用 PresistentContext.Provider
这个容器组件,向这个组件提供一个三元组 Triple<PersistentGet, PersistentSave, PersistentClear>
这个三元组接收三个函数,分别对应了持久化获取、保存、移除这三个操作,现在处于 PresistentContext.Provider
这个容器组件下的 usePersistent
函数将会使用自定义的 mmkv 进行持久化操作。
自定义存储与自定义的移除操作时请务必调用 notifyDefaultPersistentObserver(key)
这个函数来通知 hook 状态变更了
清除持久化内容 除了读写,在有的场景下,我们需要移除持久化内容,这种场景下就不能继续使用 by
关键字进行委托了,需要直接使用 =
usePersistent
函数的返回值是一个 data class ,我们可以使用解构声明轻松的获取其成员:
1 2 3 4 5 6 7 8 9 10 11 12 @Stable data class PersistentHolder <T >( val state: State<T>, val save: SaveToPersistent<T>, val clear: HookClear, ) { operator fun getValue (thisRef: Any ?, property: KProperty <*>) : T = state.value operator fun setValue (thisRef: Any ?, property: KProperty <*>, newValue: T ) { save(newValue) } }
我们需要进行如下操作,修改 by
为 =
,通过解构声明获取第三个元素的值就是 clear 函数
1 2 3 4 5 @Composable private fun SubShowCount () { val (countState,_,clear) = usePersistent(key = "count" , -1 ) Text(text = "persistent: ${countState.value} ,click to clear" ,modifier = Modifier.clickable { clear() }) }
这时我们调用 clear 函数,将会移除这个 key 对应的存储内容,并将状态修改为默认值
在自定义存储容器组件下强制使用内存持久化 在使用了自定义持久化的情况下,PresistentContext.Provider
这个容器组件下所有的 usePresistent
函数将默认使用自定义持久化方案,可以通过配置参数 forceUseMemory = true
来强制使用内存持久化:
1 2 3 4 5 6 7 8 9 10 @Composable private fun MMKVPersistentSub () { val (token, _, clear) = usePersistent(key = "token" , "321" ) var clearCount by usePersistent(key = "clearCount" , 0 , forceUseMemory = true ) Text(text = "sub component token: ${token.value} ,click to clear" , modifier = Modifier.clickable { clear() clearCount += 1 }) Text("current clear count: $clearCount " ) }
探索更多 好了以上就是 使用 hooks 的一些小小技巧,现在你可以使用 usePresistent
来轻松的使用全局状态、持久化状态!
示例源码地址:UsePersistentExample
项目开源地址:junerver /ComposeHooks
MavenCentral:hooks2
本项目已经迁移到 Compose Multiplatform ,使用新的工件 id:hooks2
如果你在 CMP 依赖,直接使用:
1 implementation("xyz.junerver.compose:hooks2:2.1.0-alpha0" )
如果你在 Android 环境依赖,请使用 id:hooks2-android
:
1 implementation("xyz.junerver.compose:hooks2-android:2.1.0-alpha0" )
详细迁移说明请查看wiki
欢迎使用、勘误、pr、star。