手动清除 Android ViewModel?
Manually clearing an Android ViewModel?
编辑: 这个问题有点过时了,因为 Google 已经让我们能够将 ViewModel
范围限定到导航图。更好的方法(而不是试图清除 activity 范围的模型)是为适当数量的屏幕和范围创建特定的导航图。
参照android.arch.lifecycle.ViewModel
class.
ViewModel
的范围是它相关的 UI 组件的生命周期,因此在基于 Fragment
的应用程序中,这将是片段生命周期。这是好事。
在某些情况下,人们希望在多个片段之间共享一个 ViewModel
实例。具体来说,我对 许多屏幕与相同的基础数据相关的情况感兴趣 。
(文档建议在同一屏幕上显示多个相关片段但 时使用类似的方法。)
这在 official ViewModel documentation 中讨论:
ViewModels can also be used as a communication layer between different
Fragments of an Activity. Each Fragment can acquire the ViewModel
using the same key via their Activity. This allows communication
between Fragments in a de-coupled fashion such that they never need to
talk to the other Fragment directly.
换句话说,为了在代表不同屏幕的片段之间共享信息,ViewModel
应该限定在 Activity
生命周期内(并且根据 Android 文档,这也可以是在其他共享实例中使用)。
现在在新的 Jetpack Navigation 模式中,推荐使用 "One Activity / Many Fragments" 架构。这意味着 activity 在应用程序被使用的整个过程中都存在。
即任何范围为 Activity
生命周期的共享 ViewModel
实例将永远不会被清除 - 内存一直在使用。
为了保留内存并在任何时间点尽可能少地使用,最好能够在不再需要时清除共享的 ViewModel
个实例。
如何从 ViewModelStore
或 holder 片段中手动清除 ViewModel
?
如果您不希望 ViewModel
限定在 Activity
生命周期内,您可以将其限定在父片段的生命周期内。因此,如果您想与屏幕中的多个片段共享 ViewModel
的实例,您可以对片段进行布局,使它们都共享一个公共父片段。这样,当您实例化 ViewModel
时,您可以这样做:
CommonViewModel viewModel = ViewModelProviders.of(getParentFragment()).class(CommonViewModel.class);
希望这对您有所帮助!
通常您不会手动清除 ViewModel,因为它是自动处理的。如果您觉得需要手动清除您的 ViewModel,您可能在该 ViewModel 中做的太多了...
使用多个视图模型没有错。第一个可以限定为 Activity 而另一个可以限定为片段。
尝试仅对需要共享的内容使用 Activity 作用域视图模型。并在 Fragment Scoped Viewmodel 中放置尽可能多的东西。当片段被销毁时,片段范围的视图模型将被清除。减少整体内存占用。
如果您查看代码 here,您会发现,您可以从 ViewModelStoreOwner
和 Fragment
、FragmentActivity
中获取 ViewModelStore
例如实现,那个接口。
Soo 从那里你可以调用 viewModelStore.clear()
,正如文档所述:
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
N.B.: 这将清除特定 LifeCycleOwner 的所有可用 ViewModel,这不允许您清除一个特定的 ViewModel。
我想我有更好的解决方案。
如@Nagy Robi 所述,您可以通过调用 viewModelStore.clear()
清除 ViewModel
。这样做的问题是它将清除此 ViewModelStore
范围内的所有视图模型。换句话说,您无法控制清除哪个 ViewModel
。
但根据@mikehc here。我们实际上可以创建自己的 ViewModelStore
。这将使我们能够精细控制 ViewModel 必须存在的范围。
注意:我还没有看到有人采用这种方法,但我希望这是一种有效的方法。这将是在单个 Activity 应用程序中控制作用域的一种非常好的方法。
请就此方法提供一些反馈。任何东西都会受到赞赏。
更新:
自 Navigation Component v2.1.0-alpha02 以来,ViewModel
s 现在可以限定为流。这样做的缺点是您必须对您的项目实施 Navigation Component
,而且您无法对 ViewModel
的范围进行精细控制。但这似乎是更好的事情。
我正在编写库来解决这个问题:scoped-vm,请随时查看,我将非常感谢任何反馈。
在幕后,它使用 提到的方法 - 它为每个范围维护单独的 ViewModelStore。但它更进一步,一旦从该范围请求视图模型的最后一个片段被破坏,它就会清除 ViewModelStore 本身。
我应该说目前整个视图模型管理(尤其是这个库)受到 serious bug 与后台堆栈的影响,希望它会被修复。
总结:
- 如果您关心
ViewModel.onCleared()
没有被调用,最好的方法(目前)是自己清除它。由于该错误,您无法保证 fragment
的视图模型将永远被清除。
- 如果您只是担心泄漏
ViewModel
- 别担心,它们将像任何其他未引用的对象一样被垃圾回收。如果适合您的需要,请随意使用我的库进行细粒度范围界定。
正如所指出的那样,使用架构组件 API 无法清除 ViewModelStore 的单个 ViewModel。此问题的一种可能解决方案是拥有一个 per-ViewModel 可以在必要时安全清除的商店:
class MainActivity : AppCompatActivity() {
val individualModelStores = HashMap<KClass<out ViewModel>, ViewModelStore>()
inline fun <reified VIEWMODEL : ViewModel> getSharedViewModel(): VIEWMODEL {
val factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
//Put your existing ViewModel instantiation code here,
//e.g., dependency injection or a factory you're using
//For the simplicity of example let's assume
//that your ViewModel doesn't take any arguments
return modelClass.newInstance()
}
}
val viewModelStore = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
val viewModelStore = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
inline fun <reified VIEWMODEL : ViewModel> getIndividualViewModelStore(): ViewModelStore {
val viewModelKey = VIEWMODEL::class
var viewModelStore = individualModelStores[viewModelKey]
return if (viewModelStore != null) {
viewModelStore
} else {
viewModelStore = ViewModelStore()
individualModelStores[viewModelKey] = viewModelStore
return viewModelStore
}
}
inline fun <reified VIEWMODEL : ViewModel> clearIndividualViewModelStore() {
val viewModelKey = VIEWMODEL::class
individualModelStores[viewModelKey]?.clear()
individualModelStores.remove(viewModelKey)
}
}
使用getSharedViewModel()
获取绑定到Activity生命周期的ViewModel实例:
val yourViewModel : YourViewModel = (requireActivity() as MainActivity).getSharedViewModel(/*There could be some arguments in case of a more complex ViewModelProvider.Factory implementation*/)
稍后,当需要处理共享的 ViewModel 时,使用 clearIndividualViewModelStore<>()
:
(requireActivity() as MainActivity).clearIndividualViewModelStore<YourViewModel>()
在某些情况下,如果不再需要 ViewModel,您会希望尽快清除它(例如,如果它包含一些敏感的用户数据,如用户名或密码)。这是一种在每次片段切换时记录 individualModelStores
状态的方法,以帮助您跟踪共享的 ViewModel:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (BuildConfig.DEBUG) {
navController.addOnDestinationChangedListener { _, _, _ ->
if (individualModelStores.isNotEmpty()) {
val tag = this@MainActivity.javaClass.simpleName
Log.w(
tag,
"Don't forget to clear the shared ViewModelStores if they are not needed anymore."
)
Log.w(
tag,
"Currently there are ${individualModelStores.keys.size} ViewModelStores bound to ${this@MainActivity.javaClass.simpleName}:"
)
for ((index, viewModelClass) in individualModelStores.keys.withIndex()) {
Log.w(
tag,
"${index + 1}) $viewModelClass\n"
)
}
}
}
}
}
我找到了一种简单而优雅的方法来处理这个问题。诀窍是使用 DummyViewModel 和模型键。
该代码有效,因为 AndroidX 在 get() 上检查模型的 class 类型。如果不匹配,它会使用当前的 ViewModelProvider.Factory.
创建一个新的 ViewModel
public class MyActivity extends AppCompatActivity {
private static final String KEY_MY_MODEL = "model";
void clearMyViewModel() {
new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).
.get(KEY_MY_MODEL, DummyViewModel.class);
}
MyViewModel getMyViewModel() {
return new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()).
.get(KEY_MY_MODEL, MyViewModel.class);
}
static class DummyViewModel extends ViewModel {
//Intentionally blank
}
}
无需使用 Navigation Component
库的快速解决方案:
getActivity().getViewModelStore().clear();
这将在不合并 Navigation Component
库的情况下解决此问题。这也是简单的一行代码。它将通过 Activity
清除 Fragments
之间共享的那些 ViewModels
据我所知,您不能通过程序手动删除 ViewModel 对象,但您可以清除其中存储的数据,对于这种情况,您应该手动调用 Oncleared()
方法
为此:
- 重写从
ViewModel
class 扩展而来的 class 中的 Oncleared()
方法
- 在此方法中,您可以通过将存储数据的字段设为 null 来清理数据
- 当你想彻底清除数据时调用此方法。
在我的例子中,我观察到的大多数事情都与 View
相关,所以我不需要清除它以防 View
被破坏(而不是 Fragment
).
如果我需要 LiveData
之类的东西将我带到另一个 Fragment
(或者只做一次),我会创建一个 "consuming observer".
可以通过扩展MutableLiveData<T>
:
来完成
fun <T> MutableLiveData<T>.observeConsuming(viewLifecycleOwner: LifecycleOwner, function: (T) -> Unit) {
observe(viewLifecycleOwner, Observer<T> {
function(it ?: return@Observer)
value = null
})
}
一旦被观察到,它就会从LiveData
.
中清除
现在您可以这样称呼它:
viewModel.navigation.observeConsuming(viewLifecycleOwner) {
startActivity(Intent(this, LoginActivity::class.java))
}
正如 OP 和 Archie 所说,Google 使我们能够将 ViewModel 的范围限定为导航图。如果您已经在使用导航组件,我将在此处添加如何操作。
您可以select所有需要在导航图中组合在一起的片段和right-click->move to nested graph->new graph
现在这会将 selected 片段移动到主导航图中的嵌套图,如下所示:
<navigation app:startDestination="@id/homeFragment" ...>
<fragment android:id="@+id/homeFragment" .../>
<fragment android:id="@+id/productListFragment" .../>
<fragment android:id="@+id/productFragment" .../>
<fragment android:id="@+id/bargainFragment" .../>
<navigation
android:id="@+id/checkout_graph"
app:startDestination="@id/cartFragment">
<fragment android:id="@+id/orderSummaryFragment".../>
<fragment android:id="@+id/addressFragment" .../>
<fragment android:id="@+id/paymentFragment" .../>
<fragment android:id="@+id/cartFragment" .../>
</navigation>
</navigation>
现在,在初始化视图模型时在片段中执行此操作
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)
如果你需要传递视图模型工厂(可能是为了注入视图模型)你可以这样做:
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph) { viewModelFactory }
确保它是 R.id.checkout_graph
而不是 R.navigation.checkout_graph
出于某种原因,创建导航图并使用 include
将其嵌套在主导航图中对我不起作用。可能是一个错误。
谢谢 OP 和@Archie 为我指明了正确的方向。
好像在最新的架构组件版本中已经解决了
ViewModelProvider 具有以下构造函数:
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* @param owner a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
* retain {@code ViewModels}
* @param factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
在 Fragment 的情况下,将使用范围化的 ViewModelStore。
androidx.fragment.app.Fragment#getViewModelStore
/**
* Returns the {@link ViewModelStore} associated with this Fragment
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*
* @return a {@code ViewModelStore}
* @throws IllegalStateException if called before the Fragment is attached i.e., before
* onAttach().
*/
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
androidx.fragment.app.FragmentManagerViewModel#getViewModelStore
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
编辑: 这个问题有点过时了,因为 Google 已经让我们能够将 ViewModel
范围限定到导航图。更好的方法(而不是试图清除 activity 范围的模型)是为适当数量的屏幕和范围创建特定的导航图。
参照android.arch.lifecycle.ViewModel
class.
ViewModel
的范围是它相关的 UI 组件的生命周期,因此在基于 Fragment
的应用程序中,这将是片段生命周期。这是好事。
在某些情况下,人们希望在多个片段之间共享一个 ViewModel
实例。具体来说,我对 许多屏幕与相同的基础数据相关的情况感兴趣 。
(文档建议在同一屏幕上显示多个相关片段但
这在 official ViewModel documentation 中讨论:
ViewModels can also be used as a communication layer between different Fragments of an Activity. Each Fragment can acquire the ViewModel using the same key via their Activity. This allows communication between Fragments in a de-coupled fashion such that they never need to talk to the other Fragment directly.
换句话说,为了在代表不同屏幕的片段之间共享信息,ViewModel
应该限定在 Activity
生命周期内(并且根据 Android 文档,这也可以是在其他共享实例中使用)。
现在在新的 Jetpack Navigation 模式中,推荐使用 "One Activity / Many Fragments" 架构。这意味着 activity 在应用程序被使用的整个过程中都存在。
即任何范围为 Activity
生命周期的共享 ViewModel
实例将永远不会被清除 - 内存一直在使用。
为了保留内存并在任何时间点尽可能少地使用,最好能够在不再需要时清除共享的 ViewModel
个实例。
如何从 ViewModelStore
或 holder 片段中手动清除 ViewModel
?
如果您不希望 ViewModel
限定在 Activity
生命周期内,您可以将其限定在父片段的生命周期内。因此,如果您想与屏幕中的多个片段共享 ViewModel
的实例,您可以对片段进行布局,使它们都共享一个公共父片段。这样,当您实例化 ViewModel
时,您可以这样做:
CommonViewModel viewModel = ViewModelProviders.of(getParentFragment()).class(CommonViewModel.class);
希望这对您有所帮助!
通常您不会手动清除 ViewModel,因为它是自动处理的。如果您觉得需要手动清除您的 ViewModel,您可能在该 ViewModel 中做的太多了...
使用多个视图模型没有错。第一个可以限定为 Activity 而另一个可以限定为片段。
尝试仅对需要共享的内容使用 Activity 作用域视图模型。并在 Fragment Scoped Viewmodel 中放置尽可能多的东西。当片段被销毁时,片段范围的视图模型将被清除。减少整体内存占用。
如果您查看代码 here,您会发现,您可以从 ViewModelStoreOwner
和 Fragment
、FragmentActivity
中获取 ViewModelStore
例如实现,那个接口。
Soo 从那里你可以调用 viewModelStore.clear()
,正如文档所述:
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
N.B.: 这将清除特定 LifeCycleOwner 的所有可用 ViewModel,这不允许您清除一个特定的 ViewModel。
我想我有更好的解决方案。
如@Nagy Robi 所述,您可以通过调用 viewModelStore.clear()
清除 ViewModel
。这样做的问题是它将清除此 ViewModelStore
范围内的所有视图模型。换句话说,您无法控制清除哪个 ViewModel
。
但根据@mikehc here。我们实际上可以创建自己的 ViewModelStore
。这将使我们能够精细控制 ViewModel 必须存在的范围。
注意:我还没有看到有人采用这种方法,但我希望这是一种有效的方法。这将是在单个 Activity 应用程序中控制作用域的一种非常好的方法。
请就此方法提供一些反馈。任何东西都会受到赞赏。
更新:
自 Navigation Component v2.1.0-alpha02 以来,ViewModel
s 现在可以限定为流。这样做的缺点是您必须对您的项目实施 Navigation Component
,而且您无法对 ViewModel
的范围进行精细控制。但这似乎是更好的事情。
我正在编写库来解决这个问题:scoped-vm,请随时查看,我将非常感谢任何反馈。
在幕后,它使用
我应该说目前整个视图模型管理(尤其是这个库)受到 serious bug 与后台堆栈的影响,希望它会被修复。
总结:
- 如果您关心
ViewModel.onCleared()
没有被调用,最好的方法(目前)是自己清除它。由于该错误,您无法保证fragment
的视图模型将永远被清除。 - 如果您只是担心泄漏
ViewModel
- 别担心,它们将像任何其他未引用的对象一样被垃圾回收。如果适合您的需要,请随意使用我的库进行细粒度范围界定。
正如所指出的那样,使用架构组件 API 无法清除 ViewModelStore 的单个 ViewModel。此问题的一种可能解决方案是拥有一个 per-ViewModel 可以在必要时安全清除的商店:
class MainActivity : AppCompatActivity() {
val individualModelStores = HashMap<KClass<out ViewModel>, ViewModelStore>()
inline fun <reified VIEWMODEL : ViewModel> getSharedViewModel(): VIEWMODEL {
val factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
//Put your existing ViewModel instantiation code here,
//e.g., dependency injection or a factory you're using
//For the simplicity of example let's assume
//that your ViewModel doesn't take any arguments
return modelClass.newInstance()
}
}
val viewModelStore = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
val viewModelStore = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
inline fun <reified VIEWMODEL : ViewModel> getIndividualViewModelStore(): ViewModelStore {
val viewModelKey = VIEWMODEL::class
var viewModelStore = individualModelStores[viewModelKey]
return if (viewModelStore != null) {
viewModelStore
} else {
viewModelStore = ViewModelStore()
individualModelStores[viewModelKey] = viewModelStore
return viewModelStore
}
}
inline fun <reified VIEWMODEL : ViewModel> clearIndividualViewModelStore() {
val viewModelKey = VIEWMODEL::class
individualModelStores[viewModelKey]?.clear()
individualModelStores.remove(viewModelKey)
}
}
使用getSharedViewModel()
获取绑定到Activity生命周期的ViewModel实例:
val yourViewModel : YourViewModel = (requireActivity() as MainActivity).getSharedViewModel(/*There could be some arguments in case of a more complex ViewModelProvider.Factory implementation*/)
稍后,当需要处理共享的 ViewModel 时,使用 clearIndividualViewModelStore<>()
:
(requireActivity() as MainActivity).clearIndividualViewModelStore<YourViewModel>()
在某些情况下,如果不再需要 ViewModel,您会希望尽快清除它(例如,如果它包含一些敏感的用户数据,如用户名或密码)。这是一种在每次片段切换时记录 individualModelStores
状态的方法,以帮助您跟踪共享的 ViewModel:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (BuildConfig.DEBUG) {
navController.addOnDestinationChangedListener { _, _, _ ->
if (individualModelStores.isNotEmpty()) {
val tag = this@MainActivity.javaClass.simpleName
Log.w(
tag,
"Don't forget to clear the shared ViewModelStores if they are not needed anymore."
)
Log.w(
tag,
"Currently there are ${individualModelStores.keys.size} ViewModelStores bound to ${this@MainActivity.javaClass.simpleName}:"
)
for ((index, viewModelClass) in individualModelStores.keys.withIndex()) {
Log.w(
tag,
"${index + 1}) $viewModelClass\n"
)
}
}
}
}
}
我找到了一种简单而优雅的方法来处理这个问题。诀窍是使用 DummyViewModel 和模型键。
该代码有效,因为 AndroidX 在 get() 上检查模型的 class 类型。如果不匹配,它会使用当前的 ViewModelProvider.Factory.
创建一个新的 ViewModelpublic class MyActivity extends AppCompatActivity {
private static final String KEY_MY_MODEL = "model";
void clearMyViewModel() {
new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).
.get(KEY_MY_MODEL, DummyViewModel.class);
}
MyViewModel getMyViewModel() {
return new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()).
.get(KEY_MY_MODEL, MyViewModel.class);
}
static class DummyViewModel extends ViewModel {
//Intentionally blank
}
}
无需使用 Navigation Component
库的快速解决方案:
getActivity().getViewModelStore().clear();
这将在不合并 Navigation Component
库的情况下解决此问题。这也是简单的一行代码。它将通过 Activity
Fragments
之间共享的那些 ViewModels
据我所知,您不能通过程序手动删除 ViewModel 对象,但您可以清除其中存储的数据,对于这种情况,您应该手动调用 Oncleared()
方法
为此:
- 重写从
ViewModel
class 扩展而来的 class 中的 - 在此方法中,您可以通过将存储数据的字段设为 null 来清理数据
- 当你想彻底清除数据时调用此方法。
Oncleared()
方法
在我的例子中,我观察到的大多数事情都与 View
相关,所以我不需要清除它以防 View
被破坏(而不是 Fragment
).
如果我需要 LiveData
之类的东西将我带到另一个 Fragment
(或者只做一次),我会创建一个 "consuming observer".
可以通过扩展MutableLiveData<T>
:
fun <T> MutableLiveData<T>.observeConsuming(viewLifecycleOwner: LifecycleOwner, function: (T) -> Unit) {
observe(viewLifecycleOwner, Observer<T> {
function(it ?: return@Observer)
value = null
})
}
一旦被观察到,它就会从LiveData
.
现在您可以这样称呼它:
viewModel.navigation.observeConsuming(viewLifecycleOwner) {
startActivity(Intent(this, LoginActivity::class.java))
}
正如 OP 和 Archie 所说,Google 使我们能够将 ViewModel 的范围限定为导航图。如果您已经在使用导航组件,我将在此处添加如何操作。
您可以select所有需要在导航图中组合在一起的片段和right-click->move to nested graph->new graph
现在这会将 selected 片段移动到主导航图中的嵌套图,如下所示:
<navigation app:startDestination="@id/homeFragment" ...>
<fragment android:id="@+id/homeFragment" .../>
<fragment android:id="@+id/productListFragment" .../>
<fragment android:id="@+id/productFragment" .../>
<fragment android:id="@+id/bargainFragment" .../>
<navigation
android:id="@+id/checkout_graph"
app:startDestination="@id/cartFragment">
<fragment android:id="@+id/orderSummaryFragment".../>
<fragment android:id="@+id/addressFragment" .../>
<fragment android:id="@+id/paymentFragment" .../>
<fragment android:id="@+id/cartFragment" .../>
</navigation>
</navigation>
现在,在初始化视图模型时在片段中执行此操作
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)
如果你需要传递视图模型工厂(可能是为了注入视图模型)你可以这样做:
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph) { viewModelFactory }
确保它是 R.id.checkout_graph
而不是 R.navigation.checkout_graph
出于某种原因,创建导航图并使用 include
将其嵌套在主导航图中对我不起作用。可能是一个错误。
谢谢 OP 和@Archie 为我指明了正确的方向。
好像在最新的架构组件版本中已经解决了
ViewModelProvider 具有以下构造函数:
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* @param owner a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
* retain {@code ViewModels}
* @param factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
在 Fragment 的情况下,将使用范围化的 ViewModelStore。
androidx.fragment.app.Fragment#getViewModelStore
/**
* Returns the {@link ViewModelStore} associated with this Fragment
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*
* @return a {@code ViewModelStore}
* @throws IllegalStateException if called before the Fragment is attached i.e., before
* onAttach().
*/
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
androidx.fragment.app.FragmentManagerViewModel#getViewModelStore
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}