背景
ViewModel为页面数据管理者,一般跟随页面生命周期共存亡。在Nav3中,每个页面抽象为一个NavEntry,那么问题就变为ViewModel如何绑定NavEntry的生命周期。
在库androidx.lifecycle.viewmodel.compose中,提供viewModel()方法,用于快速构建所需要的ViewModel对象。
解决方案
安卓官方提供lifecycle-viewmodel-navigation3库,用于解决上述问题。
核心原理:lifecycle-viewmodel-navigation3将NavEntry修饰为一个LifecycleOwner对象,在调用库androidx.lifecycle.viewmodel.compose中,提供viewModel()方法时,会自动将范围最近的LifecycleOwner与ViewModel生命周期进行绑定
使用
- 环境配置
- 代码示例
/*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nav3recipes.passingarguments.viewmodels.basic
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import com.example.nav3recipes.content.ContentBlue
import com.example.nav3recipes.content.ContentGreen
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
data object RouteA
data class RouteB(val id: String)
class BasicViewModelsActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setEdgeToEdgeConfig()
super.onCreate(savedInstanceState)
setContent {
val backStack = remember { mutableStateListOf<Any>(RouteA) }
NavDisplay(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
// In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why)
// we also need to add the default `NavEntryDecorator`s as well. These provide
// extra information to the entry's content to enable it to display correctly
// and save its state.
entryDecorators = listOf(
rememberSaveableStateHolderNavEntryDecorator(),
rememberViewModelStoreNavEntryDecorator()
),
entryProvider = entryProvider {
entry<RouteA> {
ContentGreen("Welcome to Nav3") {
LazyColumn {
items(10) { i ->
Button(onClick = dropUnlessResumed {
backStack.add(RouteB("$i"))
}) {
Text("$i")
}
}
}
}
}
entry<RouteB> { key ->
// Note: We need a new ViewModel for every new RouteB instance. Usually
// we would need to supply a `key` String that is unique to the
// instance, however, the ViewModelStoreNavEntryDecorator (supplied
// above) does this for us, using `NavEntry.contentKey` to uniquely
// identify the viewModel.
//
// tl;dr: Make sure you use rememberViewModelStoreNavEntryDecorator()
// if you want a new ViewModel for each new navigation key instance.
ScreenB(viewModel = viewModel(factory = RouteBViewModel.Factory(key)))
}
}
)
}
}
}
@Composable
fun ScreenB(viewModel: RouteBViewModel = viewModel()) {
ContentBlue("Route id: ${viewModel.key.id} ")
}
class RouteBViewModel(
val key: RouteB
) : ViewModel() {
class Factory(
private val key: RouteB,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return RouteBViewModel(key) as T
}
}
}