如何使状态流< model> Androd中的多态性?
我正在舞台上,想将我的stateflow<()
曝光到公共baseviewModel()
类中以实现对其进行公共操作,而无需在其他实现中重复它们ViewModels
。我最终提出了一个想法,但是在建造POC时面临着几个局限性。我的想法及其局限性在下面,您的原点问题解决方案受到欢迎。
我将模型分为接口
和一个具体实现。
sealed interface Model {
var isLoading: Boolean
var errors: List<String>
/**
* @param obj Should has the same concrete type as concrete type of object which copy() it invokes
* */
fun copy(obj: Model): Model
}
data class DeputiesModel(
override var isLoading: Boolean = false,
override var errors: List<String> = emptyList<String>(),
var deputies: List<Deputy> = emptyList()
) : Model {
override fun copy(obj: Model): DeputiesModel {
if (obj !is DeputiesModel)
throw IllegalArgumentException("Passed object implements ${Model::javaClass.name}" +
" interface, but should be concrete ${DeputiesModel::javaClass::name} implementation.")
return this.copy(deputies = obj.deputies, isLoading = obj.isLoading, errors = obj.errors)
}
}
我需要接口中的copy()
方法data class copy()
已关闭以进行超载和覆盖。
我的stateflow
实现已在baseViewModel()
中移动,
abstract class BaseViewModel<T : Model> : ViewModel() {
protected lateinit var state: MutableStateFlow<T>
lateinit var uiState: StateFlow<T>
}
我在此处添加了generics,以避免施放model
键入其一个具体实现,例如> deputiesModel ,在继承baseViewModel
的类中,否则此额外代码将使整体意图揭示常见方法冗余。
这是第一个常见方法:
fun removeShownError() {
state.update { state ->
state.errors = state.errors.filter { str -> !str.equals(state.errors.first()) }
state.copy(state) as T
}
}
该设计的限制是state.copy.copy(state)
不触发uistate.collectlatest {}
nourm parameperized <代码> state.copy(isloading = false)做到了。我还没有找到它的根本原因。
val viewModel: DeputiesViewModel by viewModels { viewModelFactory }
lifecycleScope.launch {
viewModel.uiState.collectLatest { it ->
if (it.errors.isNotEmpty()) {
showError(
view = requireActivity().findViewById(R.id.nav_view),
text = it.errors.first(),
onDismiss = { viewModel.removeShownError() }
)
}
(binding.list.adapter as DeputiesAdapter).update(it.deputies)
}
}
就这样。您的想法得到赞赏。
I am at stage when I would like to expose my StateFlow<Model>()
into the common BaseViewModel()
class to implement common operations on it without repeating them in others implementations of ViewModels
. I ended up with one idea, but faced with several limitations when has built PoC. My idea and its limitations are below, your solutions for origin question are welcomed.
I split my model into interface
and one concrete implementation.
sealed interface Model {
var isLoading: Boolean
var errors: List<String>
/**
* @param obj Should has the same concrete type as concrete type of object which copy() it invokes
* */
fun copy(obj: Model): Model
}
data class DeputiesModel(
override var isLoading: Boolean = false,
override var errors: List<String> = emptyList<String>(),
var deputies: List<Deputy> = emptyList()
) : Model {
override fun copy(obj: Model): DeputiesModel {
if (obj !is DeputiesModel)
throw IllegalArgumentException("Passed object implements ${Model::javaClass.name}" +
" interface, but should be concrete ${DeputiesModel::javaClass::name} implementation.")
return this.copy(deputies = obj.deputies, isLoading = obj.isLoading, errors = obj.errors)
}
}
I need a copy()
method in interface to data class copy()
is closed for overloading and overriding.
My StateFlow
implementation has been moved in BaseViewModel()
abstract class BaseViewModel<T : Model> : ViewModel() {
protected lateinit var state: MutableStateFlow<T>
lateinit var uiState: StateFlow<T>
}
I added generics here to avoid casting Model
type to one of its concrete implementation, e.g. DeputiesModel
, in classes which inherits BaseViewModel
, otherwise this extra code would made a whole intent to expose common methods redundant.
And here is a first common method:
fun removeShownError() {
state.update { state ->
state.errors = state.errors.filter { str -> !str.equals(state.errors.first()) }
state.copy(state) as T
}
}
The limitation of this design&implementation is state.copy(state)
doesn't trigger uiState.collectLatest{}
call when origin parameterized state.copy(isLoading = false)
does it. I haven't find yet the root cause of it.
val viewModel: DeputiesViewModel by viewModels { viewModelFactory }
lifecycleScope.launch {
viewModel.uiState.collectLatest { it ->
if (it.errors.isNotEmpty()) {
showError(
view = requireActivity().findViewById(R.id.nav_view),
text = it.errors.first(),
onDismiss = { viewModel.removeShownError() }
)
}
(binding.list.adapter as DeputiesAdapter).update(it.deputies)
}
}
That's all. Your ideas are appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
以下是您的常见函数示例
removeshownError()
可以通过扩展功能求解,因此您不需要baseviewModel。没有突变性的模型类:
共享功能的扩展功能,在最高级别定义的某个地方定义:
现在不需要baseviewModel。您只需添加任何具有相关流的视图模型:
顺便说一句,在ViewModel中使用
LateInit
并没有多大意义。这是诸如活动之类的课程所需的黑客攻击您自己的任何属性都将用于任何事物。在ViewModel中,您可能需要使用的所有内容都可以通过构造函数进行类初始化,因此没有理由将属性的初始化推迟。即使您确实必须使用
lateinit
为此,也无需将其用于合作伙伴的不可用的公共财产,因为该属性只能使用自定义Getter,因此它没有备份变量首先初始化:Here's how your example of the common function
removeShownError()
could be solved with an extension function so you don't need a BaseViewModel.Model classes without mutability:
Extension functions for shared functionality, defined at the top level somewhere:
Now there's no need for a BaseViewModel. You can just add to any ViewModel that has a relevant flow:
By the way, it doesn't make much sense to use
lateinit
in a ViewModel. That's a hack needed for classes like Activity where most of what you need to do to initialize variables requires Context, but the context isn't ready at class instantiation time andcreate()
is the earliest point at which any of your own properties will be used for anything. In a ViewModel, everything you could possibly need to use is already available at class initialization via the constructor, so there's no reason to postpone initialization of a property.Even if you did have to use
lateinit
for it, it wouldn't be necessary to use it for it's partner non-mutable public property, because that property could just use a custom getter, so it has no backing variable to initialize in the first place: