Loading data Reactive / non-reactive, LiveData, RxJava

Viewed 439

I'll start off by saying I've read a million blogs, watched a thousand videos an I'm getting more confused by the page.. Waffling on typing all this is helping me work some things out.

TL;DR The main question is how to retrieve data reactively (using background threads) for the UI layer AND normally (inline in a method) for Business Layer methods where your method has already been called on a background thread. Do your Repo's & Dao's need multiple methods to perform the same Queries?

@Query("Select * from Product WHERE Product.ProductId = :id")
ProductDb getForId(Integer id);

@Query("Select * from Product WHERE Product.ProductId = :id")
LiveData<ProductDb> getForIdLive(Integer id);

@Query("Select * from Product WHERE Product.ProductId = :id")
Maybe<ProductDb> getForIdRx(Integer id);

Using MVVM architecture. The ViewModel implementation from the new Android architecture components takes care of lifecycle issues so let's use that.

Database implemented with the Room database components - all set up working perfectly, it's how to retrieve the data at various layers in my app that is troubling me.

We'll take a simple example of retrieving a product from the database by Id.

LiveData This option seems fine if you simply want the data in the UI for display. Your activity or fragment creates the ViewModel, sets up an Observer which waits for the data.

// get an instance of the viewmodel
mMyViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

// Setup an observer on the Product.  This will tell us when one is loaded.
final Observer<Product> productObserver = new Observer<Product>() {
    @Override
    public void onChanged(Product product) {
        doSomethingWithProduct(product);
    }
};

The product can then be loaded by calling a method in the ViewModel, the response being spat out to doSomethingWithProduct() which we set up in the observer

mMyViewModel.getProductById(productId).observe(this,productObserver);

The ViewModel has a method

public LiveData<Product> getProductById(int id) {
    return mReferenceDataRepository.loadProductById(id);
}

The Repository has a very similar method

public LiveData<Product> loadProductById(int id) {
    return mProductDao.loadProductById(id);
}

and finally the Dao of the Room architecture as a method to do the actual database retrieval.

@Query("SELECT * from Product WHERE ProductId = :id")
LiveData<Product> loadProductById(int id);

We're using LiveData from the database to the Observer and everything works a treat. Because the Dao is LiveData the retrieval is done on a background thread, which takes care of the ANR which would be spat out by Room. For retrieving data for simple display this route seems perfect.

However, not everything is about the UI!

The app is more complex than that. I'm adding a Stock item to stock and based on several business rules which are stored in the Product/Location etc I may need to place it into quarantine or reject it.

  • The Activity shouldn't implement these business rules, its job is to simply communicate with the user, plus there may be several activities which can add a stock item

  • The ViewModel shouldn't implement these business rules, the same rule could be required from several Activities.

  • The Repository shouldn't implement these business rules - should it?

I'd implement these rules in a StockItemBL (A Business Layer). AddItemToStock method.

Now I need the Product to be loaded from the DB, but I don't want a LiveData<>. I don't want to load the product in a Reactive manner, I don't need to be reactive in the BusinessLayer and it just doesn't work when you need to load several Db tables to perform business logic. I just want a good old fashion

Product p = mReferenceDataRepository.loadProductById(productId);
Location l = mReferenceDataRepository.loadLocationById(locationId);

if (p.getQuarantine())....

if (l.getIsQuarantine())....

but that method returns a LiveData, not just a POJO Product Do I now need another method in the Repository?

I've also looked at RxJava in the same respect.
You can return a Maybe from the Dao and in fact, Rx is easier to manage the exceptions. But it has the same question - do you need separate methods in your repositories and Dao.

In fact, the reason for using a Repository is that you can switch out the implementation. If your methods expose LiveData or Maybe then can you really switch out the implementation easily?

0 Answers
Related