由于 Compose 是声明式工具集,因此更新它的唯一方法是通过新参数调用同一可组合项。这些参数是界面状态的表现形式。每当状态更新时,都会发生重组。
本篇文章将从导图中五方面分别介绍State
的相关概念和用法
简介
Compose是一种新式声明性界面工具包,它和我们之前一直接触的View
体系完全不同,比如之前我们想更新TextView
显示文字,可以直接使用TextView.setText()
来改变界面上显示的文案,而在Compose中,已经不支持这种更新方式,取而代之的是更新这个可组合项引用的State
值,这样就会触发可组合项的重组事件,重组之后可组合项会获取到State
最新的值并显示在界面上,下方图片就是Compose中可组合项的生命周期:进入组合 -> 执行0次或多次重组 -> 最后是退出组合。
Compose中采用的是State
来作用于可组合项中值的读取和更改,在值更改(与更改前值不同)之后就会触发可组合项的重组,从而达到界面刷新的机制。
remember{}
remember{}
可以将对象存储在内存当中,在第一次组合的时候将从remember
提供的值存储到组合项中,并在重组期间返回最新存储的值。
上面我们使用remember{ mutableStateOf("init") }
创建了一个可变的State
,然后在按钮点击之后改变它的值,最终会在Text
中呈现出来,这里需要注意的一点就是通过= remember{}
这种方式创建的结果是state
对象,在引用或者修改其值时需要调用state.value
,下面还有介绍两种不同的方式来创建state
,原理都是一致只是返回的对象不同而已。
接着我们再来看看另外两种方式是如何使用state
的
by remember{}
通过委托方式进行创建state
,此方式返回的对象为state
的泛型值(value, changeValue) = remember{}
通过解构方式进行创建state
,此方式第一个参数为返回的具体值,第二个参数为Lambda表达式,可直接修改状态值
这里也有一点需要大家注意,就是第二种委托方式创建的对象如果需要进行修改,就得使用var
来修饰,并且需要额外导入两个包androidx.compose.runtime.getValue
和androidx.compose.runtime.setValue
,否则AS会一直爆红提示。
通过上面的代码示例想必大家都已经了解了如何去使用remember{}
方式去创建和使用state
,到这为止大家不妨将手机屏幕旋转一下,看看更新后的state
值是什么样,在小节开头的时候提到,remember{}
方式是将值存到内存当中,它并不能在配置更改后依然保存最新的值,而是会恢复成初始值,如果遇到这种情况,大家就需要使用另外一种,那就是rememberSaveable{}
它会将值保存到Bundle
中,即使在屏幕旋转等配置更改之后也会取到最新的值,并不会丢失。具体用法这里就不再多做演示,大家可以自行体验一下。
LiveData.obseverAsState()
LiveData
我们接触的比较早了,之前也写过一篇文章简单介绍了它的使用和源码,它是一种可观察的数据存储器类,通常结合ViewModel使用,下面我们就通过代码来看下在Compose种如何使用它来转换成State
使用LiveData.observeAsState()
前需要添加额外的依赖:androidx.compose.runtime:runtime-livedata:1.4.3
,它可以之前将LiveData
转换成State
,这样我们在引用LiveData
值的时候就可以直接引用State
的值了,修改值依然可以直接修改LiveData
的值,Compose会自动响应修改的变化。
其内部实现也是非常简单,采用DisposableEffect
副作用机制,内部添加LiveData
的观察者,将观察到的值赋予state
,然后在onDispose{}
种取消观察者,通过源码可以看出,这里是绑定了生命周期的,在onDestroy()
时会触发onDispose{}
,所以在使用这种方式的情况下,大家不必担心会产生内存泄露问题,内部已经帮我们处理过了,下面是源码大家可以自行消化下。
Flow.collectAsState()
Flow
是Kotlin种一种数据流模型,它的原理就类似水流管道,上游发射下游接收,关于Flow
也是写过两片文章分别介绍了基本的使用和操作符大全,如果还没有接触过的小伙伴可以先看下前面两片文章,熟悉下如何简单使用,下面我们直接来看下在Compose种如何使用Flow
图片代码中我们采用的是collectAsState()
将Flow
转换成State
对象,读取其值是和LiveData
一样,直接读取State
的值,修改值时直接更新Flow
的值。
这里的collectAsState()
不推荐大家使用,因为它没有绑定生命周期,它内部是直接collect{}
收集流的值,然后转换成State
对象,并没有考虑到生命周期的问题,官方提供了另外一种方式帮助我们将Flow
转换成State
,也就是collectAsStateWithLifecycle()
,这种方式采用了lifecycle.repeatOnLifecycle(){}
来绑定生命周期,并且我们可以自定义在哪一时刻开始收集数据。
此方法有四个参数:
initalValue
可以定义初始值lifecycle
用于管理生命周期,在Compose种可以通过LocalLifecycleOwner.current
获取到当前的生命周期管理者minActiveState
此参数就是定义最小在哪个周期开始收集流的值context
这个就是协程的上下文,用于启动协程,默认为EmptyCoroutineContext
内部采用的是produceState()
方式,这里具体用法会在文章接下来详细介绍,它就是将我们所需要的观察状态转换成State
对象,这样就可以供Compose响应此状态。
produceState
produceState
是Compose提供的一种创建State
的方式,它可以帮助我们自由的生成State
对象
这是最简单的一种方式,只有两个参数:
initialValue
提供一个初始值producer()
它是一个挂起函数,我们可以在其内部使用协程并且提供State
的值
下面我们通过此方式来模拟一个定时器的实现,倒计时60s,每秒更新下Text文字
实现过程也是非常的简单,在producer
中通过协程的delay
模拟延时1s的效果,然后通过this.value
来更新State
的值。具体效果见下方GIF
到这4种方式就介绍完了,除了produceState
之外其它的三种基本上和我们项目的选型相关,如果项目已经采用了Kotlin的协程,那我们可以直接采用Flow
的方式,目前已经完善的挺全面了,而且操作符也是应有尽有,大家可以按需选择~
关于我
我是Taonce,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢~