0
votes

Here's flow data in my app:

In view I got method onClick were I call presenter.Method(). In this method on presenter I pass the call to model(Model got his own layer of abstracion -> interface modelHelper. It's getting injected via dagger 2 in Conctructor Presenter).

In Model i got method for Network call :

@Override
   public void networkCallForData(String request) {
       request = "volumes?q=" + request;
       compositeDisposable.add(
               api.getBook(request)
                       .subscribeOn(Schedulers.io())
                       .observeOn(AndroidSchedulers.mainThread())
                       .subscribe(
                               books -> {
                                   items.clear();
                                   items.addAll(books.items);

                               }
                               , Throwable::printStackTrace
                               , () -> {
                               }
                       )
       );
   }
}

I got 2 questions : 1. In MVP architecture should Model layer got injected instance of abstracted presenter and connect it to model just like with view ? if not how should i send data from model to presenter ?

  1. I try connect presenter to model via RxJava2 but got problem with synchronization. In model i create observable from :
private List<Items> items = new ArrayList<>();

and getter method to it :

 public Observable<List<Items>> getItemsObservable() {
        return itemsObservable;
    }

here i create observable :

    private Observable<List<Items>> itemsObservable = Observable.fromArray(items);

In presenter i got :

 private void getDataFromModel() {

        compositeDisposable.add(
                findActivityModel.getItemsObservable()
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(
                                books -> {
                                       view.setRecycler(books);

                                }, Throwable::printStackTrace
                                , () -> {

                                    view.setRecyclerVisible();
                                }
                        )
        );


    }
}

When i click on button to search i got first empty response because i observe on list with got not updated yet(Its getting updated via method network call). If I press button 2nd time thats when I got need data from 1 request. How should i chain those 2 RxJava method from different class ?

1

1 Answers

0
votes

1. In MVP architecture should Model layer got injected instance of abstracted presenter and connect it to model just like with view?

No. Model layer should not be directly accessing View nor Presentation layer. Also note that it doesn't make much sense to put .observeOn(AndroidSchedulers.mainThread()) in any of your Model layer implementation.

if not how should i send data from model to presenter ?

Model should just respond to Presenters' queries. It could be a simple function call. Model does not need to hold Presenter instances to handle that.


2. ...How should i chain those 2 RxJava method from different class ?

Consider this implementation:

Model

@Override
public Observable<List<Items>> networkCallForData(String request) {
    request = "volumes?q=" + request;
    return api.getBook(request);
}

Presenter

// Call this once in the beginning.
private void getDataFromModel() {

    compositeDisposable.add(
            findActivityModel.networkCallForData(request)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(
                            books -> {
                                   view.setRecycler(books);
                            }, Throwable::printStackTrace
                            , () -> {
                                view.setRecyclerVisible();
                            }
                    )
    );
}

Above implementation should be sufficient if you are getting the data only once per screen. But you mentioned something like a refresh button. For that you can make use of BehaviorSubject or BehaviorProcessor.

Presenter

private BehaviorSubject<List<Item>> items =
    BehaviorSubject.create(); // You may move this line to the Model layer.

// Call this once in the beginning to setup the recycler view.
private void getDataFromModel() {
    // Instead of subscribing to Model, subscribe to BehaviorSubject.
    compositeDisposable.add(
            items.subscribe(books -> {
                   // Any change in BehaviorSubject should be notified
                   view.setRecycler(books);
                   view.setRecyclerVisible();
            });
}


// Trigger this on button clicks
private void refreshData() {
    compositeDisposable.add(
            findActivityModel.networkCallForData(request)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(books -> {
                        // Refresh BehaviorSubject
                        items.onNext(books);
                    });
}

Even this might not perfectly fit your need but hope you get the idea. Also, few side notes:

  1. Observable.fromArray() returns an observable that emits array items one at a time. Therefore, it is not very useful in this scenario.

  2. It seems you have an object and an observable that wraps the object. If you have these two things in the same place that is usually a sign of a bad design.

    private Observable> itemsObservable; private List items;

This is not the right way to use observable but also violates single source of truth. One possible way of refactoring this:

private BehaviorSubject<List<Items>> itemsObservable;
// private List<Items> items; // Remove this line

public Observable<List<Items>> getItemsObservable() {
    return itemsObservable.hide();
}

// Call this whenever you need to update itemsObservable
private void updateItems(List<Item> newItems) {
    itemsObservable.onNext(newItems);
}