AndroidViewModel与SavedState

8

我需要使用带应用程序上下文和SavedStateHandleAndroidViewModel。我已经使用应用程序上下文使其正常工作,但是我无法添加一个SavedStateHandle

这是我现在拥有的,仅使用了应用程序上下文:

// A1. get ViewModel in Fragment
val viewModel = ViewModelProvider(viewLifecycleOwner).get(MyViewModel::class.java)

// A2. MyViewModel derives from my custom BaseAndroidViewModel
class MyViewModel(application: Application) :BaseAndroidViewModel(application)

// A3. BaseAndroidViewModel in turn derives from AndroidViewModel
open class BaseAndroidViewModel(application: Application) : AndroidViewModel(application)

我认为在这个问题中,可能可以将其简化为:
// B1. get ViewModel in Fragment
val viewModel = ViewModelProvider(viewLifecycleOwner).get(MyViewModel::class.java)

// B2. BaseAndroidViewModel in turn derives from AndroidViewModel
class MyViewModel(application: Application) : AndroidViewModel(application) 

因此,如果我的MyViewModel中也有一个SavedStateHandle,我需要如何修改片段中的调用(示例代码中的B1行)?我是否需要显式调用工厂SavedStateViewModelFactory?它看起来会是什么样子?(我还是 Kotlin/Android 的新手,以前从未使用过工厂)

1个回答

7

编辑:AndroidX-Activity 1.2.0AndroidX-Fragment 1.1.0的最终版本中,他们将SavedStateViewModelFactory设置为AppCompatActivity/Fragment的默认工厂,因此不需要覆盖默认工厂(这是回答后半部分所做的事情)。

更新然后使用。

class MyViewModel(val savedStateHandle: SavedStateHandle): ViewModel()

class MyAndroidViewModel(application: Application, val savedStateHandle: SavedStateHandle): AndroidViewModel(application)

应该可以正常工作。


翻译:

我需要如何修改片段中的调用(示例代码中的B1行)?我是否需要显式调用工厂SavedStateViewModelFactory?具体是什么样子?

在AndroidX-Activity 1.2.0中,他们添加了一个名为getDefaultViewModelProviderFactory()的新方法:

+    @NonNull
+    @Override
+    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+        if (getApplication() == null) {
+            throw new IllegalStateException("Your activity is not yet attached to the "
+                    + "Application instance. You can't request ViewModel before onCreate call.");
+        }
+        return ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication());
+    }
+
这意味着如果我拥有一个名为BaseActivityBaseFragment 的类,那么我可以使用viewmodel-savedstate库中的SavedStateViewModelFactory来替换它们。
class BaseActivity: AppCompatActivity() {
    override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory = 
        SavedStateViewModelFactory(application, this, intent?.extras ?: Bundle())
}

class BaseFragment: Fragment() {
    override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory = 
        SavedStateViewModelFactory(requireActivity().application, this, arguments ?: Bundle())
}

有了这个之后,你就可以依赖自动实例化的 ViewModel,并将 SavedStateHandle 作为它们的参数之一:

class MyViewModel(val savedStateHandle: SavedStateHandle): ViewModel()

class MyAndroidViewModel(application: Application, val savedStateHandle: SavedStateHandle): AndroidViewModel(application)

请注意,SavedStateViewModelFactory 期望的顺序是 application, savedStateHandle

但是,如果您需要除此之外自定义参数,则必须在调用 ViewModelProvider(viewModelStoreOwner).get(...) 方法时提供一个 object: AbstractSavedStateViewModelFactory 参数。


谢谢您的快速和详细的回复 :-) 在 BaseFragmentgetDefaultViewModelProviderFactory 覆盖中有一个轻微的复制和粘贴错误:applicationFragment 中不是直接可用的。我认为它应该是 activity?.application!!- 您能确认一下吗?是否有更好的方法?所有这些空值处理看起来有点丑陋 ;-) - stefan.at.kotlin
我添加了 requireActivity().application,但如果在 Activity 不可用时调用它,那将非常令人失望。ViewModel 应该在 onCreate 中初始化,但如果此时 Activity 不可用,则必须在 onAttach(Context) 中获取 application... 据我所知,在 onCreate 中是可以使用 activity 的。 - EpicPandaForce
1
谢谢。我学到了新东西:默认工厂也可以传递 SavedStateHandle。所以最终我只将 SavedStateHandle 作为我的 ViewModel 的第二个参数添加了进去。引用:“当使用 Fragment 1.2.0 或其传递依赖项 Activity 1.1.0 时,ViewModel 实例的默认工厂支持在不需要任何其他配置的情况下向您的 ViewModel 传递适当的 SavedStateHandle。”(来源:https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate)我猜这也包括了我观察到的内容。 - stefan.at.kotlin
哦,所以他们把SavedStateViewModelFactory设为默认值了?那么你甚至不需要覆盖任何东西。这是个好消息,我会更新帖子的。我之前并不知道这一点。 - EpicPandaForce
是的,看起来是这样。谢谢你的更新 :-) 其实我的原始错误是试图将保存状态句柄传递给 AndroidViewModel ;-) - stefan.at.kotlin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接