Jetpack Compose - Effect与协程 (十五)
Posted 嘴巴吃糖了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose - Effect与协程 (十五)相关的知识,希望对你有一定的参考价值。
SideEffect
大家都知道在Compose中有一个重组的概念,也就是Recompose, 一般是因为数据源发生了变化,界面跟随要发生变化的场景, 但是有时候我们要考虑两种场景:
1.某个Composable函数 在执行的过程中,因为数据源发生了变化,所以执行到一半 又重新执行了 但是在这个Composable函数中,我们还有其他的一些代码,跟ui无关的,这样这些代码会执行多次,有时候这个执行多次的代码 也许并不符合我们的需求
2.在某个Composable函数 中,我们有一段代码,这个代码我就是仅仅想让他在生命周期内 只执行一次,不想他因为Recompose的缘故 会执行多次
这个时候 我们就可以使用SideEffect
Row()
var count=0
Column()
SideEffect
println("hello side Effect")
var names= arrayOf("111","222","333","444")
for (name in names)
Text(text = name)
count++
Text(text = "count:$count")
他的作用就是 有2点:
- 被SideEffect包裹起来的 代码 只会执行一次
- 在重组的过程中,SideEffect 只会在重组结束之后 被执行
DisposableEffect
这个effect的作用 主要就是可以监听组件的 是否展示中,也就是组件 在界面内展示出来了,还是在界面外没有展示
setContent
var flag = remember
mutableStateOf(true)
Column
if (flag.value)
Text(text = "hello", modifier = Modifier.clickable
flag.value = !flag.value
)
DisposableEffect(Unit)
Log.v("wuyue", " coming ")
onDispose
Log.v("wuyue", "leave")
Text(text = "change flag", modifier = Modifier.clickable
flag.value = !flag.value
)
可以运行一下代码看一下 ,hello的这个text 每次展示 都会打印coming,同样的每次不展示消失的时候 也会打印leave
其实到这里也能猜到了,这些SideEffect,以及DisposableEffect中的onDispose函数 本质上都是回调函数 在重组的生命周期的各个阶段会走这些回调函数,仅此而已
另外要注意的是,如果可见性没有发生变化,那么Disposable 也是不会有变化的 比如下面的代码
setContent
var flag by remember
mutableStateOf("hello")
Column
Log.v("wuyue", " compose ")
Text(text = flag, modifier = Modifier.clickable
flag = "$flag:$Math.random()"
)
DisposableEffect(Unit)
Log.v("wuyue", " coming ")
onDispose
Log.v("wuyue", "leave")
这里就是只会改变text的内容,text组件虽然改变了,触发了Column这个组件的recompose, 但是因为text组件的可见性没有发生变化,所以DisposableEffect 只会执行coming这行代码,而且只执行一次 leave 是不会执行的
同样的 我们也可以看到,这个effect是有一个key参数的,这个参数的作用就是 当key发生变化的时候 DisposableEffect 也会得到执行 ,不管可见性有没有发生变化
还是上面的例子,我们稍微改一下:
setContent
var flag by remember
mutableStateOf("hello")
Column
Log.v("wuyue", " compose ")
Text(text = flag, modifier = Modifier.clickable
flag = "$flag:$Math.random()"
)
DisposableEffect(flag)
Log.v("wuyue", " coming ")
onDispose
Log.v("wuyue", "leave")
这个时候你就会发现,每次点击的时候,先触发了重组,然后触发了leave回调,再触发了coming回调
LaunchedEffect
这个东西和上面2个小节的effect 作用就不太一样了,这个effect主要的作用主要是在Compose中启动一个协程 而且具有2个特点
- 在重组过程完成以后 才会启动协程
- key 发生变化的时候 也会启动协程
同样的 ,这个Effect的参数和DisposableEffect 其实是一样的
这里就不演示具体的代码了,因为和上一个小节的内容是差不多的,唯一的区别其实就是这个effect是专门为协程准备的,仅此而已
首先看下 下面这段代码
setContent
Column
Log.v("wuyue"," Recompose ")
var text by remember
mutableStateOf("custom")
Text(text = "hello", Modifier.clickable
text = "$Math.random()"
)
LaunchedEffect(Unit)
delay(3000)
Log.v("wuyue"," text: $text ")
这个3s之后的打印 应该能猜到 打印的值应该是随机数了, 但是我如果稍微改一下
@Composable
fun printlnCompose(text:String)
LaunchedEffect(Unit)
delay(3000)
Log.v("wuyue"," text: $text ")
setContent
Column
Log.v("wuyue"," Recompose ")
var text by remember
mutableStateOf("custom")
Text(text = "hello", Modifier.clickable
text = "$Math.random()"
)
printlnCompose(text)
这个时候你就会发现,3s之后的打印 还是custom,而不会是随机数了。这是为啥?
其实问题出在这个函数参数这里:
这个函数参数是一个普通类型的String,这会导致 我们的LanunchedEffect 感知不到我们的 text发生了变化
所以要改一下:
@Composable
fun printlnCompose(text: String)
var rememeberText by remember
mutableStateOf(text)
rememeberText = text
LaunchedEffect(Unit)
delay(3000)
Log.v("wuyue", " text: $rememeberText ")
我们只要稍微的手动进行转换一下 即可,将这个text 手动转换成 一个remember类型的变量即可
上述的写法 也可以用一个简便的写法:
@Composable
fun printlnCompose(text: String)
var rememeberText = rememberUpdatedState(newValue = text)
LaunchedEffect(Unit)
delay(3000)
Log.v("wuyue", " text: $rememeberText ")
看下源码,其实底层和我们的代码是以一样的 美
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YmwtEZx0-1672209774529)(null)]
为什么不能在Compose中 随意启动一个协程?
有人要问了,为啥在compose中启动一个协程这么麻烦?可以看下面截图的报错信息
她告诉你 ,这个协程 必须要在Lanunedeffect中使用,直接用是不行的,为什么? 因为Kotlin中 所有的协程都需要一个Scope,这个Scope主要的作用就是在 某一个时刻将你的协程取消我们的lifeCycleScope 是和activity的生命周期绑定在一起的,并没有和compose绑定在一起,所以 如果Compose 不加这个限制,那么协程运行在compose中就会出错了
所以我们在Compose中使用协程的前提条件是必须得有一个和Compose生命周期绑定在一起的scope
一看图,还是错了,还是不给我们用吗,为嘛?
因为这里compose中有重组的概念,所以你必须要用一个remember去包裹一下,否则每次重组你的协程都要执行一次 那不是乱套了嘛
所以其实你看LanunchedEffect 也是类似的封装思路
有人可能会奇怪,既然如此,为啥还要对外暴露这个rememberScope的回调?,直接private 这个remember不行吗 ,反正单独调用她也没用
可以看一下 下面这行代码:
val scope = rememberCoroutineScope()
Text(text = "hello", Modifier.clickable
scope.launch
)
在这个点击事件里面,他其实并不属于Compose的环境了,所以我们只需要一个scope 即可完成协程的启动, 你甚至可以在这里直接启动一个lifeCycleScope的协程
唯一的区别仅仅在于 你到底希望你的协程在哪个scope的生命周期里 被结束掉。
Compose状态转换
在Compose中,一个界面组件要响应一个变量的变化,这个变量必须是一个state类型的对象
通常而言,我们会使用MutableState这个state的子接口,因为state是可读,而MutableState是可读可写在有时候,我们变化的数据源 如果不是一个MutableState 那怎么办? 怎么让Compose的页面来感知我们的数据源变化?
例如,我们要感知用户的地理位置,这个不断变化的地理位置 怎么让Compose的界面可以感知到?因为地址位置的变更显然返回的是一个坐标,而不是一个state
可以参考如下代码
var address by remember
mutableStateOf(Point(0, 0))
Text(text = address.toString())
DisposableEffect(Unit)
val callBack = object : GetAddressInfo
override fun getAddressInfo(p: Point)
address = p
// register callback
onDispose
//unregister callback
同样的 对于livedata来说 我们想感知界面的变化,那也是需要转成state的,好在runtime库 帮我们把这个操作做了
注意要引入新的依赖
implementation "androidx.compose.runtime:runtime:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.runtime:runtime-rxjava2:$compose_version"
对于flow来说 也有对应的转换方式
val flow: StateFlow<Point> = MutableStateFlow(Point(0, 0))
val flowState = produceState(Point(0, 0))
flow.collect
value = it
同样的 也有更加简便的写法:
flow.collectAsState()
snapshotflow
前面一个小节 我们介绍了各种数据类型向Compose的state转换的方法,这一小节来简单介绍一下 state如何向flow去转换,简单来说 就是把state的变化, 利用flow 通知出去
Column
var text by remember
mutableStateOf("hello")
var flow = snapshotFlow
text
LaunchedEffect(key1 = Unit)
flow.collect
Log.v("wuyue","it:$it")
Text(text = text, Modifier.clickable
text = "$Math.random()"
)
注意了 snapshotFlow 是可以感知多个state的变化的
Column
var text by remember
mutableStateOf("hello")
var age by remember
mutableStateOf(18)
var flow = snapshotFlow
"$text $age"
LaunchedEffect(key1 = Unit)
flow.collect
Log.v("wuyue","it:$it")
Text(text = text, Modifier.clickable
text = "$Math.random()"
)
Text(text = "age:$age", Modifier.clickable
age = (Math.random() * 100).toInt()
)
作者:vivo祁同伟
链接:https://juejin.cn/post/7181375063449403450
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
以上是关于Jetpack Compose - Effect与协程 (十五)的主要内容,如果未能解决你的问题,请参考以下文章
Jetpack Compose - Effect与协程 (十五)
自学Jetpack Compose 系列Compose控件Text与TextStyle的学习与使用