【问题标题】:Compose creating new ViewModel instance撰写创建新的 ViewModel 实例
【发布时间】:2022-02-12 18:14:39
【问题描述】:

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

到目前为止,案件的处理方式如下:

A. 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() })
                }
            }
        }
    }    
    ...

}

B.可组合处理与 UI 相关的所有其他内容:

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

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

如您所见,我们的 Fragments 和 Composables 中都有屏幕 ViewModel 的引用。到目前为止,这一直运行良好,可组合的 viewModel() 函数始终返回 FragmentViewModel 的相同现有实例。

当我们需要将 ViewModel 的引用范围限定为可组合对象上的 Activity 时,问题就出现了:

  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 作为参数传递给我的主要可组合屏幕。

【问题讨论】:

  • viewModel()ViewModelStoreOwner 作为参数来控制该 ViewModel 的范围,就像您的 lazyViewModel 一样。如果您想将 ViewModel 限定为您的 Activity,为什么不将您的 Activity 传递给 viewModel()
  • 为什么不将视图模型作为参数传递给 CreatePassword 组合?
  • @ianhanniballake,我尝试将我的Activity 传递给viewModel(),但我最终也得到了一个新的viewModel 实例。我得到了我的Activity,如下所示:stackoverflow.com/a/68423182/8189765
  • @MobinYardim 这正是我现在正在做的事情。

标签: android android-jetpack-compose android-viewmodel


【解决方案1】:

我设法通过向我的可组合提供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() })
                    }
                }
            }
        }
    }    
    ...

}

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-17
      • 2021-11-28
      • 2017-12-13
      • 1970-01-01
      相关资源
      最近更新 更多