1. 路由传参
Jetpack Compose 中路由传参的方式有很多种,具体可以参考 Jetpack Compose 中的导航路由
以下是最简单的路由传参测试代码:
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
@Composable
fun NavigationArgsSample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "screen1"
) {
composable("screen1") {
Screen1(onNavigateToScreen2 = {
navController.navigate("screen2/$it")
})
}
composable(
route = "screen2/{my_param}",
arguments = listOf(
navArgument("my_param") {
type = NavType.StringType
}
)
) {
val param = it.arguments?.getString("my_param") ?: ""
Screen2(param = param)
}
}
}
@Composable
private fun Screen1(onNavigateToScreen2: (String) -> Unit) {
Button(onClick = {
onNavigateToScreen2("Hello world!")
}) {
Text(text = "Click me")
}
}
@Composable
private fun Screen2(param: String) {
Text(text = param)
}
代码很简单,就是从Screen1
路由页面传一个值给Screen2
路由页面。
这种方式不能算是真正的共享数据,它只能算是 “把我的数据分享给别人” 这种概念,当参数值到达别的页面以后,就成为该页面的本地静态数据,如果你修改了该数据也不会反馈到原始路由页面中。但是只从分享数据的角度,这种方式足够简单。
另外这种方式有一个好处是可以跨越系统内存不足杀死进程的情况而存活。
我们可以通过 Android Studio Logcat 面板的 “Terminate Application” 按钮来模拟系统内存不足杀死进程的情况,但是当我打开我的 Android Studio 之后,不禁发出灵魂拷问:Where did my “Terminate Application” button gone ? 😑
好家伙,这个按钮居然没有了,如果你使用的是 Android Studio Dolphin 或者 Android Studio Flamingo 一定也会遇到这个问题,这个按钮对于模拟系统终止应用进程的场景还是非常有用的(注意它的作用不同于顶部工具栏中的stop按钮)。该怎么把它找回来呢?
不用慌,经过一番搜索,终于找到了该怎么把它找回来:Settings --> Experimental --> Enable new Logcat tool window. 将这个选项的打勾取消即可。
然后我们运行应用,将路由跳转到Screen2
然后回到后台,这时点击 Terminate Application 模拟系统杀进程,效果如下:
可以发现这种方式确实可以在系统回收应用进程后,再次打开应用时,仍然保持之前的页面状态。
这种方式缺点也很明显,假如我们路由导航路径上有 7 个屏幕,不可能将数据从第一个传到第七个,那样太麻烦了。
2. 共享ViewModel
我们知道ViewModel
可以跨越Activity
的配置变更而存活,因此它是一个Activity
中不同的Composable
组件之间共享数据的绝佳方案:
实际上ViewModel
的作用域被限定为一个离他最近的 ViewModelStoreOwner
的 Lifecycle
。它会一直保留在内存中,直到其 ViewModelStoreOwner
永久消失。
ViewModelStoreOwner
对象可能是一个 Activity
、Fragment
或者一个 Navigation
导航图(NavBackStackEntry
),具体取决于使用的场景。
如果我们打算在一个导航图之内的不同路由之间通过ViewModel
来共享数据,就可以将其作用域限定为该导航图。
下面开始测试,首先创建一个用于共享的SharedViewModel
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class SharedViewModel: ViewModel() {
private val _sharedState = MutableStateFlow(0)
val sharedState = _sharedState.asStateFlow()
fun updateState() {
_sharedState.value++
}
override fun onCleared() {
super.onCleared()
println("ViewModel cleared")
}
}
测试代码:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navigation
import com.fly.mycompose.application.examples.sharedata.model.SharedViewModel
@Composable
fun SharedViewModelSample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "onboarding"
) {
navigation(
startDestination = "personal_details",
route = "onboarding"
) {
composable(