可以通过 Fragment 访问 Activity 的 AndroidViewModel 吗?
Possible to access AndroidViewModel of Activity via Fragment?
去年夏天,我开始使用 Android 的架构组件(Room、ViewModel、LiveData)重构我的 Android 应用程序。
我有两个 Room 存储库,其中一个被应用程序的多个视图(片段)访问。因此,我使用了一个 AndroidViewModel
,它可以访问这个存储库并在我的 MainActivity
.
中初始化
new ViewModelProvider(this).get(CanteensViewModel.class);
在我的两个片段中,我通过
访问了这个 ViewModel
new ViewModelProvider(getActivity()).get(CanteensViewModel.class);
直到昨天,它才完美运行。但是后来我更新了我的依赖项,因为 androidx.lifecycle
版本 2.2.0 这不再起作用了。我总是遇到异常(siehe EDIT 2):
Caused by: java.lang.InstantiationException: java.lang.Class<com.(...).CanteensViewModel> has no zero argument constructor
所以我检查了 docs,据我所知,我 should/could 现在使用
ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication()).create(CanteensViewModel.class);
获取我的 ViewModel。但是使用这种方法我无法添加 owner
(ViewModelProvider
s 构造函数的参数),这导致了问题,我无法真正访问我在 [=62= 中创建的 ViewModel ] 从我的碎片里面。
有没有办法可以从片段内部访问 Activity 的 ViewModel?或者 重新创建 每个片段中的 ViewModel
会更好吗
ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication()).create(CanteensViewModel.class);
而不是在 Activity?
中创建它
编辑:
当我使用 ViewModelProvider
的另一个 constructor 时,它似乎有效,其中 AndroidViewModelFactory
是第二个参数。
new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication())).get(CanteensViewModel.class);
在我的 MainActivity
中执行此操作我可以通过
访问我的 Fragment
中的 CanteensViewModel
new ViewModelProvider(requireActivity()).get(CanteensViewModel.class);
编辑 2
上述异常的堆栈跟踪:
2020-02-28 14:30:16.098 25279-25279/com.pasta.mensadd E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.pasta.mensadd, PID: 25279
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.pasta.mensadd/com.pasta.mensadd.ui.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.pasta.mensadd.ui.viewmodel.CanteensViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2795)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2873)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6543)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.pasta.mensadd.ui.viewmodel.CanteensViewModel
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.pasta.mensadd.ui.MainActivity.onCreate(MainActivity.java:70)
at android.app.Activity.performCreate(Activity.java:7023)
at android.app.Activity.performCreate(Activity.java:7014)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2748)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2873)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6543)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.InstantiationException: java.lang.Class<com.pasta.mensadd.ui.viewmodel.CanteensViewModel> has no zero argument constructor
at java.lang.Class.newInstance(Native Method)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.pasta.mensadd.ui.MainActivity.onCreate(MainActivity.java:70)
at android.app.Activity.performCreate(Activity.java:7023)
at android.app.Activity.performCreate(Activity.java:7014)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2748)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2873)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6543)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
```
So I checked the docs and as I understood right I should now use
ViewModelProvider.AndroidViewModelFactory.getInstance(
this.getApplication()).create(CanteensViewModel.class);
请分享一个 link 给你提到的这个 "docs",因为这不是我第一次看到这段代码,但在这两种情况下它都同样错误。
您实际应该使用的代码是
new ViewModelProvider(this).get(CanteensViewModel.class);
Is there a way I can access the Activity's ViewModel from inside the fragments? Or would it be better to recreate the ViewModel in each fragment by
new ViewModelProvider(requireActivity()).get(CanteensViewModel.class);
考虑 also receiving a SavedStateHandle
as an argument in your AndroidViewModel
, and not only Application
。
如果你问我,显然删除 ViewModelProviders.of()
是一个 API 错误,但这就是我们现在所拥有的。
编辑:在提供的堆栈跟踪的帮助下,我终于可以弄清楚发生了什么。
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
我们使用 NewInstanceFactory
作为默认值。默认 NewInstanceFactory
有什么作用? It just calls no-arg constructor 如果可用。
等等,什么? AndroidViewModel
不应该填Application
吗?
理论上是可以的,只要你得到原来的默认ViewModelProvider.Factory
,但不是这个!
为什么不是能填AndroidViewModel的那个?
Add default ViewModel Factory interface
Use a marker interface to allow instances of
ViewModelStoreOwner, such as ComponentActivity
and Fragment, to provide a default
ViewModelProvider.Factory that can be used with
a new, concise ViewModelProvider constructor.
This updates ComponentActivity and Fragment to
use that new API to provide an
AndroidViewModelFactory by default. It updates
the 'by viewModels' Kotlin extensions to use
this default Factory if one isn't explicitly
provided.
还有
ComponentActivity:
+ @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());
+ }
+
最重要的是
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
这意味着您获得了可以正确设置 AndroidViewModel 的默认视图模型提供程序工厂如果 ViewModelStoreOwner 实现HasDefaultViewModelProviderFactory
.
理论上,ComponentActivity
确实是一个HasDefaultViewModelProviderFactory
; AppCompatActivity
从 ComponentActivity
.
延伸
然而,在您的情况下,情况似乎并非如此。出于某种原因,您的 AppCompatActivity
不是 HasDefaultViewModelProviderFactory
。
我认为您的问题的解决方案是将 Lifecycle 更新到 2.2.0,并将 implementation 'androidx.core:core-ktx
至少更新到 1.2.0。 (特别是至少 AndroidX-Activity 1.1.0 和 AndroidX-Fragment 1.2.0)。
在搜索类似问题时偶然发现了这个线程,但就我而言,我只是想从我的 activity 中获取 AndroidViewModel
的实例。我遇到了相同的零构造函数错误。添加 implementation "androidx.fragment:fragment-ktx:1.2.5"
解决了我的问题,即使我没有在我的应用程序中使用任何片段。
去年夏天,我开始使用 Android 的架构组件(Room、ViewModel、LiveData)重构我的 Android 应用程序。
我有两个 Room 存储库,其中一个被应用程序的多个视图(片段)访问。因此,我使用了一个 AndroidViewModel
,它可以访问这个存储库并在我的 MainActivity
.
new ViewModelProvider(this).get(CanteensViewModel.class);
在我的两个片段中,我通过
访问了这个 ViewModelnew ViewModelProvider(getActivity()).get(CanteensViewModel.class);
直到昨天,它才完美运行。但是后来我更新了我的依赖项,因为 androidx.lifecycle
版本 2.2.0 这不再起作用了。我总是遇到异常(siehe EDIT 2):
Caused by: java.lang.InstantiationException: java.lang.Class<com.(...).CanteensViewModel> has no zero argument constructor
所以我检查了 docs,据我所知,我 should/could 现在使用
ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication()).create(CanteensViewModel.class);
获取我的 ViewModel。但是使用这种方法我无法添加 owner
(ViewModelProvider
s 构造函数的参数),这导致了问题,我无法真正访问我在 [=62= 中创建的 ViewModel ] 从我的碎片里面。
有没有办法可以从片段内部访问 Activity 的 ViewModel?或者 重新创建 每个片段中的 ViewModel
会更好吗ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication()).create(CanteensViewModel.class);
而不是在 Activity?
中创建它编辑:
当我使用 ViewModelProvider
的另一个 constructor 时,它似乎有效,其中 AndroidViewModelFactory
是第二个参数。
new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication())).get(CanteensViewModel.class);
在我的 MainActivity
中执行此操作我可以通过
Fragment
中的 CanteensViewModel
new ViewModelProvider(requireActivity()).get(CanteensViewModel.class);
编辑 2 上述异常的堆栈跟踪:
2020-02-28 14:30:16.098 25279-25279/com.pasta.mensadd E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.pasta.mensadd, PID: 25279
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.pasta.mensadd/com.pasta.mensadd.ui.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.pasta.mensadd.ui.viewmodel.CanteensViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2795)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2873)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6543)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.pasta.mensadd.ui.viewmodel.CanteensViewModel
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.pasta.mensadd.ui.MainActivity.onCreate(MainActivity.java:70)
at android.app.Activity.performCreate(Activity.java:7023)
at android.app.Activity.performCreate(Activity.java:7014)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2748)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2873)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6543)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.InstantiationException: java.lang.Class<com.pasta.mensadd.ui.viewmodel.CanteensViewModel> has no zero argument constructor
at java.lang.Class.newInstance(Native Method)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.pasta.mensadd.ui.MainActivity.onCreate(MainActivity.java:70)
at android.app.Activity.performCreate(Activity.java:7023)
at android.app.Activity.performCreate(Activity.java:7014)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2748)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2873)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1602)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6543)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
```
So I checked the docs and as I understood right I should now use
ViewModelProvider.AndroidViewModelFactory.getInstance( this.getApplication()).create(CanteensViewModel.class);
请分享一个 link 给你提到的这个 "docs",因为这不是我第一次看到这段代码,但在这两种情况下它都同样错误。
您实际应该使用的代码是
new ViewModelProvider(this).get(CanteensViewModel.class);
Is there a way I can access the Activity's ViewModel from inside the fragments? Or would it be better to recreate the ViewModel in each fragment by
new ViewModelProvider(requireActivity()).get(CanteensViewModel.class);
考虑 also receiving a SavedStateHandle
as an argument in your AndroidViewModel
, and not only Application
。
如果你问我,显然删除 ViewModelProviders.of()
是一个 API 错误,但这就是我们现在所拥有的。
编辑:在提供的堆栈跟踪的帮助下,我终于可以弄清楚发生了什么。
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
我们使用 NewInstanceFactory
作为默认值。默认 NewInstanceFactory
有什么作用? It just calls no-arg constructor 如果可用。
等等,什么? AndroidViewModel
不应该填Application
吗?
理论上是可以的,只要你得到原来的默认ViewModelProvider.Factory
,但不是这个!
为什么不是能填AndroidViewModel的那个?
Add default ViewModel Factory interface Use a marker interface to allow instances of ViewModelStoreOwner, such as ComponentActivity and Fragment, to provide a default ViewModelProvider.Factory that can be used with a new, concise ViewModelProvider constructor. This updates ComponentActivity and Fragment to use that new API to provide an AndroidViewModelFactory by default. It updates the 'by viewModels' Kotlin extensions to use this default Factory if one isn't explicitly provided.
还有
ComponentActivity: + @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()); + } +
最重要的是
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance()); }
这意味着您获得了可以正确设置 AndroidViewModel 的默认视图模型提供程序工厂如果 ViewModelStoreOwner 实现HasDefaultViewModelProviderFactory
.
理论上,ComponentActivity
确实是一个HasDefaultViewModelProviderFactory
; AppCompatActivity
从 ComponentActivity
.
然而,在您的情况下,情况似乎并非如此。出于某种原因,您的 AppCompatActivity
不是 HasDefaultViewModelProviderFactory
。
我认为您的问题的解决方案是将 Lifecycle 更新到 2.2.0,并将 implementation 'androidx.core:core-ktx
至少更新到 1.2.0。 (特别是至少 AndroidX-Activity 1.1.0 和 AndroidX-Fragment 1.2.0)。
在搜索类似问题时偶然发现了这个线程,但就我而言,我只是想从我的 activity 中获取 AndroidViewModel
的实例。我遇到了相同的零构造函数错误。添加 implementation "androidx.fragment:fragment-ktx:1.2.5"
解决了我的问题,即使我没有在我的应用程序中使用任何片段。