Yar*_*rit 5 android android-viewmodel android-jetpack-compose dagger-hilt jetpack-compose-navigation
我的应用程序使用 hilt,我LoadManager在活动内部进行了一些工作,使用读取联系人ContentResolver,当我完成工作时,我得到发送到我的 viewModel 的光标,以便处理数据并执行一些业务逻辑,为此我声明了以下内容在我的活动之上:
@AndroidEntryPoint
class MainActivity : ComponentActivity(), LoaderManager.LoaderCallbacks<Cursor> {
private val contactsViewModel: ContactsViewModel by viewModels()
...
Run Code Online (Sandbox Code Playgroud)
这样我就可以在里面使用它onLoadFinished :
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
contactsViewModel.updateContactsListFromCursor(cursor, loader.id)
}
Run Code Online (Sandbox Code Playgroud)
在我的 viewModel 中,我有以下代码,它使用要显示的联系人更新列表的 ui 状态:
data class ContactsListUiState(
val contacts: MutableList<Contact>,
val searchFilter: String)
@HiltViewModel
class ContactsViewModel @Inject constructor() : ViewModel() {
private val _contactsListUiState =
MutableStateFlow(ContactsListUiState(mutableStateListOf(), ""))
val contactsListUiState: StateFlow<ContactsListUiState> = _contactsListUiState.asStateFlow()
private fun updateContactsList(filter: String) {
viewModelScope.launch(Dispatchers.IO) {
...
_contactsListUiState.update { currentState ->
currentState.copy(contacts = list, searchFilter = filter)
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我应该显示 a 的联系人,LazyColumn并且我按照官方文档使用 hilt 将 viewModel 传递给我的可组合函数:
@Composable
fun ContactsListScreen(
navController: NavController,
modifier: Modifier = Modifier, viewModel: ContactsViewModel = hiltViewModel()
) {
val uiState by viewModel.contactsListUiState.collectAsStateWithLifecycle()
...
Run Code Online (Sandbox Code Playgroud)
但是当我访问它时uiState.contacts它是空的,我的列表没有显示任何内容,我还注意到我在活动中使用的 viewModel 实例与我从可组合函数内部contactsViewModel获得的 viewModel 实例不同,这可能会导致此问题。hiltViewModel()
关于如何在活动和可组合函数之间共享相同视图模型的任何建议,假设我必须从 onLoadFinished 函数(不可组合)调用 viewModel,在该函数中我获得光标,因此我必须在活动本身内部有一个 viewModel 引用
基于文档。
函数 hiltViewModel() 返回一个现有的 ViewModel 或创建一个新的 ViewModel,其范围仅限于 NavController 返回堆栈上存在的当前导航图。该函数可以选择使用 NavBackStackEntry 将 ViewModel 的范围限制为父返回堆栈条目。
事实证明,当工厂成为导航图的一部分时,它们会创建 ViewModel 的新实例。但由于您已经发现要使其工作,您必须指定,所以我采取了一种基于我最近在这篇文章ViewModelStoreOwner中的答案的方法,并创建了当前活动的 CompositionLocal ,因为它的扩展是它本身。ComponentActivityViewModelStoreOwner
这是我的简短尝试,通过可能的修复来重现您的问题。
活动
@AndroidEntryPoint
class HiltActivityViewModelActivity : ComponentActivity() {
private val myViewModel: ActivityScopedViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CompositionLocalProvider(LocalActivity provides this@HiltActivityViewModelActivity) {
Log.e("ActivityScopedViewModel", "Hashcode: ${myViewModel.hashCode()} : Activity Scope")
HiltActivitySampleNavHost()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型
@HiltViewModel
class ActivityScopedViewModel @Inject constructor(): ViewModel() {}
Run Code Online (Sandbox Code Playgroud)
本地活动组合
val LocalActivity = staticCompositionLocalOf<ComponentActivity> {
error("LocalActivity is not present")
}
Run Code Online (Sandbox Code Playgroud)
简单的导航图
enum class HiltSampleNavHostRoute {
DES_A, DES_B
}
@Composable
fun HiltActivitySampleNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: String = HiltSampleNavHostRoute.DES_A.name
) {
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable(HiltSampleNavHostRoute.DES_A.name) {
DestinationScreenA()
}
composable(HiltSampleNavHostRoute.DES_B.name) {
DestinationScreenB()
}
}
}
Run Code Online (Sandbox Code Playgroud)
屏幕
// here you can use the Local Activity as the ViewModelStoreOwner
@Composable
fun DestinationScreenA(
myViewModelParam: ActivityScopedViewModel = hiltViewModel(LocalActivity.current)
// myViewModelParam: ActivityScopedViewModel = viewModel(LocalActivity.current)
) {
Log.e("ActivityScopedViewModel", "Hashcode: ${myViewModelParam.hashCode()} : Composable Scope")
}
@Composable
fun DestinationScreenB(
modifier: Modifier = Modifier
) {}
Run Code Online (Sandbox Code Playgroud)
或者更好的是,就像Phil Dukhov的回答一样,您可以LocalViewModelStoreOwner在调用构建器时用作参数。
相同的导航主机
@Composable
fun HiltActivitySampleNavHost(
...
) {
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
}
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable(HiltSampleNavHostRoute.DES_A.name) {
DestinationScreenA(
myViewModelParam = viewModel(viewModelStoreOwner)
)
}
...
}
}
Run Code Online (Sandbox Code Playgroud)
导航图中的活动和可组合项的日志都显示相同的哈希码
E/ActivityScopedViewModel: Hashcode: 267094635 : Activity Scope
E/ActivityScopedViewModel: Hashcode: 267094635 : Composable Scope
Run Code Online (Sandbox Code Playgroud)
另请参阅色雷斯人的回答。它有关于 的非常详细的解释ComponentActivity,并且基于它,我认为我提出的第一个解决方案可能适合您的情况。
| 归档时间: |
|
| 查看次数: |
2814 次 |
| 最近记录: |