Search code examples
androidmosby

Mosby MVI - How to reuse presenter


My presenter looks like following:

// I'm retaining the presenter in a singleton instances map and reuse them
// because they are loading data from the internet and this should be done once 
// per app start only
public static ArticlePresenter get(Article article)
{
    if (INSTANCES.containsKey(article.id()))
        return INSTANCES.get(article.id());
    ArticlePresenter instance = new ArticlePresenter();
    INSTANCES.put(article.id(), instance);
    return instance;
}

@Override
protected void bindIntents()
{
    ArrayList<Observable<ArticlePartialStateChanges>> observables = new ArrayList<>();

    observables.add(intent(ArticleView::loadArticleIntent)
            .doOnNext(article -> L.d("intent: loadArticleIntent"))
            .flatMap(article -> AndroInteractor.loadArticle(article)
                    .map(data -> (ArticlePartialStateChanges) new ArticlePartialStateChanges.Loaded(data))
                    .startWith(new ArticlePartialStateChanges.LoadingArticle(article))
                    .onErrorReturn(ArticlePartialStateChanges.LoadingArticleError::new)
                    .subscribeOn(Schedulers.io())
            )
    );

    Observable<ArticlePartialStateChanges> allIntents = Observable.merge(observables);
    ArticleViewState initialState = ArticleViewState.builder().build();
    Observable<ArticleViewState> stateObservable = allIntents
            .scan(initialState, this::viewStateReducer)
            .observeOn(AndroidSchedulers.mainThread());
    subscribeViewState(stateObservable, ArticleView::render);
}

And my fragment's loadArticleIntent looks like following:

@Override
public Observable<Article> loadArticleIntent()
{
    return Observable.just(article).doOnComplete(() -> L.d("Article loaded"));
}

Result

If the fragment is created the first time, I get following 3 items:

  1. The initial event
  2. The loading article event
  3. The loaded article or error event

If the fragment is created again, it will retrieve the already existing presenter from the map and will reuse the last known state from it. Then I get following:

  1. The last loaded event
  2. The initial event
  3. The loading article event
  4. The loaded article or error event

This is not perfect, I would need to change the logic to ONLY emit the last known state (the same behaviour that occurs after screen rotation).

How am I supposed to solve this?


Solution

  • Don't reuse Presenter. This is not the way it is intended to be. Reusing them might work today, but there is no guarantee that it will work in the future too.

    So basically you just want to use the data retrieved from the business logic, right? Basically you want to have this part preloaded with data AndroInteractor.loadArticle(article). So just invoke this on app start, rather then the whole presenter. Maybe you use some memory / disk cache library, or just use a BehaviorSubject internally in AndroInteractor.loadArticle(article). That one holds the latest data (if any).

    So your problem is just a "business logic problem" / "caching of data problem" and not really a "Presenter" problem. Hence, you should solve this problem in business logic layer, namely AndroInteractor.loadArticle(article) and not by keeping the whole Presenter.