在 android 上使用干净的 MVP 和 RxJava:如何让演示者在观察 UI 线程的同时不受任何 android 知识的影响?

Using clean MVP on android with RxJava: How can you keep the presenter free of any android knowledge while still observing on the UI thread?

为了使用 mvp 模式在 android 上实现 'clean' 架构,建议将 android 框架视为插件,不要泄漏任何 android意识到依赖关系到展示层。使用 rxjava,如果我有一个设计用于 'push' 数据到视图层的演示者,我可能希望有这样的逻辑:

public interface SearchPresenter {

  interface ViewLayer {
  void updateResults(List<SearchResult> searchResults)
  }
  void bind(ViewLayer viewLayer);
  void unbind();
}

public class SearchPresenterImpl implements SearchPresenter {

   ViewLayer viewLayer;
   CompositeDisposable compositeDisposable;

    @Override
    public void bind(ViewLayer viewLayer) {
      this.viewLayer = viewLayer;

      compositeDisposable = new CompositeDisposable();
      compositeDisposable.add(
            searchInteractor.getSearchResults()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(this::refreshView));

}

private void refreshView(List<SearchResult> searchResults) {
    //send the results back to the view layer to update a RecyclerView      
    viewLayer.updateResults(searchResults)
}

@Override
public void unbind() {
    compositeDisposable.dispose();
}

但是,通过观察 'AndroidSchedulers.mainThread()' 这会强制依赖于:

import io.reactivex.android.schedulers.AndroidSchedulers

此时,我的主持人现在知道 android 并与之相关;我想避免。

处理此问题的建议方法是什么,以便保证将结果传送到 android 的 UI(主)线程上的 ViewLayer,同时演示者不依赖于任何东西android 相关?

AndroidSchedulers.mainThread() 使用 Android 代码来安排主线程上的操作,不要直接在演示者中使用它。 相反,您可以重构演示者以在其构造函数中采用调度程序,在生产代码中使用常规 AndroidSchedulers.mainThread()Schedulers.io(),在测试中您可以发送 Schedulers.trampoline()Schedulers.immediate()

在这个例子中看到这个模式:https://github.com/riggaroo/GithubUsersSearchApp/blob/master/app/src/main/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenter.java

及其测试 class 在这里: https://github.com/riggaroo/GithubUsersSearchApp/blob/8b83095d7a2cc8f3cb69a945224ab4c37cf54a37/app/src/test/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenterTest.java

正如前面的回答所指出的,您可以在 Presenter 的构造函数中注入 Scheduler,但至少还有两种可能性。

  1. 你可以直接注入Schedulerinteractor然后你不需要在presenter做任何操作。然后在测试中你可以模拟你的 interactor 并完全忘记你的测试中的 rxjava 依赖。

  2. 如果您觉得在交互器中保留 AndroidSchedulers 没问题,您可以使用 RxJavaPluginsRxAndroidPlugins 来覆盖测试中的调度程序。

所以定义(Kotlin):

object RxJavaPluginHelper {

    fun setup(scheduler: Scheduler = Schedulers.trampoline()) {
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> scheduler }
        RxJavaPlugins.setComputationSchedulerHandler { scheduler }
        RxJavaPlugins.setIoSchedulerHandler { scheduler }
        RxJavaPlugins.setNewThreadSchedulerHandler { scheduler }
        RxJavaPlugins.setSingleSchedulerHandler { scheduler }
    }

    fun teardown() {
        RxAndroidPlugins.reset()
        RxJavaPlugins.reset()
    }
}

然后使用

 companion object {
        @BeforeClass
        @JvmStatic
        fun setup() {
            RxJavaPluginHelper.setup()
        }

        @AfterClass
        @JvmStatic
        fun teardown() {
            RxJavaPluginHelper.teardown()
        }
    }

就个人而言,我不会争取从演示者中删除这一行,因为从 Presenter 中删除 Android 导入的整个想法是使其可移植到不同的平台,但由于它不太可能发生我会认为它还可以。