Android,无法从分离的片段访问 ViewModel

Android, Can't access ViewModels from detached fragment

每次我旋转 phone 时,它都会崩溃并在 SearchResultFragment 中打印奇怪的异常 不管 isDetached value is false:

java.lang.IllegalStateException: Can't access ViewModels from detached fragment

在这部分代码中:

private val searchViewModel: SearchViewModel by viewModel()

fun searchWord(word: String) {
        if(!isDetached) searchResultViewModel.searchWord(word)
}

从另一个片段 SearchFragment 调用:

searchResultFragment.searchWord(searchWord)

SearchResultFragment是这样添加到SearchFragment中的:

private val searchResultFragment = SearchResultFragment()

private fun addFragment() {
    val fragmentTransaction = childFragmentManager.beginTransaction()
    fragmentTransaction.add(R.id.searchContainer, searchResultFragment)
    fragmentTransaction.commit()
}

我正在使用 Koin 进行依赖注入。我将不胜感激所有提示。

如果没有完整的上下文,很难准确了解发生了什么。

我看到的第一个问题(也被其他一些用户提到)是您似乎在从另一个片段调用片段中的 public 方法。这是一个危险信号,因为您现在将两个生命周期独立的对象相互耦合(除其他外)。

如果有的话,快速摆脱这种情况的方法是您可以改为依赖 共享视图模型,它与两个 Fragment 对话并可以提供您想要的状态。

无论如何,由于很难说清到底发生了什么,您可以在 onCreateView(...)...

中尝试类似的操作
 if (savedInstanceState == null) {
            supportFragmentManager.commit {
                replace(
                    R.id.searchContainer,
                    SearchResultFragment.newInstance(),
                    SearchResultFragment::class.java.simpleName
                )
            }
        }

newInstance() 在 SearchResultFragment 中实现为:

    companion object {
        fun newInstance() = SearchResultFragment()
    }

接下来,由于我们不知道完整的情况,您可以改为尝试查看 Fragment Manager 是否已经有您的片段...

类似这样的东西(伪代码...)

        if (savedInstanceState == null) {
            val newFragment = supportFragmentManager.findFragmentByTag(SearchResultFragment::class.java.simpleName) ?: SearchResultFragment.newInstance()
            
            supportFragmentManager.commit {
                replace(
                    R.id.simple_fragment_container,
                    newFragment,
                    SearchResultFragment::class.java.simpleName
                )
            }
        }

这样,您添加带有标记的片段(只是一个字符串来标识它们),然后您检查片段管理器是否已经拥有它并使用它,而不是总是创建一个新实例。

最后但同样重要的是,这可能会受到许多其他(外部)因素的影响:

  1. Fragment/Appcompat/AndroidX/etc 的版本。您正在使用(您可能知道,Fragment Manager 不是 Android 中最好的 class... 一直在进行许多修复和更改)。

  2. Android 版本:一些 Android API 比其他 API 的“碎片运气”更好(尤其是在我们拥有单独的工件来更改“碎片”版本之前) .

  3. 我认为 Koin 对此没有任何补充,但请确保您至少使用最新的稳定版。

  4. 也许你能做的最好的事情就是依赖 ViewModel 而不是所有这些 if (!detached) {...} 东西。你真的在与框架作斗争并沿着 FragmentManager 摆脱,有时甚至 Google 都不敢做...