应该如何为具有多个字段的 activity 实现 ViewModel
How should be implemented the ViewModel for an activity with many fields
问题
有一个设置屏幕 (SettingsActivity
),其中包含大约 10 个文本字段和 3 个按钮。在 onClick
上打开 insert/edit 文本对话框的文本字段的内容保存在 SharedPreferences
中。这些按钮执行异步请求以检索内容并保存在其他地方。在请求期间,会显示一个对话框以通知进度。
初步解决方案
数据存储库
基本上是 SharedPreferences 的包装器,它有 10 个 getter 和 10 个 setter,每个字段一个。在 get[field_name]
上,DataRepository
从 SharedPreferences
获取值,在 set[field_name]
上,它提交给 SharedPreferences
.
视图模型
一个 ViewModel
有 10 个 MutableLiveData
个对象,每个字段一个。此 class 实现 LifecycleObserver
以了解 SettingsActivity
生命周期,因此它可以从 onCreate
的存储库加载字段并将字段保存到 [=24= 的存储库].
还有 3 种方法可以执行由提到的 3 个按钮触发的 3 个异步请求。每个方法接收一个 OnRequestProgressListener
实例,该实例被传递给 class,后者发出异步请求以通知视图有关进度。
查看
具有 10 个字段的 activity,观察 ViewModel
中的 10 MutableLiveData
。在每个字段的 onClick
上,会打开一个对话框以显示 edit/insert 文本。在对话框的onPositiveButton
上,调用了对应字段的观察者
activity 实现 OnRequestProgressListener
以根据异步请求进度显示和隐藏对话框。
初解题
上面描述的设计似乎不正确。我可以指出一些:
- 10
MutableLiveData
在 ViewModel
;
DataRepository
; 中有 10 个 getter 和 10 个 setter
SharedPreferences
. 的存储库
ViewModel
接收侦听器以传递给执行异步请求的 classes,使用这些侦听器通知视图。中间都是ViewModel
。
正确解
这是正确的解决方案吗?如果不是,我相信不是,应该如何设计正确的解决方案?
- 10 MutableLiveData in the ViewModel;
完全没问题,如果你有 10 个独立的数据,你可以为每个数据创建一个 LiveData。
- A repository for SharedPreferences.
存储库应该是对数据层的抽象,使您可以轻松切换实现。因此,在理论上拥有共享首选项的存储库是可以的。
但在你的情况下,如果存储库唯一做的就是将调用转发给 SharedPreferences
,因为将存储解决方案从共享首选项切换到其他东西的可能性非常低,我会摆脱存储库并直接使用 SharedPreferences 来简化代码。
- 10 getters and 10 setters in the DataRepository;
同样如此,如果您在 class 中存储了 10 条数据并希望从外部访问它,您应该使用 属性 模式,这会导致 getter 和 Java 中的 setter。尽管在 Kotlin 中您不需要显式地编写 getters 和 setters。
此外,如果您决定删除 DataRepository,则不需要该代码。
- The ViewModel receives listeners to pass to the classes that do the async requests which use these listeners to notify the view. All with the ViewModel in the middle.
这听起来有点不对,如果您在 activity 中创建一个侦听器,您很可能会不小心使用引用了 activity 的匿名 class,将其传递给 ViewModel
并导致内存泄漏。
您不应将 activity 引用传递给 ViewModel
。
正确的通信方式是通过 LiveData。您需要创建一个 post 进度的 LiveData,在 ViewModel 中使用它,为其提供进度,并且您的 activity 需要订阅它以获取进度信息。
使用这种方法,您可以避免内存泄漏。
您可以使用一个 Settings
数据 class,其中包含封装数据的字符串、整数和吸气剂。
一个 MutableLiveData<Settings>
足以跟踪更改并将更改传递给 UI。如果您使用 TextViews 和 EditTexts,则通过数据绑定,您也不需要样板代码。要使用 LiveData
进行数据绑定,您需要使用 ViewModel
中定义的方法调用 LiveData
的 setValue()
。我做了 an example 数据绑定的工作原理和不适用于 LiveData
。
您可以查看 my answer,了解如何使用 LiveData
和可变对象并更新 UI 而无需任何锅炉代码和不必要的 MutableLiveDatas 或将 MutableLiveData<Object>
转换为 MutableLiveData<String>
或 MutableLiveData<Integer>
.
如果你能分享你的布局文件,我可以post更具体的答案。
问题
有一个设置屏幕 (SettingsActivity
),其中包含大约 10 个文本字段和 3 个按钮。在 onClick
上打开 insert/edit 文本对话框的文本字段的内容保存在 SharedPreferences
中。这些按钮执行异步请求以检索内容并保存在其他地方。在请求期间,会显示一个对话框以通知进度。
初步解决方案
数据存储库
基本上是 SharedPreferences 的包装器,它有 10 个 getter 和 10 个 setter,每个字段一个。在 get[field_name]
上,DataRepository
从 SharedPreferences
获取值,在 set[field_name]
上,它提交给 SharedPreferences
.
视图模型
一个 ViewModel
有 10 个 MutableLiveData
个对象,每个字段一个。此 class 实现 LifecycleObserver
以了解 SettingsActivity
生命周期,因此它可以从 onCreate
的存储库加载字段并将字段保存到 [=24= 的存储库].
还有 3 种方法可以执行由提到的 3 个按钮触发的 3 个异步请求。每个方法接收一个 OnRequestProgressListener
实例,该实例被传递给 class,后者发出异步请求以通知视图有关进度。
查看
具有 10 个字段的 activity,观察 ViewModel
中的 10 MutableLiveData
。在每个字段的 onClick
上,会打开一个对话框以显示 edit/insert 文本。在对话框的onPositiveButton
上,调用了对应字段的观察者
activity 实现 OnRequestProgressListener
以根据异步请求进度显示和隐藏对话框。
初解题
上面描述的设计似乎不正确。我可以指出一些:
- 10
MutableLiveData
在ViewModel
; DataRepository
; 中有 10 个 getter 和 10 个 setter
SharedPreferences
. 的存储库
ViewModel
接收侦听器以传递给执行异步请求的 classes,使用这些侦听器通知视图。中间都是ViewModel
。
正确解
这是正确的解决方案吗?如果不是,我相信不是,应该如何设计正确的解决方案?
- 10 MutableLiveData in the ViewModel;
完全没问题,如果你有 10 个独立的数据,你可以为每个数据创建一个 LiveData。
- A repository for SharedPreferences.
存储库应该是对数据层的抽象,使您可以轻松切换实现。因此,在理论上拥有共享首选项的存储库是可以的。
但在你的情况下,如果存储库唯一做的就是将调用转发给 SharedPreferences
,因为将存储解决方案从共享首选项切换到其他东西的可能性非常低,我会摆脱存储库并直接使用 SharedPreferences 来简化代码。
- 10 getters and 10 setters in the DataRepository;
同样如此,如果您在 class 中存储了 10 条数据并希望从外部访问它,您应该使用 属性 模式,这会导致 getter 和 Java 中的 setter。尽管在 Kotlin 中您不需要显式地编写 getters 和 setters。 此外,如果您决定删除 DataRepository,则不需要该代码。
- The ViewModel receives listeners to pass to the classes that do the async requests which use these listeners to notify the view. All with the ViewModel in the middle.
这听起来有点不对,如果您在 activity 中创建一个侦听器,您很可能会不小心使用引用了 activity 的匿名 class,将其传递给 ViewModel
并导致内存泄漏。
您不应将 activity 引用传递给 ViewModel
。
正确的通信方式是通过 LiveData。您需要创建一个 post 进度的 LiveData,在 ViewModel 中使用它,为其提供进度,并且您的 activity 需要订阅它以获取进度信息。
使用这种方法,您可以避免内存泄漏。
您可以使用一个 Settings
数据 class,其中包含封装数据的字符串、整数和吸气剂。
一个 MutableLiveData<Settings>
足以跟踪更改并将更改传递给 UI。如果您使用 TextViews 和 EditTexts,则通过数据绑定,您也不需要样板代码。要使用 LiveData
进行数据绑定,您需要使用 ViewModel
中定义的方法调用 LiveData
的 setValue()
。我做了 an example 数据绑定的工作原理和不适用于 LiveData
。
您可以查看 my answer,了解如何使用 LiveData
和可变对象并更新 UI 而无需任何锅炉代码和不必要的 MutableLiveDatas 或将 MutableLiveData<Object>
转换为 MutableLiveData<String>
或 MutableLiveData<Integer>
.
如果你能分享你的布局文件,我可以post更具体的答案。