在处理viewModel内部的协程时,最好让该viewModel实现CoroutineScope
,以便在清除viewModel时取消所有协程。通常,我看到coroutineContext
定义为Dispatchers.Main + _job
,因此默认情况下协程在主UI线程中执行。通常,这是在开放类上完成的,因此您所有的viewModels都可以对其进行扩展,而无需样板代码即可获得范围。
当由于Dispatchers.Main
不可用而尝试对上述viewModels进行单元测试并且尝试使用它引发异常时,会出现此问题。我正在尝试寻找一个好的解决方案,该解决方案在子viewModel上不涉及外部库或过多的样板。
我当前的解决方案是使用Dispatchers.Main
作为默认值添加maincontext作为构造参数。然后在单元测试中,在测试viewModel之前,我将其设置为Dispatchers.Default
。我不喜欢这种解决方案,因为它公开了coroutineContext实现细节,以供所有人查看和更改:
open class ScopedViewModel(var maincontext = Dispatchers.Main) : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = maincontext + _job
override fun onCleared() {
super.onCleared()
_job.cancel()
}
}
class MyViewModel : ScopedViewModel() {}
在测试中:
fun setup(){
viewModel = MyViewModel()
viewModel.maincontext = Dispacther.Default
}
答案 0 :(得分:2)
我个人从RxJava2复制了一个解决方案:如果您的测试是针对RxJava2流(包括两个或多个不同的调度程序)运行的,那么您肯定希望它们全部在单个线程中运行。 这是通过RxJava2测试完成的方式:
@BeforeClass
public static void prepare() {
RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
RxJavaPlugins.setSingleSchedulerHandler(scheduler -> Schedulers.trampoline());
RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
}
我对协程也做了同样的事情。刚刚创建了一个收集调度程序的类,但是可以更改这些调度程序。
object ConfigurableDispatchers {
@JvmStatic
@Volatile
var Default: CoroutineDispatcher = Dispatchers.Default
@JvmStatic
@Volatile
var Main: MainCoroutineDispatcher = Dispatchers.Main
...
}
然后,在@BeforeClass
方法中调用
@ExperimentalCoroutinesApi
fun setInstantMainDispatcher() {
Main = object : MainCoroutineDispatcher() {
@ExperimentalCoroutinesApi
override val immediate: MainCoroutineDispatcher
get() = this
override fun dispatch(context: CoroutineContext, block: Runnable) {
block.run()
}
}
}
这将确保该块将在调用线程中执行。
这是我发现的唯一的构造方法注入方式。