飧逛昱 發表於 2022-11-30 20:18:00

Android开发 Jetpack_Compose_6 附带效应

<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">  附带效应是指LaunchedEffect、</span><span style="color: rgba(0, 0, 0, 1)"><span class="devsite-heading">DisposableEffect、</span></span><span class="devsite-heading">rememberCoroutineScope</span><span class="devsite-heading">、</span><span class="devsite-heading">rememberUpdatedState</span><span class="devsite-heading">、produceState 、</span><span class="devsite-heading">derivedStateOf的使用。附带效应这4个字在google官方文档上的表达与解释挺让人难以理解的。</span><span class="devsite-heading">其实个人认为准确的描述应该是外部产生的数据向Compose状态作用域内传递。这里的外部数据是指网络请求数据、数据库数据、定时触发状态、子线程运算数据、Activity生命周期等等这些数据。</span></p>
<p><span style="color: rgba(0, 0, 0, 1)"><span class="devsite-heading">  在学了Compose的状态管理后可以明白,Compose的重组需要依靠</span></span>mutableStateOf&nbsp;。那为什么还需要有LaunchedEffect、<span class="devsite-heading">DisposableEffect等等这些函数呢?理由如下:</span><span class="devsite-heading"><br></span></p>
<ul>
<li><span class="devsite-heading">在Compose作用域里实现协程与异步操作,从而对一些资源的初始化与销毁进行异步操作</span></li>
<li><span class="devsite-heading">接收外部数据回调,但是建议尽量选择用协程取代接口回调方法</span></li>
<li><span class="devsite-heading">配合LifeCycle,让Compose对Activity的生命周期实现监听(这个其实与第二点其实是一样的,但是使用情况比较多这里单独拿出来强调)</span></li>
<li><span class="devsite-heading">进一步的控制Compose重组</span></li>
</ul>
<p><span style="color: rgba(0, 0, 0, 1)"><span class="devsite-heading">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;个人的一些见解,这些见解你可以不认同。因为可能是我前期的粗浅认识。我认为Compose的附带效应各种方法很多,官方文档的举例也非常难以理解。其实根本原因是附带效应的各色方法使用场景其实有一部分相当重叠,容易让人一时无法判断我学习到的这些方法应该在什么情况下使用。有一些人会认为其实只要有</span></span>DisposableEffect就能解决全部Compose里需要的协程异步、数据获取、资源初始化与释放完全不需要依靠其他方法,这个我也无法否认因为DisposableEffect的确可以满足以上全部功能。为什么附带效应会有这么多使用重叠的方法?我个人理解是jetpack的开发组,内部就有各色开发人员倾向不同的架构与思想。比如有人喜欢直接在Activity请求网络数据,有些人喜欢用MVP、MVC、MVVM这些架构请求网络数据、有一些人则倾向用RxJava、flow这种链式编程请求网络数据。他们为了满足这些干脆搞了一个大杂烩,让各种使用场景都能有各色附带效应方法使用。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 所以,我这里建议是你需要全部学会附带效应的方法。但是使用的时候只需要挑两三个(这里推荐LaunchedEffect、rememberCoroutineScope、DisposableEffect)你习惯使用的就行了... 不要过度思考什么场景使用什么附带效应方法。有些东西你会有一天突然醒悟的. 在醒悟之前用你最习惯最稳妥的方式写是最安全的。</p>
<h1><span style="color: rgba(0, 128, 128, 1)"><span class="devsite-heading">LaunchedEffect</span></span></h1>
<p>LaunchedEffect的作用主要是让你在Compose作用域里实现协程工作,以完成异步、耗时数据处理、网络请求等等工作。</p>
<h2><span style="color: rgba(0, 51, 102, 1)">简单的延迟计数</span></h2>
<p><span style="color: rgba(0, 0, 0, 1)">代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Preview()
    @Composable
    fun MyLaunchedEffect() {
      val count </span>= remember { mutableStateOf(<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">) }

      LaunchedEffect(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
                delay(</span><span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)">)
                count.value</span>++<span style="color: rgba(0, 0, 0, 1)">
            }
      }

      Text(text </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">${count.value}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontSize = <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">.sp)

    }</span></pre>
</div>
<p>效果图:</p>
<p><img src="https://img2023.cnblogs.com/blog/1497956/202211/1497956-20221130210457075-674864103.gif" alt=""></p>
<p>&nbsp;</p>
<h2><span style="color: rgba(0, 51, 102, 1)">模拟请求网络数据</span></h2>
<p>代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Preview()
    @Composable
    fun MyLaunchedEffect() {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否请求数据</span>
      val isPostData =<span style="color: rgba(0, 0, 0, 1)"> remember {
            mutableStateOf(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">请求结果</span>
      val postResult =<span style="color: rgba(0, 0, 0, 1)"> remember {
            mutableStateOf(</span>""<span style="color: rgba(0, 0, 0, 1)">)
      }

      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (isPostData.value) {
            LaunchedEffect(isPostData.value) {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里进行耗时操作处理异常,比如请求网络,从数据库获取数据</span>
                postResult.value =<span style="color: rgba(0, 0, 0, 1)"> withContext(Dispatchers.Default){
                  delay(</span>1000<span style="color: rgba(0, 0, 0, 1)">)
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span>@withContext "模拟请求结果&gt;&gt;成功 ${System.currentTimeMillis()}"<span style="color: rgba(0, 0, 0, 1)">
                }
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">请求完成后重新设置成false,这样就可以继续下一次请求</span>
                isPostData.value = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
            }
      }

      Button(onClick </span>= { isPostData.value = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> }) {
            Text(text </span>=<span style="color: rgba(0, 0, 0, 1)"> postResult.value)
      }
    }</span></pre>
</div>
<p>效果图:</p>
<p><img src="https://img2023.cnblogs.com/blog/1497956/202212/1497956-20221226170357417-2018128645.gif" alt=""></p>
<h2><span style="color: rgba(0, 51, 102, 1)">LaunchedEffect参数的作用</span></h2>
<p>其实很简单,如果参数一样,那么LaunchedEffect只会触发一次,如果参数一直变化则会多次触发。但是如果你希望LaunchedEffect在指定情况下触发你还是需要向上面的例子里一样添加if。 否则LaunchedEffect在Compose创建的时候会立刻触发。</p>
<p>代码:</p>
<div class="cnblogs_code">
<pre>    var countKey1 = 0<span style="color: rgba(0, 0, 0, 1)">

    @Preview()
    @Composable
    fun MyLaunchedEffect() {
      val countKey2 </span>=<span style="color: rgba(0, 0, 0, 1)"> remember {
            mutableStateOf(</span>0<span style="color: rgba(0, 0, 0, 1)">)
      }
      LaunchedEffect(</span>"A"<span style="color: rgba(0, 0, 0, 1)">) {
            Log.e(</span>"zh", "MyLaunchedEffect: A"<span style="color: rgba(0, 0, 0, 1)">)
      }
      LaunchedEffect(countKey1) {
            Log.e(</span>"zh", "MyLaunchedEffect: B"<span style="color: rgba(0, 0, 0, 1)">)
      }
      LaunchedEffect(countKey2.value) {
            Log.e(</span>"zh", "MyLaunchedEffect: C"<span style="color: rgba(0, 0, 0, 1)">)
      }
      Button(onClick </span>=<span style="color: rgba(0, 0, 0, 1)"> {
            countKey1</span>++<span style="color: rgba(0, 0, 0, 1)">
            countKey2.value</span>++<span style="color: rgba(0, 0, 0, 1)">
      }) {
      }
    }</span></pre>
</div>
<p>连续点击后按钮效果如下,在下面的log里可以看到A触发了一次,B和C每次点击后都触发。这里需要使用mutableStateOf创建的countKey2来触发Compose的重组,从而验证普通Int值的countKey1也能触发LaunchedEffect的执行。</p>
<div class="cnblogs_code">
<pre>2022-12-20 01:10:01.931 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: A
</span>2022-12-20 01:10:01.931 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: B
</span>2022-12-20 01:10:01.931 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: C
</span>2022-12-20 01:10:03.328 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: B
</span>2022-12-20 01:10:03.329 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: C
</span>2022-12-20 01:10:04.017 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: B
</span>2022-12-20 01:10:04.017 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: C
</span>2022-12-20 01:10:04.702 13167-13167<span style="color: rgba(0, 0, 0, 1)"> zh                      com.lwlx.composedemo               EMyLaunchedEffect: B
</span>2022-12-20 01:10:04.702 13167-13167 zh                      com.lwlx.composedemo               EMyLaunchedEffect: C</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">rememberCoroutineScope</span></h1>
<p>rememberCoroutineScope 其实与 LaunchedEffect 功能上一致,但是LaunchedEffect 只能在compose的作用域中调用,无法在按键点击回调等等地方调用,rememberCoroutineScope可以在这些地方调用。此外,如果您需要手动控制一个或多个协程的生命周期,请使用 rememberCoroutineScope,例如在用户事件发生时取消动画。</p>
<p>下面的代码模拟了一个网络请求,其中引用了&nbsp;implementation 'androidx.compose.runtime:runtime-livedata:1.2.1' ,把LiveData 转成 MutableState</p>
<p>代码:</p>
<p>ViewModel</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MainViewModel: ViewModel() {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val _data = MutableLiveData&lt;String&gt;<span style="color: rgba(0, 0, 0, 1)">()
    val data : LiveData</span>&lt;String&gt; get() =<span style="color: rgba(0, 0, 0, 1)"> _data


    suspend fun postData(){
      delay(</span>500<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">模拟网络的异步请求</span>
      _data.postValue("当前时间戳 = ${System.currentTimeMillis()}"<span style="color: rgba(0, 0, 0, 1)">)

    }
}</span></pre>
</div>
<p>activity</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Preview()
    @Composable
    fun MyCoroutineScope() {
      val coroutineScope </span>=<span style="color: rgba(0, 0, 0, 1)"> rememberCoroutineScope()
      val data </span>=<span style="color: rgba(0, 0, 0, 1)"> mViewModel.data.observeAsState()

      Column {
            Button(onClick </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                coroutineScope.launch {
                  mViewModel.postData()
                }
            }) {
            }
            Text(text </span>= data.value ?: ""<span style="color: rgba(0, 0, 0, 1)">)
      }
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">rememberUpdatedState</span></h1>
<p>rememberUpdatedState 只是辅助LaunchedEffect<strong>向外部(其他composeable方法)更新状态功能</strong>,用于在不同compose方法下LaunchedEffect的数据更新时提供最新数据。下面提供2个例子代码来验证效果。</p>
<h2><span style="color: rgba(0, 51, 102, 1)">不使用rememberUpdatedState&nbsp;</span></h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Composable
    fun MyTextField() {
      val text </span>=<span style="color: rgba(0, 0, 0, 1)"> remember {
            mutableStateOf(</span>""<span style="color: rgba(0, 0, 0, 1)">)
      }
      MyLaunchedEffect(content </span>=<span style="color: rgba(0, 0, 0, 1)"> text.value)
      Column {
            TextField(value </span>= text.value, onValueChange =<span style="color: rgba(0, 0, 0, 1)"> {
                text.value </span>=<span style="color: rgba(0, 0, 0, 1)"> it
            })
      }
    }

    @Composable
    fun MyLaunchedEffect(content:String){
      Log.e(</span>"zh", "MyLaunchedEffect重组中 content = ${content}"<span style="color: rgba(0, 0, 0, 1)">)
      LaunchedEffect(Unit){
            </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">){
                delay(</span>500<span style="color: rgba(0, 0, 0, 1)">)
                Log.e(</span>"zh", "content = ${content}"<span style="color: rgba(0, 0, 0, 1)">)
            }
      }
    }</span></pre>
</div>
<p>在输入框输入内容后,在日志里会反复打印如下代码。并没有新的 content 内容输出。</p>
<div class="cnblogs_code">
<pre>2022-12-28 15:03:08.0788216-8216zh                      com.lwlx.composedemo               Econtent =
2022-12-28 15:03:08.5808216-8216zh                      com.lwlx.composedemo               Econtent =
2022-12-28 15:03:09.0828216-8216zh                      com.lwlx.composedemo               Econtent = </pre>
</div>
<h2><span style="color: rgba(0, 51, 102, 1)">使用rememberUpdatedState&nbsp;</span></h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Composable
    fun MyTextField() {
      val text </span>=<span style="color: rgba(0, 0, 0, 1)"> remember {
            mutableStateOf(</span>""<span style="color: rgba(0, 0, 0, 1)">)
      }
      MyLaunchedEffect(content </span>=<span style="color: rgba(0, 0, 0, 1)"> text.value)
      Column {
            TextField(value </span>= text.value, onValueChange =<span style="color: rgba(0, 0, 0, 1)"> {
                text.value </span>=<span style="color: rgba(0, 0, 0, 1)"> it
            })
      }
    }

    @Composable
    fun MyLaunchedEffect(content:String){
      val contentUpdatedState by rememberUpdatedState(content) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">添加rememberUpdatedState</span>
      Log.e("zh", "MyLaunchedEffect重组中 content = ${contentUpdatedState}"<span style="color: rgba(0, 0, 0, 1)">)
      LaunchedEffect(Unit){
            </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">){
                delay(</span>500<span style="color: rgba(0, 0, 0, 1)">)
                Log.e(</span>"zh", "content = ${contentUpdatedState}"<span style="color: rgba(0, 0, 0, 1)">)
            }
      }
    }</span></pre>
</div>
<p>在输入框输入内容后,日志里会打印输入的最新内容</p>
<div class="cnblogs_code">
<pre>2022-12-28 15:06:55.249 12386-12386 zh                      com.lwlx.composedemo               EMyLaunchedEffect重组中 content =<span style="color: rgba(0, 0, 0, 1)"> As
</span>2022-12-28 15:06:55.303 12386-12386 zh                      com.lwlx.composedemo               Econtent =<span style="color: rgba(0, 0, 0, 1)"> As
</span>2022-12-28 15:06:55.312 12386-12386 zh                      com.lwlx.composedemo               EMyLaunchedEffect重组中 content =<span style="color: rgba(0, 0, 0, 1)"> As
</span>2022-12-28 15:06:55.818 12386-12386 zh                      com.lwlx.composedemo               Econtent =<span style="color: rgba(0, 0, 0, 1)"> As
</span>2022-12-28 15:06:55.870 12386-12386 zh                      com.lwlx.composedemo               EMyLaunchedEffect重组中 content =<span style="color: rgba(0, 0, 0, 1)"> Asd
</span>2022-12-28 15:06:55.926 12386-12386 zh                      com.lwlx.composedemo               EMyLaunchedEffect重组中 content =<span style="color: rgba(0, 0, 0, 1)"> Asd
</span>2022-12-28 15:06:56.321 12386-12386 zh                      com.lwlx.composedemo               Econtent = Asd</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">DisposableEffect</span></h1>
<p>DisposableEffect 必须添加一个<code dir="ltr">onDispose方法</code>&nbsp;,此方法一般用来处理资源清理与释放。 比如监听了生命周期、监听了广播等等情况。另外DisposableEffect 也带参数意义和LaunchedEffect的参数一样。</p>
<p>下面举例了官方文档上的监听了生命周期代码例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MainActivity : ComponentActivity() {

    </span><span style="color: rgba(0, 0, 255, 1)">override</span> fun onCreate(savedInstanceState: Bundle?<span style="color: rgba(0, 0, 0, 1)">) {
      super.onCreate(savedInstanceState)
      setContent {
            HomeScreen(LocalLifecycleOwner.current, {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">onStart</span>
                Log.e(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">zh</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">触发了onStart生命周期</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> )

            }, {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">onStop</span>
                Log.e(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">zh</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">触发了onStop生命周期</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> )
            })
      }
    }

    @Composable
    fun HomeScreen(
      lifecycleOwner: LifecycleOwner </span>=<span style="color: rgba(0, 0, 0, 1)"> LocalLifecycleOwner.current,
      onStart: () </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> Unit,
      onStop: () </span>-&gt;<span style="color: rgba(0, 0, 0, 1)"> Unit
    ) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里创建了2个更新状态,用于更新start生命周期与stop生命周期的回调</span>
<span style="color: rgba(0, 0, 0, 1)">      val currentOnStart by rememberUpdatedState(onStart)
      val currentOnStop by rememberUpdatedState(onStop)

      DisposableEffect(lifecycleOwner) {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建一个Activity生命周期观察者</span>
            val observer = LifecycleEventObserver { _, <span style="color: rgba(0, 0, 255, 1)">event</span> -&gt;
                <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">event</span> ==<span style="color: rgba(0, 0, 0, 1)"> Lifecycle.Event.ON_START) {
                  currentOnStart()
                } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">event</span> ==<span style="color: rgba(0, 0, 0, 1)"> Lifecycle.Event.ON_STOP) {
                  currentOnStop()
                }
            }

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">添加监听</span>
<span style="color: rgba(0, 0, 0, 1)">            lifecycleOwner.lifecycle.addObserver(observer)

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在onDispose方法里移除了生命周期监听</span>
<span style="color: rgba(0, 0, 0, 1)">            onDispose {
                lifecycleOwner.lifecycle.removeObserver(observer)
            }
      }
    }
}</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">SideEffect</span></h1>
<p>SideEffect 其实就是不带参数的LaunchedEffect,并且SideEffect会在每一次重组的时候都触发。</p>
<p>代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Preview
    @Composable
    fun MySideEffect() {
      val count </span>= remember { mutableStateOf(0<span style="color: rgba(0, 0, 0, 1)">) }
      SideEffect {
            Log.e(</span>"zh", "SideEffect触发${count.value}"<span style="color: rgba(0, 0, 0, 1)">)
      }
      Text(text </span>= count.value.toString(),fontSize = 36.sp, modifier =<span style="color: rgba(0, 0, 0, 1)"> Modifier.clickable {
            count.value</span>++<span style="color: rgba(0, 0, 0, 1)">
      })
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">produceState</span></h1>
<p>produceState<span class="devsite-heading">将非 Compose 状态转换为 Compose 状态,produceState的使用场景其实与LaunchedEffect多多少少有些重叠。google设计produceState的目的应该是使用在大量重复的网络请求情况下,比如加载网络图片、下载音频视频。这类情况有一个共同场景你可能不需要mutableStateOf 来保存数据。</span></p>
<p><span class="devsite-heading">下面的例子将网络请求的结果转换成Compose的状态,</span><span class="devsite-heading">代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Composable
    fun PostState() {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> updatePostData by remember {
            mutableStateOf(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
      }
      val state </span>=produceState&lt;Result&lt;String&gt;&gt;(initialValue = Result(-<span style="color: rgba(128, 0, 128, 1)">1</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">请求中</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">), updatePostData) {
            value </span>= <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">true</span>) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">模拟网络请求请求结果</span>
                Result(<span style="color: rgba(128, 0, 128, 1)">200</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">请求成功</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{\"id\": \"1\",\"name\": \"小明\", \"time\":${System.currentTimeMillis()}}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                Result(</span><span style="color: rgba(128, 0, 128, 1)">400</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">请求失败</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">)
            }
      }
      Text(text </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">${state}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, modifier =<span style="color: rgba(0, 0, 0, 1)"> Modifier.clickable {
            updatePostData </span>= !<span style="color: rgba(0, 0, 0, 1)">updatePostData
      })
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">附带效应重组后什么时候执行</span></h1>
<p><strong><span style="color: rgba(0, 0, 0, 1)">重组触发顺序如下:</span></strong></p>
<p><span style="color: rgba(0, 0, 0, 1)">首先携带状态数据触发重组的View &gt;&gt; Compose的其他View重组 &gt;&gt;&nbsp;&nbsp;DisposableEffect &gt;&gt;&nbsp;SideEffect &gt;&gt;&nbsp;LaunchedEffect</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Preview
    @Composable
    fun MyRecombine() {
      val count </span>= remember { mutableStateOf(0<span style="color: rgba(0, 0, 0, 1)">) }
      Log.e(</span>"zh", "外部重组1111"<span style="color: rgba(0, 0, 0, 1)">)

      LaunchedEffect(count.value){
            Log.e(</span>"zh", "LaunchedEffect: 重组3333"<span style="color: rgba(0, 0, 0, 1)">)
      }
      SideEffect {
            Log.e(</span>"zh", "SideEffect: 重组2222"<span style="color: rgba(0, 0, 0, 1)">)
      }
      DisposableEffect(count.value){
            Log.e(</span>"zh", "DisposableEffect: 重组4444"<span style="color: rgba(0, 0, 0, 1)">)
            onDispose {
            }
      }
      Log.e(</span>"zh", "外部重组5555"<span style="color: rgba(0, 0, 0, 1)">)
      Column {
            Log.e(</span>"zh", "Column内部重组6666"<span style="color: rgba(0, 0, 0, 1)">)
            Text(text </span>= "重组",fontSize = 36<span style="color: rgba(0, 0, 0, 1)">.sp)
            Text(text </span>= count.value.toString(),fontSize = 36.sp, modifier =<span style="color: rgba(0, 0, 0, 1)"> Modifier.clickable {
                count.value</span>++<span style="color: rgba(0, 0, 0, 1)">
                Log.e(</span>"zh", "Text: 重组7777"<span style="color: rgba(0, 0, 0, 1)">)
            })
      }
    }</span></pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>2022-12-30 15:40:14.2447889-7889<span style="color: rgba(0, 0, 0, 1)">zh                      com.lwlx.composedemo               EText: 重组7777
</span>2022-12-30 15:40:14.2627889-7889<span style="color: rgba(0, 0, 0, 1)">zh                      com.lwlx.composedemo               E外部重组1111
</span>2022-12-30 15:40:14.2637889-7889<span style="color: rgba(0, 0, 0, 1)">zh                      com.lwlx.composedemo               E外部重组5555
</span>2022-12-30 15:40:14.2657889-7889<span style="color: rgba(0, 0, 0, 1)">zh                      com.lwlx.composedemo               EColumn内部重组6666
</span>2022-12-30 15:40:14.3037889-7889<span style="color: rgba(0, 0, 0, 1)">zh                      com.lwlx.composedemo               EDisposableEffect: 重组4444
</span>2022-12-30 15:40:14.3037889-7889<span style="color: rgba(0, 0, 0, 1)">zh                      com.lwlx.composedemo               ESideEffect: 重组2222
</span>2022-12-30 15:40:14.3207889-7889zh                      com.lwlx.composedemo               ELaunchedEffect: 重组3333</pre>
</div>
<p>&nbsp;</p>
<p>Android Compose 框架的状态与 ViewModel 的协同(collectAsState)深入剖析(22) - 知乎</p>
<p><span style="color: rgba(0, 0, 0, 1)"><span class="devsite-heading">End</span></span></p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16939623.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/16939623.html
頁: [1]
查看完整版本: Android开发 Jetpack_Compose_6 附带效应