Using RxJava to temporarily save variables during configuration changes - android

I am using RxJava on an Android project and want to make sure I'm implementing something correctly.
I am using an Observable to login to a server. After the login occurs I may want to save the username on the client side so I'm using the doOnNext() operator to do this. Here's an example of what this looks like:
Observable<Response<Void>> doLogin(Observable<Response<Void>> retainedObservable, Subscriber subscriber, String username, String password, boolean saveUsername) {
if (retainedObservable == null) {
retainedObservable = networkLayer.loginWithcredentials(username, password)
.doOnNext(new Action1<Response<Void>>() {
#Override
public void call(Response<Void> voidResponse) {
if (saveUsername) {
databaseLayer.saveUsername(username).subscribe();
} else {
databaseLayer.saveUsername(null).subscribe();
}
}
})
.cache();
}
mSubscription = retainedObservable.subscribeOn().observeOn().subscribe(subscriber);
return retainedObservable;
}
If the device goes through a configuration change before the login finishes I want to be able to resubscribe to the observable that was running so that I don't have to ask the user to reenter their credentials of press the login button again.
To accomplish this I initially call doLogin(...) and temporarily save the returned observable in a data holder class. If the device goes through a configuration change I can grab the already created observable from the data holder class and resubscribe to it. This works great, my only hang up is that I need to also temporarily save off the username, password and saveUsername flag. This is easy enough to do, however I'm wondering if there's a way I can leverage RxJava to hold these values in the Observable and still have access to them when I need them in doOnNext(...). If I don't do this and just resubscribe to the Observable then when doOnNext(...) runs I won't have values for the saveUsername flag or the username which is obviously bad.
It would be really nice to only have to retain the Observable and somehow have it retain the username, password and saveUsername flag. Is there a way to accomplish this in RxJava or is the way I'm currently doing it the way it needs to be done if I want those variables to be retained during a configuration change?

Essentially, yes. RxJava uses a functional, stateless API, so you're not really able (or supposed) to attach additional data to an observable aside from the values it returns.
As with functional languages, there are generally two ways you can go about this:
You can either capture additional values in a closure, similar to how
you are doing it now. But obviously those values will only be
accessible within the scope of said closure, so you can't return an
Observable from a method and still access variables that were used to
create it later.
If you do need to access that data at a later point, the usual approach is to create some kind of result object that contains all your data. For example a "LoginResult" class, that contains your response as well as the original login data.

Related

Is there a way to create a StateFlow from a Flow

Can I create a StateFlow from a Flow so that I can get the .value of it? Is there a way to do it without using .collect?
Something like
val myStateFlow = StateFlow<MyDataType>(this.existingFlow)
So that later, when someone clicks a button, I can say what the last value of the flow is?
fun handleButton() {
val lastValue = myStateFlow.value
print(lastValue)
}
I would prefer not using collect, since I dont want the flow to flow until someone else decides to collect it.
There is a built-in function called stateIn.
Is there a way to do it without using .collect?
I would prefer not using collect, since I dont want the flow to flow until someone else decides to collect it.
If you use the non-suspending overload of stateIn, the flow collection doesn't have to start right away, but you have to provide an initial value for the state (because this call cannot wait for the first value of the flow if it doesn't suspend).
In that case, you can play with the started argument to make the actual collection lazy, but note that accessing myStateFlow.value won't trigger the collection of the flow and will just return the initial value over and over. Only terminal flow operators (like collect) on the StateFlow will actually trigger the underlying flow collection, which is probably not what you want (but maybe it is!).
Note that you have to have a coroutine running to get the values from the initial flow and set the state accordingly if you want to access values via myStateFlow.value. This is actually what stateIn does by default: it starts a coroutine that collects the flow to set the StateFlow's value - so it technically uses collect.

Convert RxJava Subjects to Observables

