I was looking at the example of an android app coded in MVP (link here). But now I want to recode the given RxJava1 code in the tutorial to an RxJava2 code. However, I am having trouble with it especially unSubscribe() and isUnSubscribed(). I tried to convert it and I will share my attempt.
RxJava1 Code:
public void doLogin(AuthCredentials credentials) {
cancelSubscription();
subscriber = new Subscriber<Account>() {
#Override public void onCompleted() {
if (isViewAttached()) {
getView().loginSuccessful();
}
}
#Override public void onError(Throwable e) {
if (isViewAttached()) {
getView().showError();
}
}
#Override public void onNext(Account account) {
eventBus.post(new LoginSuccessfulEvent(account));
}
};
// do login
accountManager.doLogin(credentials)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
private void cancelSubscription() {
if (subscriber != null && !subscriber.isUnsubscribed()) {
subscriber.unsubscribe();
}
}
This is my attempt on RxJava2 Code:
public void doLogin(AuthCredentials credentials) {
cancelSubscription();
subscriber = new Subscriber<Account>() {
#Override public void onSubscribe(Subscription s) {
// do login
accountManager.doLogin(credentials)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
#Override public void onCompleted() {
if (isViewAttached()) {
getView().loginSuccessful();
}
}
#Override public void onError(Throwable e) {
if (isViewAttached()) {
getView().showError();
}
}
#Override public void onNext(Account account) {
eventBus.post(new LoginSuccessfulEvent(account));
}
};
}
private void cancelSubscription() {
//isUnsubscribed and unsubscribe doesnt work anymore
}
I am fairly new to the concept of RxJava. If anyone can point out my mistakes and guide me that would be great. :)
I apologize for the late answer. Have been extremely busy. As stated by #akarnokd there have been made a lot of changes in RxJava2 as compared to RxJava1. For those who interested please look at this video.
As far as the above question is concerned, we can achieve the same by using DisposableObservables
Here is the answer to the above question. I have tested it and it works.
public void doLogin(AuthCredentials credentials) {
myDisposableObserver = new DisposableObserver<Account>() {
#Override
public void onNext(Account account) {
eventBus.post(new LoginSuccessfulEvent(account));
}
#Override
public void onError(Throwable e) {
if (isViewAttached()) {
getView().showError();
}
}
#Override
public void onComplete() {
if (isViewAttached()) {
getView().loginSuccessful();
}
}
};
// do login
accountManager.doLogin(credentials)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
}
private void cancelSubscriptionToMyPrescriptorManager() {
if (myDisposableObserver != null && !myDisposableObserver.isDisposed()) {
myDisposableObserver.dispose();
}
}
Related
Few days ago I have faced with the treading rx problem. I was thinking how the rxJava threading works. This code didn't want to run on another thread.
mAPIInterface.getAllProjects(accessToken)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Project>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
mView.showLoadProgress();
}
#Override
public void onNext(#NonNull List<Project> allProjects) {
projects = allProjects;
mView.hideLoadProgress();
mView.onProjectsLoaded();
}
#Override
public void onError(#NonNull Throwable e) {
mView.hideLoadProgress();
mView.onProjectsLoadError();
}
#Override
public void onComplete() {
}
});
Then I have find the solution but I'm still not sure if it's ok. That's look like there are some more good solution:
mAPIInterface.getAllProjects(accessToken)
.subscribeOn(Schedulers.newThread())
.map(new Function<List<Project>, List<Project>>() {
#Override
public List<Project> apply(#NonNull List<Project> projects) throws Exception {
return projects;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Project>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
mView.showLoadProgress();
}
#Override
public void onNext(#NonNull List<Project> allProjects) {
projects = allProjects;
mView.hideLoadProgress();
mView.onProjectsLoaded();
}
#Override
public void onError(#NonNull Throwable e) {
mView.hideLoadProgress();
mView.onProjectsLoadError();
}
#Override
public void onComplete() {
}
});
With that map() middleware it works. But should I use that everytime when I need to run the process on another thread?
I'm trying to refactor my synchronization procedure with rxJava support.
But I've faced a strange (for me) error.
Initially, I execute 'sync' procedure. Then in 'onCompleted' I execute syncPart2. It's the same procedure (but with others nodes to sync)
In the 'syncPart2' I get 'error=DatabaseError: Permission denied'. With database rules everything ok, this error appears on the different nodes (and current sync works fine).
Basically, I have 16 nodes to sync one by one, exactly in specific order. Maybe I've chosen wrong Rx operation to do that? By the way, if I use only one 'concat' everything ok! But I have more than 9 (max size of 'concat' args) nodes to sync.
public class RxFirebaseDatabase {
#NonNull
public static Observable<DataSnapshot> observeSingleValueEvent(#NonNull final Query query) {
return Observable.create(new Observable.OnSubscribe<DataSnapshot>() {
#Override
public void call(final Subscriber<? super DataSnapshot> subscriber) {
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(dataSnapshot);
subscriber.onCompleted();
}
}
#Override
public void onCancelled(DatabaseError error) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(new RxFirebaseDataException(error));
}
}
});
}
});
}
}
public static void sync() {
Observable.concat(
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DELETED_OBJECTS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.MSI_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.COURSES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.ALLERGIES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.PHONES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.MEDICINES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.PROFILES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.ANALYSES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DIAGNOSES_NODE))
)
.observeOn(Schedulers.io())
.subscribe(new Subscriber<DataSnapshot>() {
#Override
public void onCompleted() {
syncPart2();
}
#Override
public void onError(Throwable e) {
Log.d(AppConstants.TAG_SYNC, "The error appears: " + e.getMessage());
}
#Override
public void onNext(DataSnapshot dataSnapshot) {
GenericClass genericClass = retrieveInfoAboutNode(dataSnapshot);
if (genericClass.getMyType() == DeletedObject.class) {
handleDeletedObjects(dataSnapshot);
} else if (genericClass.getMyType() == MedicineSchedulerItem.class) {
handleMSI(dataSnapshot);
} else if (genericClass.getMyType() == MedicineCourse.class) {
handleMedicineCourse(dataSnapshot);
} else {
handle(dataSnapshot, genericClass);
}
}
});
}
public static void syncPart2() {
Observable.concat(
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.HOSPITALS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.RECOMMENDATIONS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.USER_FILES_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.SPECIALIZATIONS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DOCTORS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.DOCTOR_VISITS_NODE)),
RxFirebaseDatabase.observeSingleValueEvent(getFirebaseReference(AppConstants.FIREBASE_CONSTANTS.PHOTOS_NODE))
)
.subscribe(new Subscriber<DataSnapshot>() {
#Override
public void onCompleted() {
EventBus.getDefault().post(new FirebaseEvents().new SyncFinished().new AllTasksFinished());
}
#Override
public void onError(Throwable e) {
Log.d(AppConstants.TAG_SYNC, "The error appears: " + e.getMessage());
}
#Override
public void onNext(DataSnapshot dataSnapshot) {
GenericClass genericClass = retrieveInfoAboutNode(dataSnapshot);
handle(dataSnapshot, genericClass);
}
});
}
Actually, permissions were not the cause of this issue.
I've had 'InvocationTargetException' because of Realm incorrect thread. But why the error was 'permissions denied' is still puzzle for me.
I want to send multiple requests over the network and this tutorial
helped but i'm stuck at the latter part .
seems i'm expected to return a value(OrderValues) from onSubscribe,onNext,....
since apply function returns a value. But ....,onNext returns void by default.
Any help?Here is my piece of code
Observable<Restaurant> orderRestaurant= IdentityClient.getAPIService()
.getRestaurantById(restaurantId)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<Menu> orderMenu= IdentityClient.getAPIService()
.getMenuById(menuId)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<User> orderUser= IdentityClient.getAPIService()
.getUserById(userId)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<OrderValues> combineValues=Observable.zip(orderRestaurant,
orderMenu, orderUser,
new Function3<Restaurant, Menu, User, OrderValues>() {
#Override
public OrderValues apply(Restaurant restaurant, Menu menu, User user)
throws Exception {
return new OrderValues(restaurant,menu,user);
}
I get an error here "cannot resolve method 'subscribe anonymous
org.reactivestreams.Subscriber(....OrderValues)
}).subscribe(new Subscriber<OrderValues>() {
#Override
public void onSubscribe(Subscription s) {
}
#Override
public void onNext(OrderValues orderValues) {
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
}
});
I'm assuming that you are using RxJava 2.
Use Observer instead of Subscriber. And also do not assign the result to a new Observable (you called it combineValues).
private void myMethod() {
Observable.zip(orderRestaurant, orderMenu, orderUser, new Function3<Restaurant, Menu, User, OrderValues>() {
#Override
public OrderValues apply(#NonNull Restaurant restaurant, #NonNull Menu menu, #NonNull User user) throws Exception {
return new OrderValues(restaurant, menu, user);
}
}).subscribe(new Observer<OrderValues>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(OrderValues orderValues) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
}
I am using RxSearchView to query the text changes in a form of "Search as you type"
RxSearchView.queryTextChanges(searchView)
but I would like to also catch when the user submits the search, so then I have to use
RxSearchView.queryTextChangeEvents(searchView) or searchView.setOnQueryTextListener
When I use any of these last 2, it looks like they are cancelling the first RxSearchView.queryTextChanges, looks like that there can only be 1 observable attached to SearchView.
How can I observe both events at the same time?
Here is the full code
private void setupSearch() {
RxSearchView.queryTextChangeEvents(searchView)
.skip(1)
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
final boolean empty = TextUtils.isEmpty(searchViewQueryTextEvent.queryText());
if (empty) {
//Dont show anything clear adapter
}
return !empty;
}
}).subscribe(new Subscriber<SearchViewQueryTextEvent>() {
#Override
public void onNext(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String searchTerm = searchViewQueryTextEvent.queryText().toString();
if (searchViewQueryTextEvent.isSubmitted()) {
submitFullSearch(searchTerm);
} else {
submitRecommendationsSearch(searchTerm);
}
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
});
}
There is only one observable since it overwrites the view's listener, but you can use RxSearchView.queryTextChangeEvents(searchView) to monitor both types of events. It gives a stream of SearchViewQueryTextEvent events. For each event, you can check isSubmitted() to determine if it is a submission or a change event and fetch the current text with queryText().
Here is how could use ConnectableObservable to get the events into two streams to filter separately --
private void setupSearch() {
ConnectableObservable<SearchViewQueryTextEvent> searchObs = RxSearchView.queryTextChangeEvents(searchView).publish();
searchObs.skip(1)
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
final boolean empty = TextUtils.isEmpty(searchViewQueryTextEvent.queryText());
if (empty) {
//Dont show anything clear adapter
}
return !empty;
}
}).subscribe(new Subscriber<SearchViewQueryTextEvent>() {
#Override
public void onNext(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String searchTerm = searchViewQueryTextEvent.queryText().toString();
if (!searchViewQueryTextEvent.isSubmitted()) {
submitRecommendationsSearch(searchTerm);
}
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
});
searchObs.subscribe(new Subscriber<SearchViewQueryTextEvent>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchViewQueryTextEvent searchViewQueryTextEvent) {
if (searchViewQueryTextEvent.isSubmitted()) {
submitFullSearch(searchTerm);
}
}
});
Subscription searchSub = searchObs.connect();
I have the following code. It's basically an attempt to send all data from a specific SQLite table to DynamoDB:
Observable.create(new Observable.OnSubscribe<Area>() {
#Override
public void call(Subscriber<? super Area> subscriber) {
try {
for (Area item : areaDao.listAll()) {
subscriber.onNext(item);
}
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
}).flatMap(new Func1<Area, Observable<Area>>() {
#Override
public Observable<Area> call(Area area) {
dynamoDBMapper.save(area);
return Observable.just(area);
}
}).observeOn(
AndroidSchedulers.mainThread()
).doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.w("AreaHandler", "Could not upload area", throwable);
}
}).doOnCompleted(new Action0() {
#Override
public void call() {
Toast.makeText(ctx, R.string.toast_upload_successful, Toast.LENGTH_SHORT).show();
}
}).subscribeOn(
Schedulers.io()
).subscribe(new Action1<Area>() {
#Override
public void call(Area area) {
areaDao.delete(area.getId());
}
});
I'm trying to run it on an emulator with disabled Internet connectivity, and what happens is that the Dynamo client does a couple of (failed) retries, then an exception is thrown and it crashes the app. From what I read in the docs, the exception should be swallowed by doOnError instead of being let out to the wild and killing the process.
What am I missing?
You are grabbing the error in the wrong place. doOnError is for side effects. It does not handle the error.
Option 1. Pass in two Action1
Observable.just(1, 2, 3)
.subscribe(
new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
System.err.print(throwable);
}
});
Option 2: Pass in an Observer
Observable.just(1, 2, 3)
.subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
System.err.print(throwable);
}
#Override
public void onNext(Integer integer) {
System.out.println(integer);
}
});