如何使用 Android ViewModel 实现 Arrow Kt?
How implement Arrow Kt with Android ViewModel?
在 Android 中,网络操作通常在 ViewModel
内完成。这确保即使 Activity
或 Fragment
被重新创建(例如当设备旋转时),网络调用也会继续进行并且不会被取消。
现在将ViewModel
的网络请求结果提交给视图(Activity
/Fragment
)。您有一个反应性组件,例如 LiveData
或 Observable
来设置其值。喜欢:
val resultLiveData = MutableLiveData<Result>()
fun doNetworkRequest() {
repository.requestSomeResult() // Assume this returns Arrow's IO
.unsafeRunAsync { eitherResult ->
eitherResult.fold({ error ->
// Handle Error
}, { result ->
resultLiveData.value = result
})
}
}
我想知道是否有办法让 val resultLiveData = MutableLiveData<Result>()
不依赖于特定的实现,例如 LiveData
返回 higher kind
、Kind<F, Result>
相反。
有什么办法吗:
val result = Kind<F, Result>()
fun doNetworkRequest() {
repository.requestSomeResult() // Assume this returns Arrow's IO
.unsafeRunAsync { eitherResult ->
eitherResult.fold({ error ->
// Handle Error
}, { result ->
resultLiveData.sendValue(result) // Or however it should be done
})
}
}
所以我以后可以用我想要的实现来定义 Kind<F, Result>
?
感谢您提出这个问题!这是我最近一直在做的事情。有几点要提一下:
在接下来的几天里,我们将发布 Arrow 的 KotlinX 集成模块。我将允许您在 CoroutineScope 中确定 IO 任务的范围。即:
yourIOTask().unsafeRunScoped(scope) { cb -> }
如果取消提供的范围,这将确保您的 IO 任务被取消。这意味着您可以在本示例中使用视图模型范围来确定 "view model" 操作的范围,例如 doNetworkRequest
,并且您将确保这些操作能够在配置更改后继续存在,并在视图模型发布时被取消。
也就是说,如果我们观察 Android ViewModel 目前的工作方式,您仍然需要 "middle layer cache" 来交付结果,正如您提到的那样,以确保这些结果始终已交付,一旦视图开始观察,我就会获得最新鲜的数据。通过使用此机制以及上一段中提到的范围,您可以确保您的长 运行 任务始终交付结果,无论它们是在配置更改之前、期间还是之后完成。
从这个意义上说,如果您想继续使用 Android ViewModel,您可以使用箭头对某些内容进行编码,例如:
interface ViewStateCache<ViewState> {
val cacheScope: CoroutineScope
fun observeViewState(observer: LifecycleOwner, renderingScope: CoroutineScope, render: (ViewState) -> IO<Unit>): IO<Unit>
fun updateViewState(transform: (ViewState) -> ViewState): IO<ViewState>
}
我们可以使用此契约来确保以纯粹的方式使用 ViewModel。所有 ViewModel 都可以实现此契约,例如:
class ViewModelViewStateCache<ViewState>(initialState: ViewState) : ViewModel(), ViewStateCache<ViewState> {
override val cacheScope: CoroutineScope = viewModelScope
private val _viewState = MutableLiveData<ViewState>(initialState)
private val viewState: LiveData<ViewState> = _viewState
override fun updateViewState(transform: (ViewState) -> ViewState) =
IO {
val transformedState = transform(viewState.value!!)
_viewState.postValue(transformedState)
transformedState
}
override fun observeViewState(observer: LifecycleOwner, renderingScope: CoroutineScope, render: (ViewState) -> IO<Unit>) =
IO {
viewState.observe(observer, Observer<ViewState> { viewState ->
viewState?.let { render(it).unsafeRunScoped(renderingScope) {} }
})
}
}
这样,您就可以有效地拥有一个使用 Android ViewModel 实现的视图状态缓存。这是一个实现细节,所以你可以注入它。您的程序将针对该界面运行。
此处,ViewModel 仅用作将结果传递给 的缓存,并且通过包装其观察和更新视图状态的操作使其变得纯粹 IO
.
有了这样的东西,您可以拥有纯函数来编码您的表示和线程协调逻辑,并将结果传送到提到的缓存,例如:
fun doNetworkRequest(): IO<Unit> = IO.fx {
!viewStateCache.updateViewState { Loading }
!repository.requestSomeResult().redeemWith(
ft = {
viewStateCache.updateViewState { ErrorViewState(ServerError) }
},
fe = { error ->
viewStateCache.updateViewState { ErrorViewState(error) }
},
fb = { data ->
viewStateCache.updateViewState { SuccessfulViewState(data) }
}
)
}
这些函数不需要存在于视图模型中,而是使用缓存作为委托来传递结果,因此它可以作为实现细节注入 .
您还需要在创建视图后立即开始观察视图状态缓存,因此这与您已经对视图模型所做的类似。请注意,我们有意将范围公开为缓存合同的一部分,以便您可以从外部访问它。
这是一个示例,说明如何包装当前的 ViewModel api 以继续使用它们并确保它们的配置更改支持,主要考虑逐步迁移到 Arrow。
这种方法更像是一种方便的方法,可能需要一些改进,但应该可行。我们目前正在探索 Android 常见问题,例如配置更改,以便通过扩展或类似方式为集成库中的用户提供无缝体验。
希望这足够有用,如果没有,请告诉我
在 Android 中,网络操作通常在 ViewModel
内完成。这确保即使 Activity
或 Fragment
被重新创建(例如当设备旋转时),网络调用也会继续进行并且不会被取消。
现在将ViewModel
的网络请求结果提交给视图(Activity
/Fragment
)。您有一个反应性组件,例如 LiveData
或 Observable
来设置其值。喜欢:
val resultLiveData = MutableLiveData<Result>()
fun doNetworkRequest() {
repository.requestSomeResult() // Assume this returns Arrow's IO
.unsafeRunAsync { eitherResult ->
eitherResult.fold({ error ->
// Handle Error
}, { result ->
resultLiveData.value = result
})
}
}
我想知道是否有办法让 val resultLiveData = MutableLiveData<Result>()
不依赖于特定的实现,例如 LiveData
返回 higher kind
、Kind<F, Result>
相反。
有什么办法吗:
val result = Kind<F, Result>()
fun doNetworkRequest() {
repository.requestSomeResult() // Assume this returns Arrow's IO
.unsafeRunAsync { eitherResult ->
eitherResult.fold({ error ->
// Handle Error
}, { result ->
resultLiveData.sendValue(result) // Or however it should be done
})
}
}
所以我以后可以用我想要的实现来定义 Kind<F, Result>
?
感谢您提出这个问题!这是我最近一直在做的事情。有几点要提一下:
在接下来的几天里,我们将发布 Arrow 的 KotlinX 集成模块。我将允许您在 CoroutineScope 中确定 IO 任务的范围。即:
yourIOTask().unsafeRunScoped(scope) { cb -> }
如果取消提供的范围,这将确保您的 IO 任务被取消。这意味着您可以在本示例中使用视图模型范围来确定 "view model" 操作的范围,例如 doNetworkRequest
,并且您将确保这些操作能够在配置更改后继续存在,并在视图模型发布时被取消。
也就是说,如果我们观察 Android ViewModel 目前的工作方式,您仍然需要 "middle layer cache" 来交付结果,正如您提到的那样,以确保这些结果始终已交付,一旦视图开始观察,我就会获得最新鲜的数据。通过使用此机制以及上一段中提到的范围,您可以确保您的长 运行 任务始终交付结果,无论它们是在配置更改之前、期间还是之后完成。
从这个意义上说,如果您想继续使用 Android ViewModel,您可以使用箭头对某些内容进行编码,例如:
interface ViewStateCache<ViewState> {
val cacheScope: CoroutineScope
fun observeViewState(observer: LifecycleOwner, renderingScope: CoroutineScope, render: (ViewState) -> IO<Unit>): IO<Unit>
fun updateViewState(transform: (ViewState) -> ViewState): IO<ViewState>
}
我们可以使用此契约来确保以纯粹的方式使用 ViewModel。所有 ViewModel 都可以实现此契约,例如:
class ViewModelViewStateCache<ViewState>(initialState: ViewState) : ViewModel(), ViewStateCache<ViewState> {
override val cacheScope: CoroutineScope = viewModelScope
private val _viewState = MutableLiveData<ViewState>(initialState)
private val viewState: LiveData<ViewState> = _viewState
override fun updateViewState(transform: (ViewState) -> ViewState) =
IO {
val transformedState = transform(viewState.value!!)
_viewState.postValue(transformedState)
transformedState
}
override fun observeViewState(observer: LifecycleOwner, renderingScope: CoroutineScope, render: (ViewState) -> IO<Unit>) =
IO {
viewState.observe(observer, Observer<ViewState> { viewState ->
viewState?.let { render(it).unsafeRunScoped(renderingScope) {} }
})
}
}
这样,您就可以有效地拥有一个使用 Android ViewModel 实现的视图状态缓存。这是一个实现细节,所以你可以注入它。您的程序将针对该界面运行。
此处,ViewModel 仅用作将结果传递给 的缓存,并且通过包装其观察和更新视图状态的操作使其变得纯粹 IO
.
有了这样的东西,您可以拥有纯函数来编码您的表示和线程协调逻辑,并将结果传送到提到的缓存,例如:
fun doNetworkRequest(): IO<Unit> = IO.fx {
!viewStateCache.updateViewState { Loading }
!repository.requestSomeResult().redeemWith(
ft = {
viewStateCache.updateViewState { ErrorViewState(ServerError) }
},
fe = { error ->
viewStateCache.updateViewState { ErrorViewState(error) }
},
fb = { data ->
viewStateCache.updateViewState { SuccessfulViewState(data) }
}
)
}
这些函数不需要存在于视图模型中,而是使用缓存作为委托来传递结果,因此它可以作为实现细节注入 .
您还需要在创建视图后立即开始观察视图状态缓存,因此这与您已经对视图模型所做的类似。请注意,我们有意将范围公开为缓存合同的一部分,以便您可以从外部访问它。
这是一个示例,说明如何包装当前的 ViewModel api 以继续使用它们并确保它们的配置更改支持,主要考虑逐步迁移到 Arrow。
这种方法更像是一种方便的方法,可能需要一些改进,但应该可行。我们目前正在探索 Android 常见问题,例如配置更改,以便通过扩展或类似方式为集成库中的用户提供无缝体验。
希望这足够有用,如果没有,请告诉我