我的应用程序使用句柄,我在我的活动中使用LoadManager读取联系人,使用ContentResolver读取联系人,当我完成工作时,我会将光标发送到我的viewModel,以便处理数据并执行一些业务逻辑,为此,我在我的活动之上声明了以下内容:

@AndroidEntryPoint
class MainActivity : ComponentActivity(), LoaderManager.LoaderCallbacks<Cursor> {
    private val contactsViewModel: ContactsViewModel by viewModels()
 ...

这样我就可以在onLoadFinished内使用它:

    override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
  
                contactsViewModel.updateContactsListFromCursor(cursor, loader.id)
     }

在我的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)
            }
        }

最后,我应该按照官方文档使用句柄显示LazyColumn和我将viewModel传递给我的可组合函数的联系人:

@Composable
fun ContactsListScreen(
       navController: NavController,
       modifier: Modifier = Modifier, viewModel: ContactsViewModel = hiltViewModel()
   ) {
       val uiState by viewModel.contactsListUiState.collectAsStateWithLifecycle()
       ...

但当我访问uiState.contacts时,它是空的,我的列表没有显示任何内容,我还注意到,我在活动中使用的contactsViewModel与我从Composable函数内的hiltViewModel()获得的viewModel实例不同,这可能会导致此问题.

如何在活动和可组合函数之间共享sameViewModel的任何建议假设我必须从获取游标的onLoadFinded函数(该函数不可组合)调用viewModel,因此我必须在活动本身内有一个viewModel引用

推荐答案

docs为基数.

函数的作用是:返回现有的视图模型或creates a new one scoped to the current navigation graph present on the NavController back stack.该函数可以 Select 性地接受 NavBackStackEntry将视图模型的范围设置为父后台堆栈条目.

事实证明,当工厂是导航图的一部分时,它们会创建一个新的视图模型实例.但是,因为您已经发现要使它工作,您必须指定视图模型StoreOwner,所以我根据最近对这post的回答采取了一种方法,并创建了当前活动的CompostionLocal,因为它将Component活动扩展为视图模型StoreOwner本身.

这是我的简短try ,用可能的修复方法重现您的问题.

活动

@AndroidEntryPoint
class Hilt活动视图模型活动 : Component活动() {

    private val my视图模型: 活动Scoped视图模型 by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
                CompositionLocalProvider(Local活动 provides this@Hilt活动视图模型活动) {
                    Log.e("活动Scoped视图模型", "Hashcode: ${my视图模型.hashCode()} : 活动 Scope")
                    Hilt活动SampleNavHost()
            }
        }
    }
}

视图模型

@Hilt视图模型
class 活动Scoped视图模型 @Inject constructor(): 视图模型() {}

Local 活动 Composition

val Local活动 = staticCompositionLocalOf<Component活动> {
    error("Local活动 is not present")
}

简单导航图

enum class HiltSampleNavHostRoute {
    DES_A, DES_B
}

@Composable
fun Hilt活动SampleNavHost(
    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()
        }
    }
}

屏风

// here you can use the Local 活动 as the 视图模型StoreOwner
@Composable
fun DestinationScreenA(
    my视图模型Param: 活动Scoped视图模型 = hilt视图模型(Local活动.current)
    // my视图模型Param: 活动Scoped视图模型 = viewModel(Local活动.current)
) {
    Log.e("活动Scoped视图模型", "Hashcode: ${my视图模型Param.hashCode()} : Composable Scope")
}

@Composable
fun DestinationScreenB(
    modifier: Modifier = Modifier
) {}

Or better yet, like from this answer by Phil Dukhov, you can use Local视图模型StoreOwner as the parameter when you invoke the builder.

相同的导航主机

@Composable
fun Hilt活动SampleNavHost(
    ...
) {

    val viewModelStoreOwner = checkNotNull(Local视图模型StoreOwner.current) {
        "No 视图模型StoreOwner was provided via Local视图模型StoreOwner"
    }

    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {

        composable(HiltSampleNavHostRoute.DES_A.name) {
            DestinationScreenA(
                my视图模型Param = viewModel(viewModelStoreOwner)
            )
        }

        ...
    }
}


来自活动的日志(log)和NAV图中的Composable都显示相同的哈希码

E/活动Scoped视图模型: Hashcode: 267094635 : 活动 Scope
E/活动Scoped视图模型: Hashcode: 267094635 : Composable Scope

Also have a look at Thracian's answer. It has a very detailed explanation about Component活动, and based from it I think my first proposed solution would probably work in your case.

Android相关问答推荐

译码BLE血糖仪特征值

避免在按下肯定按钮时自动取消AlertDialog

在一列中垂直对齐两个框

在androidStudio中,如何使用带有ResolutionStrategy的ResolutionSelector而不是setTargetResolve()?

在 kotlin 上向适配器添加绑定视图功能

错误:参数的类型必须是用@Entity注释的类或其集合/array. java.lang.String tocd);

Jetpack Compose的val变量不能被重新分配

如何删除 Jetpack Compose 中按钮的左边框?

在 Android Studio 中获取更新版本的 Picasso 库的错误警告消息

Koin Android-KMM:我有嵌套范围但注入不起作用

视觉转换后获取文本

我该怎么做文本计时器

如何在 Jetpack Compose 中禁用 Horizo​​ntalPager 的分页动画

如何在 Jetpack Compose 中添加多个标签

状态值更改时屏幕未重新组合 - Jetpack Compose

将房间中的实体更新为 isCompleted 并使用 Flow 问题获取所有数据

重命名列失败的房间自动迁移(NOT NULL 约束失败,生成错误的迁移类)

更新后 Firebase 服务无法在模拟器上运行

如何使用文件提供程序将视频从一个应用程序共享到另一个应用程序?

如何在 Jetpack Compose 中填充矢量图像的背景?