Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Room with RxJava2 - (Flowable, Maybe, Repository Pattern)

I am using Room for the database layer in my application and Retrofit for network calls - both in room and retrofit I am using RxJava2 (this is my first project with rxjava so I am still quite newbie in this area). To inject database, api etc. I am using Dagger 2.

I want to make a Network Call and add response from the network to the database. When there is no need for making another network call - I want to fetch data from the database. I am having problem with the use of Maybe / Flowable in my room repository.

This is Dao:

@Dao
public interface CoinDao {
    @Query("SELECT * FROM coin")
    Flowable<List<Coin>> getAllCoins();

    @Insert
    void insert(List<Coin> coins);

    @Update
    void update(Coin... coins);

    @Delete
    void delete(Coin... coins);
}

This is my repository:

public class CoinRepository implements Repository {

private CoinMarketCapNetworkApi api;

private final CoinDao coinDao;


public CoinRepository(CoinMarketCapNetworkApi api, CoinDao coinDao) {
    System.out.println("Creating CoinRepository");
    this.api = api;
    this.coinDao = coinDao;
}

@Override
public Flowable<List<Coin>> getCoinResults() {
    System.out.println("getting coin results");
    return getCoinResultsFromDatabase().switchIfEmpty(getCoinResultsFromNetwork())
}


@Override
public Flowable<List<Coin>> getCoinResultsFromNetwork() {
    System.out.println("getting results from network");
    return api.getCoins().doOnNext(new Consumer<List<Coin>>() {
        @Override
        public void accept(List<Coin> coins) throws Exception {
            System.out.println("inserting to db");
            coinDao.insert(coins);
        }
    });
}

@Override
public Flowable<List<Coin>> getCoinResultsFromDatabase() {
    System.out.println("getting coins from database");
    return coinDao.getAllCoins();
}

}

I firstly run the app to fill the database only with network call

@Override
public Flowable<List<Coin>> getCoinResults() {
return getCoinResultsFromNetwork();
}

And when the network call was executed the data was successfuly added to the database - I run the app once again with only getting data from database and it was also successfull - the data was fetched from db.

@Override
public Flowable<List<Coin>> getCoinResults() {
return getCoinResultsFromDatabase();
}

However when I try now to do such a thing

return getCoinResultsFromDatabase.switchIfEmpty(getCoinResultsFromMemory));

The problem is that everytime switchIfEmpty is executed and every time "getCoinResultsFromMemory()" is executed (even though the data in database is available).

According to https://medium.com/google-developers/room-rxjava-acb0cd4f3757 I have read that the Flowable when there is no data in database will emit nothing and I should use Maybe. But why getResultsFromMemory() returns empty even though there is data in database? How exactly should I use Maybe in this scenario?

I have tried changing Flowable to Maybe

Maybe<List<Coin>> getCoinResultsFromDatabase()

and doing something like this - to access the resutl from maybe and check there if the list is empty or not, but I don't know how to return flowable in this case:

public Flowable<List<Coin>> getCoinResults() {
getCoinResultsFromDatabase().subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<List<Coin>>() {
                @Override
                public void accept(List<Coin> coins) throws Exception {
                    System.out.println("returning coins from maybe" + coins.get(0).getId());
                    if (coins.isEmpty()) {
                        System.out.println("coin list is empty");
                        Flowable<List<Coin>> flowable = getCoinResultsFromNetwork();
                    } else {
                         Flowable<List<Coin>> flowable = getCoinResultsFromDatabase();
                    }
                }
            });
return flowable //how to access this flowable??
}

Maybe I am missing something and there is a better and more clean solution.

like image 917
Krzysztof Palczewski Avatar asked Apr 03 '18 15:04

Krzysztof Palczewski


1 Answers

There are few issues with your code:

1.Looks like in room Flowable<List<Coin>> getAllCoins() will always return some value: either list of items or empty list so Maybe will not help here

2.In this piece of code

@Override
public Flowable<List<Coin>> getCoinResults() {
    System.out.println("getting coin results");
    return getCoinResultsFromDatabase().switchIfEmpty(getCoinResultsFromNetwork())
}

the getCoinResultsFromNetwork is called right when you call getCoinResults method not when the flowable is empty (this is plain java method call)

You need to perform deferred call. The final solution may look like this

@Override
public Flowable<List<Coin>> getCoinResults() {
    System.out.println("getting coin results");
    return getCoinResultsFromDatabase()
        .filter(list -> !list.isEmpty())
        .switchIfEmpty(
            Flowable.defer(() -> getCoinResultsFromNetwork()))
}
like image 143
Eugene Popovich Avatar answered Nov 18 '22 19:11

Eugene Popovich