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?

孤街醉人 提交于 2019-12-06 04:13:26

问题


In an effort to implement 'clean' architecture on android with the mvp pattern it is advised to treat the android framework as a plugin and not leak any android aware dependencies into the presenter layer. Using rxjava, if I have a presenter that is designed to 'push' data to the view layer I may want to have logic like this:

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();
}

However, by observing on 'AndroidSchedulers.mainThread()' this forces a dependency on:

import io.reactivex.android.schedulers.AndroidSchedulers

at which point, my presenter now knows about android and is tied to it; which I want to avoid.

What is the advised way to handle this so that the result is guaranteed to be delivered to the ViewLayer on android's UI (Main) thread while the presenter maintains no dependencies on anything android related?


回答1:


AndroidSchedulers.mainThread() uses Android code to schedule actions on the main thread, don't use it directly in your presenter. instead You can refactor the presenter to take Schedulers in its constructor, in production code use the regular AndroidSchedulers.mainThread() and Schedulers.io(), in testing you can just send Schedulers.trampoline() or Schedulers.immediate() .

see this pattern in this example: https://github.com/riggaroo/GithubUsersSearchApp/blob/master/app/src/main/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenter.java

and its test class here: https://github.com/riggaroo/GithubUsersSearchApp/blob/8b83095d7a2cc8f3cb69a945224ab4c37cf54a37/app/src/test/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenterTest.java




回答2:


As pointed in previous answer you can inject Scheduler in constructor of Presenter but there is at least two more possibilities.

  1. You can inject Scheduler directly to interactor and then you don't need to do any manipulation in presenter. Then in tests you can mock your interactor and completely forget about rxjava dependency in your tests.

  2. You can use RxJavaPlugins and RxAndroidPlugins to override schedulers in tests if you feel that it is fine for you to keep AndroidSchedulers in interactors.

So define (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()
    }
}

And then use

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

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

Personally I would not fight for removing this line from presenter, as whole idea behind removing Android imports from Presenter is to make it portable to different platforms, but since it is unlikely to happen I would treat it as ok.



来源:https://stackoverflow.com/questions/46570795/using-clean-mvp-on-android-with-rxjava-how-can-you-keep-the-presenter-free-of-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!