I recently ran into discussion about usage of Subject, like this one here: https://github.com/JakeWharton/RxRelay/issues/7
I see a lot of people saying that Subject should be avoided and some people even say any usage of Subject is inherently a bad practice. While I agree on the theoretical level that Subject can be and should be avoided, I can hardly get rid of subjects in real practices. It seems impractical, or even impossible to do so.
Imagine a simple theoretical weather app that has just two things:
a view that displays current weather information
a refresh button which re-fetch the weather information from the server.
(Let's assume for simplicity that the app does not show the data at initial launch, but waits for the users to press refresh button at least once.)
Then you can think of a view model design like this:
ViewModel
interface IWeatherViewModel {
// Provides weather data
Flowable<WeatherData> getWeatherDataToDisplay();
// Lets view to refresh
void refresh();
}
If I use Subject then IWeatherViewModel can be implemented like this:
class WeatherViewModel implements IWeatherViewModel {
private final BehaviorProcessor<WeatherData> weatherData = BehaviorProcessor.create();
private final PublishProcessor<Boolean> eventRefresh = PublishProcessor.create();
WeatherViewModel() {
eventRefresh
.flatMapSingle(x -> getWeatherData())
.subscribe(weatherData);
}
// Provides weather data
public Flowable<WeatherData> getWeatherDataToDisplay() {
weatherData.hide();
}
// Lets view to refresh
public void refresh() {
eventRefresh.onNext(true);
}
private Single<WeatherData> getWeatherData() {
... // omitted for simplicity
}
}
The idea is to have a PublishProcessor that emits refresh event whenever refresh() is called which is then propagated to a BehaviorSubject. All subscribers that observe getWeatherDataToDisplay() will be notified once getWeatherData() is successful.
However I find it difficult to implement the same thing without Subject.
The app needs to propagate refresh() call to stream. I might be able to replace PublishProcessor using Flowable.create() but it doesn't look clean at all, the best I could do is:
private FlowableEmitter emitter;
private final Flowable<Boolean> eventRefresh = Flowable.create(emitter -> {
this.emitter = emitter;
}, BackpressureStrategy.BUFFER);
public void refresh() {
emitter.onNext(true);
}
Now suddenly I have to have a new instance variable that I cannot make final..
Also I am not able to find any operator that can effectively replace BehaviorProcessor, nor any hot observable that emits the latest item immediately on subscription. This behavior is necessary because the view should be able to detach and re-attach seamlessly, just like LiveData.
If you see any improvement that can be made, or have a different approach to the problem, please share your thougts.

Android MVP doubts about validating

I´m starting to implement the MVP pattern on an Android project and I have some doubts about where I should validate the fields before doing any action.
For example, If I have to send a form with three fields (name, email, text).
Should I validate the fields in the activity or I should send them to the Presenter for being validated?
I'm not 100% sure yet if the comunication with the presenter has to be only with the right data already validated or not.
It really depends, my recommendation is that (And what I normally do):
If the field can be validated without access to database or complex operations, I'd do it in the activity. Examples of such fields would be: Password (Passwords need to contain at least 7 characters), Age (Age must be numeric)
If the field needs to be validated by accessing the database (or by web service) or the operation requires complex logic and resource, do it in the presenter. Examples of such fields would be: Username (To check if it is a duplicated username by accessing the database)
Think of it as a front-end and back-end of a website, although not completely same, it does help you to clarify confusing concepts.
View should never decide to do things by itself, presenter keeps waiting by events notified by view and presenter decides what to do then, view only keeps waiting orders from presenter.
So, no, validation is a presenter task, even if it is a very simple task such as validating a field.
You can do like this in activity:
private Presenter mPrensenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
mPrensenter.load(name,email,text);
}
});
}
#Override
public void onRightDataValidated(){
}
then there is two interface MainView and Prensenter:
public interface MainView{
void onRightDataValidated();
}
public interface Presenter{
void load(String name,String email,String text);
}
int the impl of the Presenter,when the data need to be invalidate in load method,u can use MainView.onRightDataValidated to callback , u can find more in my github MVP Demo
Part of the point of MVP is to make testing easier. If you approach questions like these asking, "What if I never tested the view," then that gives the right perspective on what logic should or should not go there. The presenter should lend itself to fast JUnit testing and relieve the developer from needing to write Android instrumentation tests.
Bottom line, you're going to want to test your validation logic to be sure it is sound and if you put that in the Presenter, it makes life easier.
Well I believe you should do the validation in activity. And Simply presenter will call the validation method to check if the validation passes then it will complete the action otherwise show the error.!
In one of my client's project, There is detail page and on click of submit button it should check if detailed page filled then it will save the order with the detail otherwise show the error.
And this is how i have implemented--
Here you can see the isDetailFilledOut() is a validation method and it will return true if validation passes otherwise false.
If it returns true it checks if internet also available then it saves the order by calling model's saveOrder method otherwise shows the fill out detail warning.

