带有保存状态的视图模型、Jetpack导航、数据绑定和Coroutines
自推出以来,ViewModel已经成为最“核心”的Android Jetpack库之一。根据我们2019年的开发者基准数据,超过40%的安卓开发者已经将ViewModels添加到他们的应用程序中。如果你不熟悉ViewModels,可能不清楚为什么会出现这种情况:ViewModels通过将数据与你的UI分离来促进更好的架构,使其易于处理UI生命周期,同时也提高了测试能力。关于完整的解释,请查看ViewModels。一个简单的例子和官方文档。
由于ViewModels是如此的基础,在过去的几年中,有很多工作都是为了让它们更容易使用,更容易与其他库集成。在这篇文章中,我将介绍四种集成方式。
- ViewModels中的保存状态 —ViewModel的数据在背景进程重启后仍能存活。
- 带有ViewModel的NavGraph — ViewModels和导航库的整合
- 在数据绑定中使用ViewModels — 使用ViewModels和LiveData轻松实现数据绑定
- viewModelScope — Kotlin Coroutines和ViewModels的整合
ViewModels中的保存状态:ViewModel数据在背景进程重启后仍能存活。
在lifecycle-viewmodel-savedstate:1.0.0-alpha01中添加,Java和Kotlin都是如此。
onSaveInstanceState
的挑战
当ViewModels最初推出时,有一个涉及onSaveInstanceState
的混乱问题。活动和片段可以通过三种方式被销毁。
1.你的意思是要永久地导航离开:用户导航离开或明确地关闭活动—比如按后退按钮或触发一些调用finish()
的代码。该活动已永久消失。
2.配置发生了变化:用户旋转了设备或做了一些其他的配置变化。该活动需要立即重建。
3. 应用程序被置于后台并且其进程被终止:当设备内存不足并需要快速释放一些内存时会发生这种情况。当用户导航回您的应用程序,活动将需要重建。
在情况2和3中,你想重建活动。ViewModels总是帮助你处理情况2,因为ViewModel在配置改变时不会被销毁;但在情况3中,ViewModel也会被销毁,所以你实际上需要使用活动中的onSaveInstanceState
回调来保存和恢复数据。我在ViewModels中更详细地讨论了这个棘手的区别。Persistence, onSaveInstanceState(), Restoring UI State and Loaders中详细介绍了这种棘手的区别。
保存的状态模块
ViewModel保存的状态模块帮助你处理第三种情况:进程死亡。ViewModel不再需要向活动发送和接收状态。相反,你现在可以在ViewModel中处理保存和恢复数据。现在ViewModel可以真正处理和持有它自己的所有数据。
这是用SavedStateHandle完成的,它与Bundle非常相似;它’是一个数据的键值映射。这个SavedStateHandle “bundle”在ViewModel中,它可以在后台进程死亡后继续存在。任何你以前必须保存在onSaveInstanceState
中的数据现在都可以保存在SavedStateHandle中。例如,用户的id是你可能保存在SavedStateHandle中的东西。
设置 "保存状态 "模块
让我们看看如何使用这个新模块。请注意,下面显示的代码与这个代码非常相似,来自Lifecycles Codelab的第6步。那段代码是用Java编写的,下面的代码是用Kotlin编写的。
第1步:添加依赖关系
SavedStateHandle 目前处于 alpha 状态(这意味着 API 可能会发生变化,我们正在寻求反馈),并且它是一个独立的库。需要添加的依赖性是。
implementation ‘androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha01’
注意,如果您想了解库中发生的变化,请查看生命周期发布说明页面。
第2步:更新对ViewModelProvider的调用
接下来,你要创建一个具有SavedStateHandle的ViewModel类型。在你的活动或片段onCreate
中,将你对ViewModelProvider的调用更新为。
// This ktx requires at least androidx.fragment:fragment-ktx:1.1.0 or
// androidx.activity:activity-ktx:1.0.0
val viewModel by viewModels { SavedStateVMFactory(this) }
// Or the non-ktx way...
val viewModel = ViewModelProvider(this, SavedStateVMFactory(this))
.get(MyViewModel::class.java)
创建ViewModel的类是ViewModel工厂,并且有一个ViewModel工厂可以制造具有SavedStateHandle的ViewModel,该工厂名为SavedStateVMFactory。现在创建的ViewModel将有一个SavedStateHandle,与传入的活动/片段相关。
注意。即将发布的Androidx activity和fragment库的alpha版本将在7月推出。在这些版本中(如此处所述),当你在活动或片段中制作一个ViewModel时,SavedStateVMFactory将成为默认的ViewModelProvider.Factory。这意味着,如果你’正在使用Androidx活动或片段的最新alpha版本,你将不需要添加lifecycle-viewmodel-savedstate依赖或明确使用SavedStateVMFactory。简而言之,当这种变化发生时,如果你’正在使用新的alpha版本,你可以跳过步骤1和2,直接进入下面的步骤3。
第3步:在ViewModel中使用SaveStateHandle。
一旦你完成了这些,你就可以在你的ViewModel中使用SavedStateHandle。下面是一个在SavedStateHandle中保留用户ID的例子。
class MyViewModel(state : SavedStateHandle) : ViewModel() {
// Keep the key as a constant
companion object {
private val USER_KEY = "userId"
}
private val savedStateHandle = state
fun saveCurrentUser(userId: String) {
// Sets a new value for the object associated to the key.
savedStateHandle.set(USER_KEY, userId)
}
fun getCurrentUser(): String {
// Gets the current value of the user id from the saved state handle
return savedStateHandle.get(USER_KEY)?: ""
}
}
- Construct:
MyViewModel
将SavedStateHandle作为构造函数的参数。 - 保存:
saveNewUser
方法展示了一个在SavedStateHandle中保存数据的例子。你保存了USER_KEY
的键值对,然后是当前的userId
。当数据在ViewModel中更新时,它应该被保存在SavedStateHandle中。 - Retrieve:
savedStateHandle.get(USER_KEY)
是一个获取保存在SaveStateHandle中的当前值的例子。
现在,如果活动由于旋转而被破坏,或者由于操作系统杀死你的进程以释放内存,你可以确保SavedStateHandle会有你的数据。
通常情况下,你会在你的ViewModel中使用LiveData。为此,你可以使用SavedStateHandle.getLiveData()
方法。这里’是一个用LiveData替换getCurrentUser
的例子,它可以进行观察。
// getLiveData gets MutableLiveData associated with a key.
// When the value associated with the key updates, the MutableLiveData does as well.
private val _userId : MutableLiveData<String> = savedStateHandle.getLiveData(USER_KEY)
// Only expose a immutable LiveData
val userId : LiveData<String> = _userId
要了解更多信息,请查看生命周期Codelab的第6步和官方文档。
ViewModel和Jetpack导航:带有ViewModel的NavGraph
在navigation 2.1.0-alpha02中添加,Java和Kotlin都是如此。
ViewModel共享的挑战
Jetpack Navigation开箱即用,适用于设计有相对较少的活动&mdash;甚至只有一个&mdash;包含多个片段的应用程序。我们选择这种架构的一些原因在Ian Lake’出色的演讲Single Activity:为什么、何时和如何。其中一个原因是,这种架构允许你通过创建一个活动共享的ViewModel在不同的目的地之间共享数据。你使用活动创建一个ViewModel,然后你可以从活动的任何片段中获得对该ViewModel的引用。
// Any fragment's onCreate or onActivityCreated
// This ktx requires at least androidx.fragment:fragment-ktx:1.1.0
val sharedViewModel: ActivityViewModel by activityViewModels()
现在想象一下,我们有一个单一的活动应用程序,我们有八个片段目的地。其中,有四个是购物结账流程。
带有购物结账流程中的一些屏幕的导航图。
对于结账流程中的这四个目的地来说,共享数据是很重要的,比如发货地址或用户是否使用了优惠券代码。我们将把这些信息放在ViewModel中,但ViewModel与什么相关?这些信息对应用程序的其他部分并不重要,但以前我们对共享ViewModel的唯一选择是将ViewModel与活动相关联。这意味着所有的八个目的地都可以访问这个ViewModel。
ViewModel NavGraph集成
Navigation 2.1.0引入了与导航图关联的ViewModels。在实践中,这意味着你可以采取一系列相关的目的地,如入职流程、登录流程或结账流程;将它们放入一个嵌套的导航图;并在这些屏幕之间启用共享数据。
要创建一个嵌套的导航图,你可以选择你的屏幕,点击右键,然后选择移动到嵌套图→ 新图:。
显示如何 "移动到嵌套图 "的屏幕截图
在XML视图中,注意嵌套的导航图的id,在这个例子中是checkout_graph
。
<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>
一旦你完成了这些,你就可以使用by navGraphViewModels
来获得ViewModel。
val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)
这在Java编程语言中也是可行的,使用。
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other fragment setup code
NavController navController = NavHostFragment.findNavController(this);
ViewModelProvider viewModelProvider = new ViewModelProvider(this,
navController.getViewModelStore(R.id.checkout_graph));
CheckoutViewModel viewModel = viewModelProvider.get(CheckoutViewModel.class);
// Use Checkout ViewModel
}
请注意,嵌套图与导航图的其他部分是封装在一起的。你可以导航到一个嵌套图(你会去到嵌套图的起始目的地),但你不能从图的外部直接导航到嵌套图中的一个特定目的地。因此,它们是为封装的屏幕集合而设计的,比如结账流程或登录流程。
ViewModel NavGraph集成是I/O 2019上宣布的新导航功能之一。更多内容请查看讲座Jetpack Navigation和文档。
ViewModel和数据绑定:在数据绑定中使用你的ViewModel和LiveData。
在Android Studio 3.1中添加了Java和Kotlin两种语言。
所有这些LiveData的繁文缛节
这个集成是一个老东西,但也是一个好东西。ViewModels通常包含LiveData,而LiveData是为了被观察。通常这意味着在片段中添加一个观察者。
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
myViewModel.name.observe(this, { newName ->
// Update the UI. In this case, a TextView.
nameTextView.text = newName
})
}
数据绑定库是关于观察你的数据和更新UI。通过同时使用ViewModel、LiveData和Data Binding,您可以删除之前的LiveData观察代码,并直接从布局XML中引用您的ViewModel和LiveData。
使用数据绑定、ViewModel和LiveData
假设在你的 XML 布局中,你想引用你的 ViewModel。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="viewmodel"
type="com.android.MyViewModel"/>
</data>
<... Rest of your layout ...>
</layout>
要使用LiveData与数据绑定,你只需要调用binding.setLifecycleOwner(this)
,然后将你的ViewModel传递给你的绑定,就像这样。
class MainActivity : AppCompatActivity() {
// This ktx requires at least androidx.activity:activity-ktx:1.0.0
private val myViewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Inflate view and create binding
val binding: MainActivityBinding =
DataBindingUtil.setContentView(this, R.layout.main_activity)
//Specify this activity as the lifecycleOwner for Data Binding
binding.lifecycleOwner = this
// Pass the ViewModel into the binding
binding.viewmodel = myViewModel
}
}
现在在你的布局中,你可以使用你的ViewModel。如下图所示,我将文本设置为viewmodel.name
。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="viewmodel"
type="com.android.MyViewModel"/>
</data>
<TextView
android:id="@+id/name"
android:text="@{viewmodel.name}"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
</layout>
请注意,viewmodel.name
可以是一个字符串或一个LiveData。如果它是一个LiveData,用户界面将在LiveData发生变化时更新。
ViewModel和Kotlin Coroutines : viewModelScope
在生命周期2.1.0中添加 仅限Kotlin。
安卓系统中的程序员
Kotlin Coroutines是处理异步代码的一种新方式。另一种处理异步代码的方式是使用回调。回调很好,但如果你正在编写复杂的异步代码,你可能最终会有很多层嵌套的回调;这使你的代码难以理解。Coroutines简化了所有这些,并且还提供了一种简单的方法来确保你不会阻塞主线程。如果你是coroutines的新手,有一篇名为Coroutines on Android的深度博文系列,以及codelab Using Kotlin Coroutines in your Android App。
一个简单的coroutine看起来就像一个做一些工作的代码块。
// Don't use GlobalScope - for example purposes only
GlobalScope.launch {
longRunningFunction()
anotherLongRunningFunction()
}
在这里,我只启动了一个 coroutine,但很容易启动数百个 coroutine,并有可能失去对它们的跟踪;如果你失去了对一个 coroutine 的跟踪,并且它正在运行一些你打算停止的工作,这就是所谓的 工作泄露。
为了避免工作泄露,你应该通过将它们添加到CoroutineScope来组织你的轮回,这是一个跟踪轮回的对象。CoroutineScope可以被取消;当你取消一个范围时,它们会取消所有相关的coroutines。上面我使用的是GlobalScope,顾名思义,它是一个全球可用的CoroutineScope。使用GlobalScope通常是不好的做法,原因与编写全局可访问的变量通常不好一样。因此,你需要创建一个作用域,或者获取对它的访问。在ViewModels中,如果你使用viewModelScope,这很容易。
查看模型的范围
通常情况下,如果你的ViewModel被销毁了,那么与ViewModel相关的一堆 "工作 "也应该被停止。
例如,假设你准备在屏幕上显示一个位图。这是一个你应该在不阻塞主线程的情况下和工作的例子,如果你永久地远离或关闭屏幕,这些工作应该被停止。对于这样的工作,你应该使用viewModelScope。
viewModelScope是ViewModel类上的一个Kotlin扩展属性。它是一个CoroutineScope,一旦ViewModel被销毁(当onCleared()
被调用时)就会被取消。因此,当你使用ViewModel时,你可以使用这个范围来启动所有的coroutine。
这里有一个小的例子。
class MyViewModel() : ViewModel() {
fun initialize() {
viewModelScope.launch {
processBitmap()
}
}
suspend fun processBitmap() = withContext(Dispatchers.Default) {
// Do your long running work here
}
}
如果你正在使用Kotlin Coroutines和ViewModels,优秀的博文Easy Coroutines in Android: viewModelScope会有更多细节。更多关于Coroutines和架构组件的信息,请查看文档和讲座Understand Kotlin Coroutines on Android。
结语
综上所述,我们的目标是:"我们的目标"。
- ViewModels用SavedStateHandle模块来处理
onSaveInstanceState
的情况。 - 你可以将一个ViewModel的范围扩大到Jetpack Navigation NavGraph,以便在片段之间进行更精确和封装的数据共享。
- 如果你使用数据绑定库和ViewModels,你可以将你的ViewModel传递给你的绑定。如果你也使用LiveData,请使用
binding.setLifecycleOwner(lifecycleOwner)
。 - ...如果你使用Kotlin Coroutines与ViewModel,那么当ViewModel被销毁时,使用viewModelScope来自动取消你的coroutines。
这些集成中有许多是来自社区的直接反馈和请求。如果你正在寻找ViewModel的某个功能或集成,你可以关注功能请求列表,并考虑提出你自己的请求。
若要了解有关架构和 Android Jetpack 的最新进展,请关注Android 开发人员媒介博客,并密切关注AndroidX 发布说明。
对这些功能中的任何一个有疑问?请留下评论!谢谢你的阅读!
特别感谢Ian Lake、Yigit Boyar、Jose Alcérreca、Sean McQuillan、Jisha Abubaker和Alex Michael Cook的修改和贡献。
问题是关于实现双向绑定(从UI中更新LiveData的值)。
这很有说服力,而且真的很有效果。
<android.support.design.widget.TextInputEditText android:text=”@={viewModel.commentText}”/>
但我还想存储LiveData的状态!这有什么办法?有什么好的方法吗?
快速的问题;我的大部分LiveData属性都是暴露ViewModel状态的密封类,密封类不能实现Parcelable,那么有没有办法将密封类存储在SavedStateHandle上呢?
密封的类可以实现Parcelable。
只是一个关于SavedStateVMFactory
的实际使用的问题,如果我有另一个工厂与所需的ViewModel
的依赖关系,我如何在ViewModel
的实例化中组合/使用它?比如说。
someViewModel = ViewModelProviders.of(this, someViewModelFactory)
.get(SomeViewModel::class.java)
那么,如果我放弃了我的自定义ViewModelFactory
,我应该如何将依赖关系传递给ViewModel
呢?
如何在Fragments之间使用SavedState共享一个ViewModel?
当在一个流程中的多个屏幕之间共享ViewModel时,最佳做法是什么?除了必须在屏幕之间共享的数据的共享 ViewModel 之外,每个屏幕是否应该有一个单独的 ViewModel,用于处理该屏幕特有的状态?
谢谢你!
有一个问题,在存储和性能方面,savedStateHandler是否与操作系统在onSaveInstanceState()中的处理方式相同,而是被转移到ViewModel上?
SavedStateHandle,我们是否可以用它来进行片段通信,我可以设置/获取实时数据对象。使用它们会不会有什么隐患,我相信SaveStateHandle不是用来保存更大的对象的?如果我使用这个片段通信,你怎么说?
只有在实际需要时才序列化状态不是更好吗?这看起来像什么呢(也许是将片段的onSaveInstanceState
转发到视图模型)?
这是一篇了解ViewModel组件基本原理的好文章我使用了最新的Lifecycle 2.2.0版本,以便为ViewModel组件添加自定义参数和Saved State。
很好的文章。请将工厂更新为AbstractSavedStateViewModelFactory(我想这只是新名字)。
似乎SavedStateVMFactory被替换成了SavedStateViewModelFactory,这里应该有更多的变化。你能给我指点一下这方面的最新文章/教程/例子吗?
是否有一个代码样本/github repo来说明SavedStateHandle
的用法?
我们是否应该使用依赖性注入框架(即Koin)来注入ViewModels?
是否有一个谷歌样本来说明 "SavedStateHandle "的用法?
你能解释一下`by viewModels'的代码吗?我似乎不能让它编译,IDE抱怨类型推理的问题。
你需要为片段加入KTX插件。
大家好......很确定这篇文章已经被作者抛弃了。
好的
我不明白你在这一节中写了些什么。
"ViewModels中的保存状态:ViewModel数据在背景进程重启后仍然存在"。
不使用保存的状态模块,活动(或片段)使用onSaveInstanceState(Bundle)来保存状态。onCreate(Bundle savedInstanceState)在Bundle不是null时恢复状态,因为null表示之前没有保存任何东西。
如何使用 "保存状态模块 "来实现?我看到Saved State Module允许使用一个键来保存和恢复数值,但这还不足以在进程死亡时保存和恢复数据。我还需要知道什么时候调用这些函数。是否有一个我应该覆盖的回调?
我是否应该在每次改变数值时保存我的状态?这似乎很浪费。每次我需要一个值的时候,我都要恢复吗?这似乎让存储库变得多余了。
你能否提供顺序,说明在流程死亡和配置变更中数据的流向?是否有一个使用存储库的例子?
为什么文档中说"......有一个特殊的方法:getLiveData(String key),它返回包裹在LiveData可观察到的值"。你能在这里定义一下 "特殊 "的含义吗?我什么时候使用get(key)与getLiveData(key)?
谢谢你。
它’都存储在一个Map<String, Object>
中,并通过SavedStateRegistryOwner
(Fragment
/Activity
)所拥有的SavedStateRegistry
自动持久化到onSaveInstanceState
中。
不错的文章。写得非常好。
你好,我真的很难找到能清楚解释基本事物的好文档。我的核心问题是,当我试图将 "LifeCycles CodeLab "中的例子应用于我的Kotlin应用程序时,我遇到了我不理解的崩溃。
具体来说,我想在NavGraph集成中用Kotlin复制Timer的例子,但我发现我不太明白。
例如,当我从Fragment中调用ViewModelProviders方法时,我的应用程序会崩溃,除非我使用带有throw和Elvis操作符的 "run",为什么呢,有没有更好的方法?
接下来我不明白的是,为什么在Java中需要使用onChange方法,而在Kotlin中不仅不需要,而且似乎也不起作用(我擦掉了它,使里面的代码起作用);为什么,难道我做错了吗?
另外,我使用Init方法来替代ViewModel中来自Java的构造函数,而且每次我在片段之间改变时都会调用它,这正常吗? 最后,我没有看到任何关于在ViewModel上使用工厂的必要性的解释。我的意思是,当你需要使用一个非空的构造函数时,看起来你需要它们,但没有解释如何正确地调用它们,如果它们需要一直被使用*我并不总是向我的构造函数传递参数*,还有谁能指出一个好的教程,告诉像我这样的新手何时以及为什么使用 "by lazy "和 "by instance"?
嗨,凯文。
这里有很多问题,我将回答其中的几个问题
For example, My app crashes when I call the ViewModelProviders method from a Fragment, unless I use “run” with a throw and Elvis operators, why?, and is there a better way?
一个埃尔维斯运算符加上使用run
,听起来像是你在试图处理无效性。在没有看到代码的情况下,我不确定在这种情况下什么是 null,所以很难提供建议。使用 Kotlin 中的 NavGraph 集成来获取 ViewModel 的那行代码不应要求进行 null 检查。
The next thing that I do not understand is why in Java I needed to use the onChange method, but in Kotlin it was not only not needed
这’是因为SAM 转换,这是 Kotlin 拥有的一项功能,它可以将“单一抽象方法”接口转换为lambda 表达式&mdash;简而言之,在 Kotlin 中,您可以使用 lambda 表达式而无需明确重写 onChanged
。
and also can somebody point to a good tutorial on when and why to use “by lazy” and “by instance” to people that are noobs like me?
上面的三个问题是很好的问题,但它们是一般的Kotlin问题,而不是专门针对ViewModels的。你可以通过搜索和查看各种堆栈溢出的答案来一点一点地学习东西;如果你想要更多的指导,我建议你查看一些Kotlin学习资源。如果你喜欢现场学习,我们也正在进行Kotlin/Everywhere活动,你附近可能会有一个。
在这之后,我想看看Room with a View codelab in Kotlin,它将给你介绍ViewModel、LiveData和Room与Kotlin,或者我想参加Developing Android apps with Kotlin课程(时间投入更大,但更深入地了解做事背后的“原因” )。该课程假定你知道Kotlin,但真正深入到ViewModels、Navigation、架构等。我还建议看一下Sunflower示例应用程序。
Also I used the Init method to substitute the constructor from Java in the ViewModel, and it seems to be called everytime I change between fragments, is this normal?
如果它被调用,那是因为ViewModel被重构了 - 如果它是一个全新的片段(而不是在配置更改后制作的片段),这一点是真的。导航组件在你导航时确实创建了新的片段,所以这是有道理的。除非你点击返回按钮,否则它不会把你 "带回 "一个片段。
I have not seen any explanation on the need to use factories on ViewModels. I mean, it looks like you need them when you need to use a non-empty constructor, but there is no explanation on how to correctly call them, if they need to be used all the time *I don’t pass arguments to my constructor always*
你是正确的&mdash;当你需要向ViewModel传递参数时,ViewModel工厂会被使用。当你需要向ViewModel中获取数据时,你有两个选择:要么使用工厂并通过构造函数传递数据,要么在ViewModel中创建一个setter方法并通过该方法设置数据。正如在另一条评论中提到的,在这个视频和代码差异的用Kotlin开发Android应用中,可以找到一个关于如何添加ViewModel工厂的更全面的例子。
嘿!是否有可能使用DI的Saved State功能,或者在这种情况下我们被ViewModelProvider束缚住了?
你可以使用DI的Saved State功能。一般来说,要向ViewModel添加构造参数,你要做一个ViewModelProvider.Factory
,然后在初始化ViewModel时传入工厂。因此,不使用Saved State模块的例子看起来像这样。
val viewModel by viewModels { MyViewModelFactory(repository) }
// Or
val viewModel = ViewModelProvider(this, MyViewModelFactory(repository)).get(MyViewModel::class.java)
关于如何添加ViewModel工厂的更全面的例子可以在这个视频和代码差异中找到,这些代码来自我帮助制作的用Kotlin开发Android应用程序课程。
对于Saved State模块,你不需要从一个普通的工厂扩展,而是从AbstractSavedStateVMFactory
扩展。
val viewmodel by viewModels { MySavedStateViewModelFactory(repository, this) }
// Or
val viewModel = ViewModelProvider(this, MySavedStateViewModelFactory(repository, this)).get(MyViewModel::class.java)
如果你’具体谈论的是使用Dagger、Koin或其他框架,则需要更多的代码来连接一切。我绝非Dagger专家,但询问同事是否还有其他事情要做,他们建议你可能需要使用模块中的SavedStateRegistryOwner
。
@Binds
internal abstract fun bindSavedStateRegistryOwner(myActivity: MyActivity): SavedStateRegistryOwner
对于Dagger + ViewModels,我建议查看GithubBrowser架构组件样本和2018年IO Sched样本。
非常感谢您提供的这个:viewModelScope.sponse
太多的例子还在使用GlobalScope!谢谢你 Lyla Fujiwara
感谢您的精彩文章。我想问一个问题。
目前,我的viewmodels采取Repository参数,提供本地和远程的数据。在这种情况下,当我们考虑SavedStateHandle和Repository参数时,viewmodels必须按照这种方法接受两个参数。这种方法听起来并不符合清洁代码的原则。你能给我一些建议来解决这个问题吗?
嘿,Seyfullah — 谢谢你的问题。所以我想注意的第一件事是,从活动1.1.0-alpha01和片段1.2.0-alpha01发布起,SavedStateVMFactory
是活动和片段的默认工厂。这意味着你将不需要手动传入SavedStateHandle
,它将直接为你提供。
至于自己解决这个问题,你能说明你’寻求什么便利?例如,如果问题是模板,我打赌你可以为你的片段和活动写一个扩展函数,当需要ViewModel时,总是使用SavedStateVMFactory
&mdash;但我不确定这是否解决了你的根本问题。
SavedStateHandle是否会取代存储库?