You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Hilt在Activity与Compose函数间共享同一ViewModel实例

解决Activity与Compose共享同一Hilt ViewModel实例的问题

问题根源

默认情况下,在NavHost包裹的Compose屏幕中调用hiltViewModel()时,ViewModel会绑定到NavBackStackEntry(导航栈条目)作为ViewModelStoreOwner;而Activity中通过by viewModels()获取的ViewModel是绑定到Activity本身的。两者作用域不同,因此会生成两个独立的ViewModel实例,导致状态无法同步。

解决方案:指定Compose使用Activity作为ViewModelStoreOwner

既然需要保留Activity中的ViewModel引用,直接在Compose组件中指定ViewModelStoreOwner为当前Activity,就能复用Activity中创建的ViewModel实例。

步骤1:修改Activity代码,传递ViewModelStoreOwner到Compose

@AndroidEntryPoint
class ContactsActivity : AppCompatActivity() {
    // 保留Activity中的ViewModel引用
    private val viewModel: ContactsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化联系人Loader逻辑
        loaderManager.initLoader(CONTACTS_LOADER_ID, null, contactsLoaderCallbacks)
        
        setContent {
            ContactsAppTheme {
                // 将Activity自身作为ViewModelStoreOwner传递给Compose屏幕
                ContactsListScreen(viewModelStoreOwner = this)
            }
        }
    }

    private val contactsLoaderCallbacks = object : LoaderManager.LoaderCallbacks<Cursor> {
        override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
            // 实现联系人Loader创建逻辑
            return CursorLoader(
                this@ContactsActivity,
                ContactsContract.Contacts.CONTENT_URI,
                arrayOf(ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY),
                null,
                null,
                "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} ASC"
            )
        }

        override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
            // 将Cursor传递给ViewModel处理
            viewModel.processContactsCursor(data)
        }

        override fun onLoaderReset(loader: Loader<Cursor>) {
            // 重置Cursor相关资源
            viewModel.clearContacts()
        }
    }

    companion object {
        private const val CONTACTS_LOADER_ID = 1001
    }
}

步骤2:修改Compose组件,使用传入的ViewModelStoreOwner获取ViewModel

@Composable
fun ContactsListScreen(
    viewModelStoreOwner: ViewModelStoreOwner
) {
    // 通过指定的ViewModelStoreOwner获取与Activity共享的ViewModel实例
    val viewModel: ContactsViewModel = hiltViewModel(viewModelStoreOwner)
    // 收集StateFlow状态,确保生命周期安全
    val uiState by viewModel.contactsListUiState.collectAsStateWithLifecycle()

    LazyColumn(modifier = Modifier.fillMaxSize()) {
        if (uiState.isLoading) {
            item {
                Text(text = "加载中...", modifier = Modifier.padding(16.dp))
            }
        } else {
            items(uiState.contacts) { contact ->
                Text(text = contact.name, modifier = Modifier.padding(16.dp))
            }
        }
    }
}

// 示例联系人数据类与UI状态
data class Contact(val id: String, val name: String)
data class ContactsListUiState(
    val isLoading: Boolean = true,
    val contacts: List<Contact> = emptyList()
)

步骤3:确保ViewModel使用@HiltViewModel注解

@HiltViewModel
class ContactsViewModel @Inject constructor() : ViewModel() {
    private val _contactsListUiState = MutableStateFlow(ContactsListUiState())
    val contactsListUiState: StateFlow<ContactsListUiState> = _contactsListUiState.asStateFlow()

    fun processContactsCursor(cursor: Cursor?) {
        val contacts = mutableListOf<Contact>()
        cursor?.use {
            while (it.moveToNext()) {
                val id = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
                val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
                contacts.add(Contact(id, name))
            }
        }
        _contactsListUiState.value = _contactsListUiState.value.copy(
            isLoading = false,
            contacts = contacts
        )
    }

    fun clearContacts() {
        _contactsListUiState.value = ContactsListUiState()
    }
}

关键说明

  • ViewModelStoreOwner是ViewModel的作用域载体,Activity实现了该接口,指定它作为owner后,Compose会复用Activity中已创建的ViewModel实例。
  • 使用collectAsStateWithLifecycle()替代普通的collectAsState(),可以确保在Activity进入后台时自动停止收集状态,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Yarin Shitrit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 01:42:50