组合创建新的 ViewModel 实例

Compose creating new ViewModel instance

我在一个慢慢采用 Jetpack Compose 的项目中工作。它主要是一个 Activity 多个 Fragment 应用程序,我们在其中使用 Android 的导航组件来处理到每个屏幕 (Fragment) 的转换。只要有可能,我们只会用可组合项替换 Fragments 的 XML 布局。

目前案件处理情况如下:

一个。 Fragments 显示可组合项并处理导航:

class ScreenFragment : Fragment() {

    // For observing events that trigger navigation
    private val viewModel by lazyViewModel { ScreenViewModel() }

    override fun onCreateView( ... ): View {
        return ComposeView(requireContext()).apply {

            setViewCompositionStrategy(DisposeOnLifecycleDestroyed(viewLifecycleOwner))

            setContent {
                AppTheme {
                    Screen(onBackPressed = { findNavController().navigateUp() })
                }
            }
        }
    }    
    ...

}

乙。处理其他所有 UI 相关的可组合项:

@Composable
fun CreatePassword(
    onBackPressed: () -> Unit,
) {

    // For observing UI states and events
    val viewModel: CreatePasswordViewModel = viewModel()
    ...
}

如您所见,我们的 Fragment 和可组合项中都有屏幕 ViewModel 的参考。到目前为止,这一直运行良好,可组合的 viewModel() 函数总是返回 FragmentViewModel.

的相同现有实例

当我们需要在可组合项上引用范围为 ActivityViewModel 时,问题就来了:

  1. 我们在 Activity 上创建 ViewModel
class MainActivity : AppCompatActivity() {

    private val viewModel by lazyViewModel { MainViewModel() }
    ...
}
  1. Fragment 上为 MainActivityViewModel 获取参考
class MainFragment : Fragment() {

    private val viewModel: MainViewModel by viewModels(::requireActivity)
    ...
}
  1. 如上所示,我们获得了可组合项的 ViewModel 引用(项目 B)

通过这样做,Fragment 具有与 Activity 相同的实例,但可组合项没有。

我的问题是, 是否可以在可组合项中获取 ActivityViewModel 的引用? 现在我只是传递 FragmentViewModel 作为我的主要可组合屏幕的参数。

我通过向我的可组合项提供 ViewModelStoreOwner 来设法传递 ActivityViewModel,如下所示:

class ScreenFragment : Fragment() {

    // For observing events that trigger navigation
    private val viewModel by lazyViewModel { ScreenViewModel() }

    override fun onCreateView( ... ): View {
        return ComposeView(requireContext()).apply {

            setViewCompositionStrategy(DisposeOnLifecycleDestroyed(viewLifecycleOwner))

            setContent {
                val viewModelStoreOwner =
                    compositionLocalOf<ViewModelStoreOwner> { requireActivity() }
    
                AppTheme {
                    CompositionLocalProvider(
                        LocalViewModelStoreOwner provides viewModelStoreOwner.current
                    ) {
                        Screen(onBackPressed = { findNavController().navigateUp() })
                    }
                }
            }
        }
    }    
    ...

}

听起来像是一个经典的依赖问题,此时我会考虑使用某种依赖注入库,并确保将 viewModel 实例作为单例提供。

我可以推荐 Koin: https://insert-koin.io/

或者,最近流行的 Hilt: https://developer.android.com/training/dependency-injection/hilt-android

使用良好的 DI 后,您可以在撰写函数中获得 viewModel 的实例。