Comose状态的生命周期

   本文主要介绍remember、retain、rememberSaveable、rememberSerializable,以及ViewModel 的用法,以及他们之间的区别,何时使用。

1、remember

   作用域限定在组合的对象,重组后继续存在,不能在activity重建后继续存在,不能在进程中止后继续存在。
   创建内部对象,避免每次重组是创建巨大开销的对象。

@Composable
private fun rememberState() {
    val count = remember { mutableStateOf(0) }
    var counter = remember { mutableStateOf(Counter()) }

    Column {
        Text(
            text = "login  in with email ${count.value}",  // 会导致重组
            style = TextStyle.Default,
            modifier = Modifier
                .padding(vertical = 20.dp)
                .clickable {
                    count.value++
                }
        )
        Button(onClick = {
            counter.value.num ++  // 不会导致重组
        }) {
            Text("存储普通对象 : ${counter.value.num}")
        }
    }

}
class Counter(var num : Int = 0)

2、retain

  重组后对象继续存在,旋转activity重建后对象存在,不能在进程中止后继续存在。
  适合创建成本高且不可序列化的对象,如播放器Player、Bitmap、图片缓存对象等

class RetainPalyer {

    val scope = CoroutineScope(Dispatchers.IO + Job())
    var playCount = 0

    init {
        Log.d("RetainPalyer", "创建实例:${this.hashCode()}")
    }

    fun play() {
        playCount++
        Log.d("RetainPalyer", "播放,次数:$playCount")
    }

    fun release() {
        scope.cancel()
        Log.d("RetainPalyer", "释放资源:${this.hashCode()}")
    }
}

@Composable
fun RetainDemoPage() {
    Column(modifier = Modifier.padding(20.dp)) {
        Text(text = "retain vs remember 实例对比\n旋转屏幕查看日志hashCode")
        Spacer(modifier = Modifier.height(16.dp))

        val rememberPlayer = remember { RetainPalyer() }
        val retainPlayer = retain { RetainPalyer() }

        DisposableEffect(Unit) {
            onDispose {
                rememberPlayer.release()
                retainPlayer.release()
            }
        }

        Button(onClick = { rememberPlayer.play() }) {
            Text("remember播放器播放")
        }
        Spacer(modifier = Modifier.height(8.dp))
        Button(onClick = { retainPlayer.play() }) {
            Text("retain播放器播放")
        }

        Spacer(modifier = Modifier.height(24.dp))
        Text("=== 实例地址 ===")
        Text("remember hash: ${rememberPlayer.hashCode()}")
        Text("retain hash: ${retainPlayer.hashCode()}")
    }
}

   旋转之前


image.png

   旋转之后


image.png

  可以看到旋转之后,retainPlayer没有重新创建。
  2.1、同一个activity,验证跳转到另一个compose页面,retain对象是否相同。
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun MainTab(receiver : String) {
    Box(modifier = Modifier.fillMaxWidth()) {
        Scaffold(
            topBar = {
                TopAppBar(title = { Text(text = "页面2: ") }, navigationIcon = {})
            },
            content = { it ->
                Column(
                    modifier = Modifier.padding(it),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    val retainPlayer = retain { RetainPalyer() }
                    val retainPalyer1 = retain { RetainPalyer() }
                    Text(text = AnnotatedString(text = "retainPlayer hash: ${retainPlayer.hashCode()}"), modifier = Modifier.padding(horizontal = 14.dp, vertical = 14.dp) )
                    Text(text = AnnotatedString(text = "retainPlayer1 hash: ${retainPalyer1.hashCode()}"), modifier = Modifier.padding(horizontal = 14.dp, vertical = 14.dp) )
                    Text(text = AnnotatedString(text = "从上一个页面传递的hashCode ${receiver}"), modifier = Modifier.padding(horizontal = 14.dp, vertical = 14.dp))
                }
            }
        )
    }
}
image.png

   从上图可以看出,同一个compose页面创建的RetainPalyer创建的是不同的对象。不同compose页面创建RetainPalyer也不是同一个对象。retain只是在屏幕旋转时,同一位置从activity中恢复的对象是同一个。

3、rememberSaveable

   重组后对象存在,旋转activity后对象存在,进程中止后继续存在。
   老版本的API,它通过 Android 的 SavedStateRegistry 保存数据,是往 Bundle 里面存数据,适合简单的数据,需要自定义Saver。

class User {
    var name: String = ""
    var age: Int = 0;

    constructor(name: String, age: Int) {
        this.age = age
        this.name = name
    }

    companion object {
        val stateSaver: Saver<MutableState<User>, List<Any?>> = Saver(
            save = { state -> listOf(state.value.name, state.value.age) },
            restore = { mutableStateOf(User(it[0] as String, it[1] as Int)) }
        )
    }
}

@Composable
private fun remeberSave() {
    var user = rememberSaveable(saver = User.stateSaver) {
        mutableStateOf(User("zhangsan", 18))
    }
    var count = rememberSaveable { mutableIntStateOf(0) }
    Column {
        Text(
            text = "保存的age值 ${user.value.age}", modifier = Modifier.clickable {
                user.value = User("lisi", 19)
            }
        )
        Text(
            text = "savecount count = ${count.value}", modifier = Modifier
                .clickable {
                    count.value++
                }
                .absolutePadding(top = 10.dp))
    }
}

4、rememberSerializable

   新版本的API,自动序列化对象,适合存储复杂的数据,比如List<Any>等。

@Serializable
class Person (val name:String,var age : Int){

 }
 var person by rememberSerializable { mutableStateOf(Person("abc", 190)) }
image.png

5、ViewModel

   在ComposeA中操作UI,当从ComposeA跳转到ComposeB,ComposeA里面保留的数据就被销毁了,当再次返回时,会重组。
  5.1、将状态保存在remember中,可以看到返回时,重组创建了新的对象。


remember

image.png

  5.2、将状态保存在ViewModel中,可以看到返回时,还是原来的ViewModel对象。


viewmodel

打印

   总结:remember 的生命周期跟随 Composition,而 ViewModel 的生命周期跟随 ViewModelStoreOwner。每一个Composition都有一个ViewModelStoreOwner。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容