Reading from SharedPreferences vs. keeping an instance of the object

THE SCENARIO
I have a class that makes use of a request list set by the user. The request list is stored in SharedPreferences. The dilemma I'm facing is to whether to keep an instance of the request list or to read from SharedPreferences every time the request list is needed (which is very frequent).
Also not that Gson is used to deserialize the object.
The code goes like this:
public List<PrayerTimesCalculator.Time> getDefaultRequestList() {
if (mRequestList != null) return mRequestList;
// Try getting request list from preferences;
Gson gson = new Gson();
String json = mSharedPref.getString(KEY_PREF_REQUEST_LIST, null);
Type listType = new TypeToken<List<Time>>() {
}.getType();
mRequestList = gson.fromJson(json, listType);
if (mRequestList != null) return mRequestList;
// Create default list;
mRequestList = Arrays.asList(
Time.DAWN,
Time.MORNING,
Time.AFTERNOON,
Time.EVENING,
Time.MID_NIGHT);
return mRequestList;
}
THE GOAL
My concern is that if I keep around an instance of the request list, and there are multiple instances of this class, an update to the request list in one instance of the class would not be reflected in the rest of the instances until they are recreated.
Thus, I'm leaning towards reading from SharedPreferences unless there is a better way to keep the request list objected updated in all instances.
THE QUESTION
(1) So, how efficient is it to read the same key from SharedPreferences quite frequently by multiple instances of the object? and (2) Is there a better way to keep the request list objected updated in all instances?
So there are a couple of approaches you can take to this.
First, your object is small - re-reading SharedPreferences thousands of times would hardly be noticeable. It's not like SharedPreferences is on a remote drive or has a "bad connection."
Second, if you don't like that answer, then you need a DAO (Data Access Object). SharedPreferences is a form of this already. It provides a means to store and retrieve data with confidence that you have the most recent data available. But, if you feel like you can improve on it's optimization (because it's generic, and this is your app), then you can provide access to you data through a static object that performs both "read" and "write" operations. This will guarantee that access to the object is done with the most recent data. Of course, you will need to be thread aware, etc. (something that is not always guaranteed by SharedPreferences).
Next, you could persist your data in a database and use Cursors or other built-in or custom DAOs. This requires another level of complexity and a lot of overhead, but is useful when several components of your app might need access to the data, provide updates or needs real-time monitoring of changes because background threads or other objects may make modifications that will change your app behavior or result in UI updates.
Last, you could use more complex data stores like a Content Provider. This is really required for cases where you want/need other apps to access data provided by your app (and your app may also consume the data). That's a complex solution and implementation is well outside the scope of this question.
But I mention it because you seem interested in being certain that frequent reads of SharedPreferences is acceptable. It definitely is acceptable - otherwise there would be something else besides it, databases and Content Providers.

RxJava and Cached Data

I'm still fairly new to RxJava and I'm using it in an Android application. I've read a metric ton on the subject but still feel like I'm missing something.
I have the following scenario:
I have data stored in the system which is accessed via various service connections (AIDL) and I need to retrieve data from this system (1-n number of async calls can happen). Rx has helped me a ton in simplifying this code. However, this entire process tends to take a few seconds (upwards of 5 seconds+) therefore I need to cache this data to speed up the native app.
The requirements at this point are:
Initial subscription, the cache will be empty, therefore we have to wait the required time to load. No big deal. After that the data should be cached.
Subsequent loads should pull the data from cache, but then the data should be reloaded and the disk cache should be behind the scenes.
The Problem: I have two Observables - A and B. A contains the nested Observables that pull data from the local services (tons going on here). B is much simpler. B simply contains the code to pull the data from disk cache.
Need to solve:
a) Return a cached item (if cached) and continue to re-load the disk cache.
b) Cache is empty, load the data from system, cache it and return it. Subsequent calls go back to "a".
I've had a few folks recommend a few operations such as flatmap, merge and even subjects but for some reason I'm having trouble connecting the dots.
How can I do this?
Here are a couple options on how to do this. I'll try to explain them as best I can as I go along. This is napkin-code, and I'm using Java8-style lambda syntax because I'm lazy and it's prettier. :)
A subject, like AsyncSubject, would be perfect if you could keep these as instance states in memory, although it sounds like you need to store these to disk. However, I think this approach is worth mentioning just in case you are able to. Also, it's just a nifty technique to know.
AsyncSubject is an Observable that only emits the LAST value published to it (A Subject is both an Observer and an Observable), and will only start emitting after onCompleted has been called. Thus, anything that subscribes after that complete will receive the next value.
In this case, you could have (in an application class or other singleton instance at the app level):
public class MyApplication extends Application {
private final AsyncSubject<Foo> foo = AsyncSubject.create();
/** Asynchronously gets foo and stores it in the subject. */
public void fetchFooAsync() {
// Gets the observable that does all the heavy lifting.
// It should emit one item and then complete.
FooHelper.getTheFooObservable().subscribe(foo);
}
/** Provides the foo for any consumers who need a foo. */
public Observable<Foo> getFoo() {
return foo;
}
}
Deferring the Observable. Observable.defer lets you wait to create an Observable until it is subscribed to. You can use this to allow the disk cache fetch to run in the background, and then return the cached version or, if not in cache, make the real deal.
This version assumes that your getter code, both cache fetch and non- catch creation, are blocking calls, not observables, and the defer does work in the background. For example:
public Observable<Foo> getFoo() {
Observable.defer(() -> {
if (FooHelper.isFooCached()) {
return Observable.just(FooHelper.getFooFromCacheBlocking());
}
return Observable.just(FooHelper.createNewFooBlocking());
}).subscribeOn(Schedulers.io());
}
Use concatWith and take. Here we assume our method to get the Foo from the disk cache either emits a single item and completes or else just completes without emitting, if empty.
public Observable<Foo> getFoo() {
return FooHelper.getCachedFooObservable()
.concatWith(FooHelper.getRealFooObservable())
.take(1);
}
That method should only attempt to fetch the real deal if the cached observable finished empty.
Use amb or ambWith. This is probably one the craziest solutions, but fun to point out. amb basically takes a couple (or more with the overloads) observables and waits until one of them emits an item, then it completely discards the other observable and just takes the one that won the race. The only way this would be useful is if it's possible for the computation step of creating a new Foo to be faster than fetching it from disk. In that case, you could do something like this:
public Observable<Foo> getFoo() {
return Observable.amb(
FooHelper.getCachedFooObservable(),
FooHelper.getRealFooObservable());
}
I kinda prefer Option 3. As far as actually caching it, you could have something like this at one of the entry points (preferably before we're gonna need the Foo, since as you said this is a long-running operation) Later consumers should get the cached version as long as it has finished writing. Using an AsyncSubject here may help as well, to make sure we don't trigger the work multiple times while waiting for it to be written. The consumers would only get the completed result, but again, that only works if it can be reasonably kept around in memory.
if (!FooHelper.isFooCached()) {
getFoo()
.subscribeOn(Schedulers.io())
.subscribe((foo) -> FooHelper.cacheTheFoo(foo));
}
Note that, you should either keep around a single thread scheduler meant for disk writing (and reading) and use .observeOn(foo) after .subscribeOn(...), or otherwise synchronize access to the disk cache to prevent concurrency issues.
I’ve recently published a library on Github for Android and Java, called RxCache, which meets your needs about caching data using observables.
RxCache implements two caching layers -memory and disk, and it counts with several annotations in order to configure the behaviour of every provider.
It is highly recommended to use with Retrofit for data retrieved from http calls. Using lambda expression, you can formulate expression as follows:
rxCache.getUser(retrofit.getUser(id), () -> true).flatmap(user -> user);
I hope you will find it interesting :)
Take a look at the project below. This is my personal take on things and I have used this pattern in a number of apps.
https://github.com/zsiegel/rxandroid-architecture-sample
Take a look at the PersistenceService. Rather than hitting the database (or MockService in the example project) you could simply have a local list of users that are updated with the save() method and just return that in the get().
Let me know if you have any questions.

Categories

Resources