I have just started learning RxJava and thinking reactive.
I have found some article (7 tips) that says subjects should only be used as a last resort or the first implementation, and I have been thinking..
As of now I have one PublishSubject
PublishSubject.create()
.scan(0, (a, b) -> a + 1).subscribe(replaySubject)
It is incrementing the value by 1 every time, and is subscribed by replaySubject
ReplaySubject.create();
That just prints the value. So at start I have 0, then on every PublishSubject.onNext I am incrementing the value, so I got 1, 2 etc. Because its replaySubject I am getting the whole chain.
However I have been thinking whether or not this may be done without subjects? I have been reading the RxJava Wiki and the operators, but I can't figure out any way this could be done.
Update
So the pseudo code I am trying to archieve is somehow to have one observable that starts with 1
Observable.just(1)
Now I do have on click listener
OnClick{ }
And every time I click on some button I want to see all previous numbers + 1, so 1 then 1, 2 next 1,2,3 etc.
I have been trying with ConnectableObservator.replay however this does not succeeded at all. And inside listener I jave been trying to first add scan on my Observable to increase value and then subscribe so I can print it. But this does not work either. Damn I think i'm in a black corner and misunderstood the idea of observables...
Since you are writing an Android app, you can use RxAndroid. Here is an example,
Observable.just(1).flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(Integer initialValue) {
return ViewObservable.clicks(button, false)
.subscribeOn(AndroidSchedulers.mainThread())
.scan(initialValue, new Func2<Integer, View, Integer>() {
#Override
public Integer call(Integer integer, View v) {
return integer + 1;
}
});
}
}).subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(Integer integer) {
System.out.println(integer);
}
});
I'm not sure if it's what you want. Maybe you only need:
ViewObservable.clicks(button, false)
.subscribeOn(AndroidSchedulers.mainThread())
.scan(1, new Func2<Integer, View, Integer>() {
#Override
public Integer call(Integer integer, View v) {
return integer + 1;
}
}).subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(Integer integer) {
System.out.println(integer);
}
});
Related
My Reactive knowledge is very basic and I was wondering what the right way would be if I like to return an observable from a function which is using an observable. I wanna extend the observable which I am calling with a check.
In my example, I think it is a lot of code for not much. I think I would also need to worry about the disposable of the inner observable. Do I?
public Completable updateUserPhotoURL(Uri photoURL, UserProfileChangeRequest profileUpdates) {
return Completable.create(emitter -> {
if (mFirebaseUser == null) {
emitter.onError(new Exception("Firebase User is not initiated"));
}
RxFirebaseUser.updateProfile(mFirebaseUser, profileUpdates).complete()
.subscribe(new DisposableCompletableObserver() {
#Override
public void onComplete() {
emitter.onComplete();
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
emitter.onError(e);
}
});
});
}
What would be the right (more elegant) way of doing so?
I am completely new to rxJava and it's really confusing, I want to make my app offline first and I've decided to use Realm and Retrofit, First I want to get the data from retrofit and then get the data from my remote webservice then, use realm's insertOrUpdate to merge the remote objects with the local one. I'm able to get on this process so far but when I looked into my Network requests on stetho, this method is complete requesting infinite times. Where did I go wrong? Here's the function
public Observable<RealmResults<Event>> all() {
Realm realm = Realm.getDefaultInstance();
return realm.where(Event.class).findAllAsync()
.asObservable()
.filter(new Func1<RealmResults<Event>, Boolean>() {
#Override
public Boolean call(RealmResults<Event> events) {
return events.isLoaded();
}
})
.doOnNext(new Action1<RealmResults<Event>>() {
#Override
public void call(RealmResults<Event> events) {
service.getEvents()
.subscribeOn(Schedulers.io())
.subscribe(new Action1<List<Event>>() {
#Override
public void call(final List<Event> events) {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(events);
}
});
} // auto-close
}
});
}
});
}
and here's the function on my activity, where I use it
private void getEvents() {
Log.i("EVENTSELECTION", "STARTING");
repository.all()
.subscribe(new Subscriber<List<Event>>() {
#Override
public void onCompleted() {
Log.i("EVENTSELECTION", "Task Completed");
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onError(Throwable e) {
Log.e("EVENTSELECTION", e.getMessage());
swipeRefreshLayout.setRefreshing(false);
e.printStackTrace();
}
#Override
public void onNext(List<Event> events) {
Log.i("EVENTSELECTION", String.valueOf(events.size()));
}
});
}
Thank you so much.
Where did I go wrong?
Let's go through it:
1.
public Observable<RealmResults<Event>> all() {
Realm realm = Realm.getDefaultInstance();
This opens a Realm instance that will never be closed. So your Realm lifecycle management is wrong, refer to the documentation for best practices.
2.
return realm.where(Event.class).findAllAsync()
.asObservable() // <-- listens for changes in the Realm
// ...
.doOnNext(new Action1<RealmResults<Event>>() {
#Override
public void call(RealmResults<Event> events) {
service.getEvents() // <-- downloads data
.subscribeOn(Schedulers.io())
.subscribe(new Action1<List<Event>>() {
You basically say that "in case there are any changes made to data in Realm, then download data from the service and write it into the Realm"
Which will trigger the RealmChangeListener which will trigger a download and so on.
This is a conceptual error, you're using Realm notifications incorrectly.
RealmResults<T> is not just a list of objects, it is also a subscription for changes. So you need to keep it as a field reference, and "stay subscribed to changes in the database".
RealmResults<Sth> results;
RealmChangeListener<RealmResults<Sth>> changeListener = (element) -> {
if(element.isLoaded()) {
adapter.updateData(element);
}
};
void sth() {
results = realm.where(Sth.class).findAllSortedAsync("id");
results.addChangeListener(changeListener);
}
void unsth() {
if(results != null && results.isValid()) {
results.removeChangeListener(changeListener);
results = null;
}
}
In your case, RealmResults<T> which symbolizes a subscription and also provides access to the current/new data is wrapped as an Observable<T> which you can create subscribers to.
Observable<List<<Sth>> results;
Subscription subscription;
Action1<List<Sth>> changeListener = (element) -> {
if(element.isLoaded()) {
adapter.updateData(element);
}
};
void sth() {
results = realm.where(Sth.class).findAllSortedAsync("id").asObservable();
subscription = results.subscribe(changeListener);
}
void unsth() {
if(subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
subscription = null;
results = null;
}
}
As you can see, you have a subscription at the start of the component, and an unsubscription at the end of the component.
Calling Observable.first() is incorrect, it does not make sense to do that. If you saw it in any tutorial (I've seen it before...), then that tutorial was wrong.
So it's really a by design on realm and it won't call the onCompleted, I added a .first() at the end of my getEvents function to get only the first result.
I have a ChatThreadsActivity() which displays messages between two people.
Whenever the user opens the activity, I make an API to fetch the most recent 100 messages. So in the onCreate() method I call makeApiRequestToGetChatThread().
I know I am doing alot of things wrong here.
Inside onNext() I add rows to the chatThreadAdapter. I know this is wrong since I update adapter after ever insert.
chatDaoObject.queryChatThreadsFromDB(someId).observeOn(AndroidSchedulers.mainThread()).subscribe()
Should this be anonymously called? When will this be unsubscribed if ever?
How do I unsubscribe from it?
I've read about BackPressure and realize this is it, I've run this on Android Monitor and here's how I can identify it. Am I doing this right?
private void makeApiRequestToGetChatThreads() {
public void onResponse(Call call, final Response response) {
final String responseString = response.body().string();
runOnUiThread (() -> {
final JSONArray array = new JSONArray(responseString);
JSONObject obj;
for (int i=0 ; i < array.length ; i++) {
obj = new JSONObject(array.get(i));
insertAChatIntoDB (obj);
}
}
});
}
private void insertAChatIntoDB(JSONObject o) {
if(insertSubscriber != null) {
insertSubscriber.unsubscribe();
}
insertSubscriber = new Subscriber<Long>() {
public void onCompleted() {
}
public void onError() {
}
public void onNext() {
chatDaoObject.queryChatThreadsFromDB(someId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<ChatObject>>() {
#Override
public void call(List<ChatObject> chatList) {
if (chatList != null) {
//Royal Screw up
//After every insertion in the DB I update the UI
chatThreadAdapter.addAllChatThreadsIntoList(chatList);
//Notify is called inside the above below
//notifyItemRangeChanged(initialPosition,chatList.size())
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
}
});
}
};
try {
//Inserts happening on Schedulers.computation()
chatDaoObject.insertAChatInDB(o).observeOn(Schedulers.computation()).subscribe(insertSub);
} catch (JSONException e) {
e.printStackTrace();
Crashlytics.logException(e);
}
}
I use RxJAVA and SQLBrite Dao and here's what the query looks like:
//In `ChatDao` class
ChatDataDao extends Dao {
...
...
public Observable<long> insertAChatInDB(JSONObject o) {
ChatThreadsTable.ContentValuesBuilder valuesBuilder = ChatThreadsTable.contentValues();
...
//populating columns values
...
return insert(ChatThreadsTable.TABLE_NAME, valuesBuilder.build(), SQLiteDatabase.CONFLICT_IGNORE);
}
public Observable<List> queryChatThreadsFromDB () {
return rawQuery("SELECT * FROM " + ChatThreadsTable.TABLE_NAME).run().mapToList(ChatObjectMapper.MAPPER);
}
...
...
}
Edit:
Is this the right way to query db without worrying about subscription/unsubscription?
rawQuery("SELECT * FROM " + SomeObject.TABLE_NAME + " ORDER BY " + SomeObject.COL1 + " , " +SomeObject.COL2 + " DESC").run().mapToList(SomeObjectMapper.MAPPER)
.flatMap(new Func1<List<SomeObject>, Observable<SomeObject>>() {
#Override
public Observable<SomeObject> call(List<SomeObject> SomeObjects) {
return Observable.from(SomeObjects);
}
}).doOnNext(new Action1<SomeObject>() {
#Override
public void call(SomeObject chatThreadObject) {
}
}).subscribe();
what is going on with all these tutorials telling people to create their own Subscribers? Here's a cleaned up version:
private void makeApiRequestToGetChatThreads() {
// ... some call that calls back onResponse
}
public void onResponse(Call call, final Response response) {
// are you sure this is how you parse a JSON String?
Observable
.from(response.body().string())
.observeOn(Schedulers.computation())
.flatMapIterable(JsonArray::new)
.map(JSONObject::new)
.flatMap(chatDaoObject::insertAChatInDB)
.flatMap(chatDaoObject::queryChatThreadsFromDB)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(chatThreadAdapter::addAllChatThreadsIntoList)
.subscribe(dummy -> {}, throwable -> {
e.printStackTrace();
Crashlytics.logException(e);
});
}
It's quite likely that there's a better way to directly make the network call return an Observable. Check your documentation / peers.
When the Observable completes all processing and UI updates, it will unsubscribe on it's own. However, if the network call takes a while to complete, the user might have already switched screens/apps, and the UI manipulation will break your app. Consider saving the subscription in your view and unsubscribing. Hint: creating the subscription in the onResponse is... not optimal.
You are getting 100s of threads, and updating the UI for each and every one of them. Are you sure you want that?
I don't think you need to care about backpressure.
I use RxJava + Retrofit to make API calls in my Android app. There may be cases when user makes a request and his token is expired. In this cases I receive a normal response in my onNext, but the response contains not the result but an error element with some code. If such thing happens I need to re-login the user and only after getting a new token repeat the original request.
So I want to organize this using RxJava.
To make things easier I will bring a simple example. Let's say I have the following method:
public void test(int someInt){
Observable.just(someInt)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
log("onCompleted");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
log("onError");
}
#Override
public void onNext(Integer integer) {
log("onNext - " + integer);
}
});
I want to check if (someInt == 0) before onNext() is called. If I get false I want to continue and get onNext() called, but if I get true I want to perform some action and repeat the original observable only once, if the condition returns false second time I don't want to repeat again.
Can someone help me to figure out what options do I have for this?
P.S. I am new in RX world.
Here you go. Since you want to retry the whole chain .retryWhen is great for it so you have to "play" a bit with the errors.
Below if you detect a invalid token, you pass an error (only on the first time) which the retryWhen will catch and resubscribe to the whole rx chain (starting from Observable.just(someInt)).
haveRetriedOnce = false;
Observable.just(someInt)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(integer ->{
if(integer == 0){
if(haveRetriedOnce){
return Observable.error(new UserOperationException());
}
// problem, throw an error and the .retryWhen will catch it
return Observable.error(new InvalidTokenException());
}else{
return Observable.just(integer);
}
})
.retryWhen(observable -> observable.flatMap(throwable->{
if(throwable instanceOf InvalidTokenException){
haveRetriedOnce = true;
return just(0); // retry, the int here is irrelevant
}else{
// other error, pass it further
return Observable.error(throwable);
}
}))
.subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
log("onCompleted");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
log("onError");
}
#Override
public void onNext(Integer integer) {
log("onNext - " + integer);
}
}
i have execution method which does some time consuming network calls on io thread
example
/**
* network call
* #param value
* #return
*/
private Observable<Integer> execute(final int value) {
return Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> subscriber) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("done " + value);
subscriber.onNext(value);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io());
}
then i have list of "commands" which must be executed in order. (one after another)
example (Observable.range(x,y) represents list of commands)
public List<Integer> testObservableBackpressure(){
return Observable.range(0,5).flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(Integer integer) {
System.out.println("started " + integer);
return exeute(integer);
}
}).toList().toBlocking().single();
}
in this way out put is
started 0
started 1
started 2
started 3
started 4
done 0
done 1
done 2
done 4
done 3
Produses faster than is consuming
I want results like that
started 0
done 0
started 1
done 1
started 2
done 2
...
but..
public List<Integer> testObservableBackpressure(){
return Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(final Subscriber<? super Integer> subscriber) {
Observable.range(0,5).subscribe(new Subscriber<Integer>() {
#Override
public void onStart() {
request(1);
}
#Override
public void onCompleted() {
subscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
subscriber.onError(e);
}
#Override
public void onNext(Integer integer) {
System.out.println("started " + integer);
execute(integer).subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
subscriber.onNext(integer);
request(1);
}
});
}
});
}
}).toList().toBlocking().single();
}
this way results are as expected
started 0
done 0
started 1
done 1
started 2
done 2
started 3
done 3
started 4
My question would be if there is another more elegant way to handle this problem?
I'm not sure you need any particular backpressure strategy here. Just use concatMap.
If you use concatMap instead of flatMap, each new input value will only subscribe when the last Observable emitted from the concatMap completes. Under the hood, concatMap uses a SerialSubscription for this. That should give you the ordering you want.
The output that I get when I run your code is:
started 0
started 1
started 2
started 3
started 4
done 1
done 3
done 4
done 2
done 0
Notice that the "done" messages are out of order. This is due to the fact that your code basically parallelises the execution of each call to execute. For each item emitted by the Observable.range you flatMap an Observable that runs on its own IOScheduler. Thus every item is handled in parallel on separate threads which makes it impossible for the items to stay in order and correctly interleaved. One option to achieve the desired behaviour is to make sure that all items run on the same IOScheduler (instead of each item on its own):
import rx.Observable;
import rx.Subscriber;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import java.util.List;
public class Test {
private Observable<Integer> execute(final int value) {
return Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> subscriber) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("done " + value);
subscriber.onNext(value);
subscriber.onCompleted();
}
});
}
public List<Integer> testObservableBackpressure(){
return Observable.range(0, 5).flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(Integer integer) {
System.out.println("started " + integer);
return execute(integer);
}
}).subscribeOn(Schedulers.io()).toList().toBlocking().single();
}
public static void main(String[] args) {
new Test().testObservableBackpressure();
}
}
Notice that the only difference is where you invoke the subscribeOn operator. This code results in the following output:
started 0
done 0
started 1
done 1
started 2
done 2
started 3
done 3
started 4
done 4