我正在使用rxKotlin,MVVM + Clean Architecture创建脱机的第一个应用程序作为我的辅助项目,昨天我决定通过使用变压器摆脱样板的SubscribeOn和ObservOn。我很快意识到,变压器的应用功能被忽略了。
这是我的基本可完成用例(交互器)的代码:
['Python', 'is', 'an', 'programming', 'language']
这是特定交互器的实现:
abstract class CompletableUseCase(private val transformer: CompletableTransformer) {
abstract fun createCompletable(data: Map<String, Any>? = null) : Completable
fun completable(data: Map<String, Any>? = null) : Completable {
return createCompletable(data).compose(transformer)
}
}
我的自定义转换器已传递给SaveRouteInteractor的构造函数:
class SaveRouteInteractor(
transformer: CompletableTransformer,
private val routeRepository: RouteRepository
) : CompletableUseCase(transformer) {
companion object {
private const val PARAM_ROUTE = "param_route"
}
fun saveRoute(route: Route) : Completable {
val data = HashMap<String, Route>()
data[PARAM_ROUTE] = route
return completable(data)
}
override fun createCompletable(data: Map<String, Any>?): Completable {
val routeEntity = data?.get(PARAM_ROUTE)
routeEntity?.let {
return routeRepository.saveRoute(routeEntity as Route)
} ?: return Completable.error(IllegalArgumentException("Argument @route must be provided."))
}
}
以及RouteRepository方法的实现:
class IOCompletableTransformer(private val mainThreadScheduler: Scheduler) : CompletableTransformer {
override fun apply(upstream: Completable): CompletableSource {
return upstream.subscribeOn(Schedulers.io()).observeOn(mainThreadScheduler)
}
}
我将Room用作本地源,因此在ViewModel中调用保存交互器后,我收到IlligalStateException通知我,不允许我访问主线程上的数据库。
也许我遗漏了一些东西,但是似乎转换功能被忽略了。我调试了此方法,并将其应用到上游的subscriptionOn和observeOn。
非常感谢您的帮助, 节奏!
答案 0 :(得分:1)
由于代码是部分代码,很难告诉您问题出在哪里。
例如此处:
return localRouteSource.saveRoute(route)
.flatMap { localID ->
route.routeId = localID
remoteRouteSource.saveRoute(route)
}
.flatMapCompletable { localRouteSource.updateRouteID(route.routeId, it) }
我想localRouteSource.saveRoute()
正在使用您向我们展示的交互器,但尚不清楚remoteRouteSource.saveRoute()
或localRouteSource.updateRouteID()
的实现方式。
它们还需要在IO线程上进行订阅。
根据经验,应该在知道需要时切换线程。
换句话说,您应该在知道您正在做IO的地方使用subscribeOn()
,该位置应尽可能接近实际工作。当您知道需要在UI线程中获得这些结果并且可能会在其他线程中获得时,将使用ObserveOn。
在您的示例中,绝对不需要继续使用observeOn(MAIN_THREAD)
,只有在您想要显示结果时才需要({我想)。
其他一些事情:
此代码
override fun createCompletable(data: Map<String, Any>?): Completable {
val routeEntity = data?.get(PARAM_ROUTE)
routeEntity?.let {
return routeRepository.saveRoute(routeEntity as Route)
} ?: return Completable.error(IllegalArgumentException("Argument @route must be provided."))
}
它是在调用该方法时而不是在预订completable时评估的。
换句话说,当您调用该方法时,它将破坏Rx契约并计算data?.get(PARAM_ROUTE)
。如果它是不可变的,则没有什么区别,但是如果它可以在执行期间更改值,则应将其包装在Completable.defer { }
最后,在这里
.flatMap { localID ->
route.routeId = localID
remoteRouteSource.saveRoute(route)
}
您正在修改链外的某物(route.routeId = localID
),这称为副作用。
请谨慎对待此类内容,Rx的构建方式更加安全,可用于不可变对象。
我个人不会介意的,只要您了解正在发生的事情以及何时可能造成问题。