2017-10-04 7 views
1

Dans le but d'implémenter une architecture «propre» sur android avec le modèle mvp, il est conseillé de traiter l'infrastructure android comme un plugin et de ne laisser aucune dépendance aux fonctions Android dans la couche de présentation. En utilisant rxjava, si j'ai un présentateur qui est conçu pour les données « push » à la couche de vue je veux avoir la logique comme ceci:Utiliser MVP propre sur Android avec RxJava: Comment pouvez-vous garder le présentateur libre de toute connaissance Android tout en observant sur le fil de l'interface utilisateur?

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

Cependant, en observant le «AndroidSchedulers.mainThread() ce forces une dépendance sur:

import io.reactivex.android.schedulers.AndroidSchedulers 

à quel point, mon présentateur connaît maintenant Android et est lié à elle; que je veux éviter.

Quel est le moyen conseillé de gérer cela afin que le résultat est garanti pour être livré à la ViewLayer sur le fil de l'interface utilisateur (principale) android tandis que le présentateur ne maintient aucune dépendance à quoi que ce soit Android?

+0

Peut-être que l'interface ViewLayer nécessite une méthode comme 'Observable getThread();'? – 0X0nosugar

Répondre

1

AndroidSchedulers.mainThread() utilise le code Android pour planifier des actions sur le thread principal, ne l'utilisez pas directement dans votre présentateur. à la place Vous pouvez refactor le présentateur pour prendre Schedulers dans son constructeur, dans le code de production utiliser les AndroidSchedulers.mainThread() et Schedulers.io() réguliers, dans les tests, vous pouvez simplement envoyer Schedulers.trampoline() ou Schedulers.immediate().

voir ce modèle dans cet exemple: https://github.com/riggaroo/GithubUsersSearchApp/blob/master/app/src/main/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenter.java

et sa classe de test ici: https://github.com/riggaroo/GithubUsersSearchApp/blob/8b83095d7a2cc8f3cb69a945224ab4c37cf54a37/app/src/test/java/za/co/riggaroo/gus/presentation/search/UserSearchPresenterTest.java

+0

Bien sûr. Exactement la pièce qui me manquait. – JohnRock

0

Comme il est indiqué dans la réponse précédente, vous pouvez injecter Scheduler dans le constructeur de Presenter mais il y a au moins deux possibilités.

  1. Vous pouvez injecter Scheduler directement à interactor et vous n'avez pas besoin de faire une manipulation dans presenter. Puis dans les tests, vous pouvez mocker votre interactor et complètement oublier la dépendance rxjava dans vos tests.

  2. Vous pouvez utiliser RxJavaPlugins et RxAndroidPlugins pour passer outre ordonnanceurs dans les tests si vous vous sentez qu'il est bien pour vous de garder AndroidSchedulers dans interacteurs.

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

Et puis utilisez

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

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

Personnellement, je ne me battrais pas pour enlever cette ligne de présentateur, comme idée derrière la suppression Android importations en provenance Presenter est de le rendre portable à différentes plates-formes, mais comme il est peu probable que cela se produise je le traiterais comme ok.