RxJava2 repository cache with observers concurrency - android

I have a repository making a network call via a datasource.
I have two use cases that both call that reposository to get the resource but are making different treatments.
Those use cases are subscribing at the same time.
The behavior I want is to make the network call on first subscription, block the other ones and then cache the data to observers in queue.
For now I have a fetch method in the repository actually making the network call (called at one place in my code). I have a get method that return a behavior subject that is valued when the fetch method get data (the uses cases are observing this behavior subject).
I don't like having those two methods and I think it should be transparent. In an ideal case the use cases should call a single repository method and get data (the first one making the network call because cache is missing, the second one getting the cached data).
Is there a way to do this with RxJava ?

Related

Logic to update network requests using Work Manager

I have an application which fetches data from an API. SO basically, right now, the app works as such:
If connected to the internet, fetch data and use Android Room to store for offline use
If not connected to the internet, check if data exists in Room. If exists, display it. If it doesn't exist, display an error message.
I did some research online on how to implement an efficient offline storing policy and Google suggests to use Work Manager to queue requests and then send it when connected.
I actually want to know how to implement this ? (not the code but the logic, i.e should i schedule requests everyday to the API or every time it's connected to the internet ?)
If someone with experience with offline apps could help would be great.
My network requests are done through Retrofit and i already create a class that perform calls to the API.
Keep in mind WM (work manager) is designed to perform operations when certain conditions are met (e.g.: the user has enough battery, the display is off, etc.). So this may end up with your data not being updated when you need it. WM is good for operations you want to happen but are not critical to occur "right now". I'd say always use the Room DB as the single source of truth. If the data is in room, show it, if it's not, fetch it, if you can't, well, you tried. Send a message to the user. You can use a NetworkConnectivityListener to monitor connectivity and check if you have a pending query (you could store the parameters of this query in your Room database in another table for ease of use). So you'd query the DB, obtain the pending queries (if any) and execute them, update the data, and let the ViewModel/Repository decide if there's a context to show this data (UI).
I feel like you are very close to achieve what you need.
So in other words:
UI: Observes its viewModel for some sealed class xxx state to tell it what to do (show an empty list, show an error, pass data to a recyclerview adapter, etc.).
ViewModel: Using its viewModelScope.launch { ... } will call a repository.fetch(...) or similar. Your viewModel will fetch this data when the Fragment tells it to do so (e.g. the user pressed a button) or on some lifecycle event (onStart for example).
The Repository in this case normally exposes a flow (if you can use the experimental api) or a suspend function that can perform the following actions (that can vary depending on your business rules)
If the data is available in the Database, return it immediately.
If the data is old (or we still want to refresh it), then perform the network API (if there's connectivity to do so). If there's No connectivity, you could store this "pending" query in the database for later. You could also check if you have a pending query before doing any of this, perhaps it's outdated or perhaps you need to execute it.
In any case, once the query goes through, you insert the results in the database, and call the same method you used in step 1.
Don't forget to update your "pending" query if you had one (or if you use this).
With WorkManager, you could schedule the "fetch data from API" part to happen at some point (so your data will be kept more up to date), but I all really depends on the use-cases you have.

Pattern for notifying Activity of a network status code

I have many Activities that have several fragments. Part of the work those fragments do is to make authenticated network calls. It is possible that the token will expire. When that happens, I will see the 401 in the networking layer. What is the preferred method to communicate back to up the stack to whichever fragment initiated the network activity that it needs to initiate the logoff procedure? In other words, when the networking layer gets a 401, I want to communicate that back to the UI that the user needs to be log offeven though many other Activities may have also been invoked since then. This is often done via custom exceptions being handled in the app but I was wondering if there was a specific pattern that was preferred other than that. Something using Live Data perhaps?
You should separate in layers. Each layer should be responsible of a single task. There are many ways to do this, but you could use the MVVM Google approach which consists in:
M (Model): data classes and repositories that persist/retrieve them.
V (View): Activities/Fragments that observe data objects (LiveData) exposed by the VM and also call actions of the VM.
VM (ViewModel): Exposes actions, e.g. login, internally calls a Repository which calls the API (e.g. using Retrofit2), when it receives a result, it updates the data objects observed by the V.
Have a look at:
ViewModel
LiveData
Codelab

Can I use ViewModel to send data to the server?

Currently, I am using ViewModel to load a list of Shows in my app. Then, I display it in the main screen. It survives to configuration changes and does not provoke memory leaks since ViewModel has this features.
Now I need to do something a bit different. I need to log in a user making a network call to an endpoint using retrofit 2 and rxjava 2, he will type the email and password, and then will be redirected to the MainActivity. Second, I also need to update the user information, he types his new information and then I send to the server.
May I use ViewModel to log in a user or to update user information? If not, what sould I use instead?
What I need
This retrofit 2 call should survive to configuration changes, as rotating the screen, and it can not cause memory leak.
What I tried
Calling retrofit using enqueue() method in the MainActivity. Obs: This approach is not good because it puts to much things in the ui class, it provokes memory leaks.
Using static inner classes. Obs: It does not survive to configuration changes, as I need to cancel the task in onDestroy().
MVP seems fine to login a user.
You can check google sample project, specifically this class
https://github.com/googlesamples/android-architecture/blob/todo-mvvm-databinding/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskViewModel.java
Usually you would trigger the network operation in the model view, while the code for implementing the network logic is written in a different class (ApiManager for example), and then get the results in your ViewModel
LoginViewModel will have an object of some network manager class
When you click login, the viewmodel will call networkManager.performLogin()
Results are then passed to the viewmodel (RxJava is handy in here).
In your ViewModel do whatever processing you need when you receive the results

RxJava get cache then wait for network calls to update

Can we have any workaround for this problem.
I'm trying to get cache to show to user first then call network api to update current data (if device have network connection)
How we can combine them together ?
If you can use your cache and api as observables whose emits same class of items, you are probably looking for merge method.

Android's ContentResolver.isSyncActive returns false before the actual sync is over

I am triggering a sync with a REST service using SwipeRefreshLayout's onRefresh method by calling ContentResolver.requestSync, which launches my Syncadapter.
The Syncadapter then uses Volley's Request to communicate with the server.
To detect the end of the sync operation and close the activity indicator I use ContentResolver.isSyncActive inside SyncStatusObserver of the Fragment from which I initiated the sync.
The problem is that the ContentResolver.isSyncActive returns false before the actual sync with the server is over, causing the activity indicator to disappear almost immediately.
Am I correct assuming that the asynchronous nature of Volley's Request causes the SyncAdapter's onPerformSync to return immediately which, in turn, makes the ContentResolver think that the sync is over?
If yes, what is the correct/recommended solution here? I can come up with workarounds, but wanted to make sure I am not missing something obvious.
I have looked through many examples including iosched, swiperefresh, basicsyncadapter etc., but they all seem to "hold" the onPerformSync method until everything is over.
Thank you
You should make the requests act synchronously inside the onPerformSync method. I use Retrofit for calls with backends but I think Volley should also provide the synchronous functionality.

Categories

Resources