I found this when trying to understand RxJava in this example
getUserObservable method which emit some users i need to know why he put
.subscribeOn(Schedulers.io()) while he already call it on main funcion
i provided snipt for both methods
i know that subscribeOn will make the process happened on background thread , but when he called it two times is this will made any different i don't know , as i understand just calling it one time in getUsersObservable will be enough
private Observable<User> getUsersObservable() {
String[] maleUsers = new String[]{"Mark", "John", "Trump", "Obama"};
final List<User> users = new ArrayList<>();
for (String name : maleUsers) {
User user = new User();
user.setName(name);
user.setGender("male");
users.add(user);
}
return Observable
.create(new ObservableOnSubscribe<User>() {
#Override
public void subscribe(ObservableEmitter<User> emitter) throws Exception {
for (User user : users) {
if (!emitter.isDisposed()) {
emitter.onNext(user);
}
}
if (!emitter.isDisposed()) {
emitter.onComplete();
}
}
}).subscribeOn(Schedulers.io());
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_operator);
getUsersObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Function<User, User>() {
#Override
public User apply(User user) throws Exception {
// modifying user object by adding email address
// turning user name to uppercase
user.setEmail(String.format("%s#rxjava.wtf", user.getName()));
user.setName(user.getName().toUpperCase());
return user;
}
})
.subscribe(new Observer<User>() {
#Override
public void onSubscribe(Disposable d) {
disposable = d;
}
#Override
public void onNext(User user) {
Log.e(TAG, "onNext: " + user.getName() + ", " + user.getGender() + ", " + user.getAddress().getAddress());
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: " + e.getMessage());
}
#Override
public void onComplete() {
Log.e(TAG, "All users emitted!");
}
});
}
This is normally done for 2 reasons:
You see at the place, where the method is invoked, on which scheduler the observable is is subscribed on (if this is done within the method you do not know from outside).
You have the possibility to use the same method and subscribe to it on different schedulers in different places of your app.
But if you know for sure, it's always going to be the same scheduler, you can as well move the subscribeOn() into the method itself.
EDIT
I didn't see, that .subscribeOn(Schedulers.io()) is already called inside the getUsersObservable() method. It does not make sense to call it inside the method and outside, when calling the method. That seems like a bug to me. As described, above, usually .subscribeOn() is called outside the method, but you can also do it inside. Doing both makes no sense.
Related
I often find myself writing this piece of code again and again in multiple activities when using firebase realtime db:
ValueEventListener v =new ValueEventListener() {
#Override
public void onDataChange (#NonNull DataSnapshot dbSnapshot){
String ourKey="";
String ourValueID="";
for (DataSnapshot childSnap : dbSnapshot.getChildren()) {
String childKey = childSnap.getKey();
if (childKey == null) {
//do some stuff 1 // and break/Continue/return
}
//or we can directly do something here, as we already assured key is present
else if(childKey.equals(ourKey)){
//do some stuff 2 // and break/Continue/return
MyClass myClass =childSnap.getValue(MyClass.class);
if(myClass==null){
//do some stuff 3 // and break/Continue/return
}
else if(myClass.getID().equals(ourValueID)){
//do some stuff 4 // and break/Continue/return
}
else {
//do some stuff 5 // and break/Continue/return
}
}
else {
//do some stuff 6 // and break/Continue/return
}
}
}
#Override
public void onCancelled (#NonNull DatabaseError databaseError){
//do some stuff 7
}
};
although this is suppose to be how firebase works, it makes my code a lot more unreadable and difficult to debug. what could be a good approach to use these callbacks in a way, that i write this code once and de-clutter my code base?An example would be great.
Inside onDataChange(), you can just call a method:
ValueEventListener v =new ValueEventListener() {
#Override
public void onDataChange (#NonNull DataSnapshot dbSnapshot){
String ourKey="";
String ourValueID="";
retrieveDataFromFb(dbSnapshot);
public void retrieveDataFromFb(DataSnapshot dataSnapshot){
for (DataSnapshot childSnap : dbSnapshot.getChildren()) {
String childKey = childSnap.getKey();
if (childKey == null) {
//do some stuff 1 // and break/Continue/return
}
else if(childKey.equals(ourKey)){
MyClass myClass =childSnap.getValue(MyClass.class);
}
}
From what I understand you want to store all DB methods in a separate class so that you can reuse these methods which would make the code look cleaner and you are trying to get callback values when they get returned from firebase.
There can be many ways to handle callbacks for events what I recommend is to use an interface it will modularize your code and make it look cleaner, so what you can do is to store the DB methods in a separate class (say FirebaseDB), create your methods there and use an interface to get the callbacks. An example on how you can do it:-
Create an Interface either in the class or separate from the class
public class FirebaseDB {
//This is your interface
public interface DBCallbacklistner {
void onCallback(Map<String, Object> keyMap);
}
public void getkeys(String any_value_you_need_to_pass, DBCallbacklistner dbCallbacklistner){
//I have used a different method here you can use your releveant method here
database.somemethod(any_value_you_need_to_pass, new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot documentSnapshot) {
//Suppose you receive the callback here
if(documentSnapshot.exists()){
Map<String, Object> keysMap = (HashMap<String, Object>) documentSnapshot.getData();
//Pass the callback in your interface
dbCallbacklistner.onCallback(keysMap);
}
}
});
}
}
Use that interface wherever you want
Using the function from the class call that interface and use the values
mFirebaseDBObject.getkeys(value, new FirebaseDB.DBCallbacklistner() {
#Override
public void onCallback(Map<String, Object> keyMap) {
if (keyMap != null) {
//Use your keymap here
}
}
});
One more thing I want to point out is that If there are too many callbacks for different calls, I suggest to make separate interfaces based on logical seperation of callbacks.
Because if there are many callbacks in a single interface you would have to override each one of them, whether you require it or not.
For the time being, i am using the following approach:
Suppose my firebase db consists of a list of objects which can be deserialised to the following format:
class MyClass{
public String myClassUniqueID;
... other attributes;
}
For the db i will be handling all value event listener's lifecycle in my own activity(i.e attaching to the db refernce via dbRef.addValueEventListener(dbListener); or dbRef.removeEventListener(dbListener);, But the process of creating this dbListener and passing it the neccessary tasks to be done would be managed in the following utility function :
public interface DbListenerActions {
void onMyClassObjFound(#NonNull MyClass matchedObj);
default void onMyClassObjNOTFound() {
}
}
public static ValueEventListener getMyClassObjectFinderListener(String id, DbListenerActions actions) {
Log.e(TAG, "onDataChange: our id:" + id);
ValueEventListener dbListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dbSnapshot) {
for (DataSnapshot currChildSnap : dbSnapshot.getChildren()) {
String currChildKey = currChildSnap.getKey();
MyClass currChildValue = currChildSnap.getValue(MyClass.class);
if (currChildKey == null) {
Log.e(TAG, "onDataChange: currChildKey is null. continuing");
continue;
}
if (currChildValue == null) {
Log.e(TAG, "onDataChange: currChildValue is null.continuing");
continue;
}
if (currChildValue.myClassUniqueID.equals(id)) {
Log.e(TAG, "onDataChange: currChildValue id matches our id ");
Log.e(TAG, "onDataChange: performing action and RETURNING(i.e getting out of this callback)");
//do stuff here
actions.onMyClassObjFound(currChildValue);
return;
} else {
Log.e(TAG, "onDataChange: current obj DOES NOT matches our id. continuing");
Log.e(TAG, "onDataChange: current object ID :" + currChildValue.myClassUniqueID);
Log.e(TAG, "onDataChange: --------------------------------------------------------------");
continue;
}
}
Log.e(TAG, "onDataChange: user not found, performing not found action" );
actions.onMyClassObjNOTFound();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
return dbListener;
}
In this way, i am able to get the necessary log info that i want during my debugging and since there are only 2 possible actions that i want to perform, i get a lot more assurance on the working of my listener. 50 lines of necessary but redundant code from 11 activities combined to just 1 utility function!
Now all i need to write is this small , more easy to debug piece of code in each of my activity:
ValueEventListener dbListener=getMyClassObjectFinderListener("some_id", new DbListenerActions() {
#Override
public void onMyClassObjFound(#NonNull MyClass matchedObj) {
//callSomeFunction()
// callSomeOtherFunction(matchedObj)
//...
}
});
Since i made the onMyClassObjNOTFound(..) function default i don't even need to provide that unless i really want to perform some action there. So this whole thing is working nicely for me :D
I asked on twitter about this thing too, somebody told me that an abstract class could also be used for such thing. I didn't got to research more there, but if someone knows about that approach too, then let me know!
I want to implement a logic using RxJava in my android application, which requires three parallel api calls. Only the third api call has a retry logic. If, after having three attempts, the success is achieved then a subsequent call will be made for the fourth api, else only the result of first and second api calls will be passed on to the subscriber.
I tried to achieve this using Zip operator but then got stuck with retry logic for third api call.
Observable<String> observable1 = Observable.just("A","B");
Observable<Integer> observable2 = Observable.just(1,2);
Observable<Boolean> observable3 = Observable.just(Boolean.TRUE, Boolean.FALSE);
Observable.zip(observable1, observable2, observable3, new Function3() {
#Override
public Object apply(String s, Integer integer, Boolean aBoolean) throws Exception {
if (aBoolean==null){
alphabets3.retry(3).doOnComplete(new Action() {
#Override
public void run() throws Exception {
// the result will never be used
}
});
}
return s+integer+aBoolean;
}
}).subscribe(new Observer<Object>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
Log.e("onNext-->", o.toString());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
if any Observable failed in the Zip operator, Zip will fail the stream, the only way I know to achieve parallel execution and error handling with Zip, is to add onErrorResumeNext to each Observable, that map the error to a new model to deal with later .. and handling what you want to do in the zip mapping function ... for example
Obsevable.zip(
observable1.onErrorResumeNext{Observable.just(Model(it)},
observable2.onErrorResumeNext{Observable.just(Model(it)},
observable3.retryWhen {t is TimeOutException} //here you can add your retry logic
.onErrorResumeNext(t -> Observable.just(Model(t)),(m1 , m2, m3) -> Result())
I'm using RxJava's flatmap in order to execute multiple calls in sequence where one call relys on the previous call. I also need to know which observable emitted an error in the case that onError is called in order to properly implement my error handling. How do I achieve this?
Here is my code:
mSubscription = RxUtil.callObservable(mDataManager.createAccount(email, password))
.flatMap(new Func1<AuthResult, Observable<Void>>() {
#Override
public Observable<Void> call(AuthResult authResult) {
User user = new User(0, null, null, name, null, username, 0, 0);
return RxUtil.callObservable(mDataManager.createUser(authResult.getUser().getUid(), user));
}
})
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
try {
throw (Exception) throwable;
} catch (FirebaseAuthUserCollisionException ucException) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_exists));
} catch (Exception exception) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_general));
}
}
#Override
public void onNext(Void aVoid) {
getPickUpView().createAccountSuccessful(authResult);
}
});
I was thinking about this the wrong way. Is summary, I thought this was an issue that I needed to address when i didn't. RxJava will emit all errors in the onError method no matter what observable emits the error. Once onError is called the subscription is done, so the flatmap call will never take place.
In summary, all I need to do is handle my errors from both observables I call (the original and the one in the flatmap) in the same onError method.
I am trying firestore database on Android.
This is my code that inserts a document:
public Observable<Post> createPost(final Post post){
return Observable.create(new Observable.OnSubscribe<Post>() {
#Override
public void call(final Subscriber<? super Post> subscriber) {
try{
DocumentReference documentReference = getCollection().document();
post.setId(documentReference.getId());
documentReference.set(post).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
subscriber.onNext(post);
subscriber.onCompleted();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
subscriber.onError(e);
subscriber.onCompleted();
}
});
}catch (Exception ex){
subscriber.onError(ex);
Log.e(LOG_TAG, ex.getMessage(), ex);
}
}
});
}
The document gets inserted into the database but neither of the onSuccess nor the onFailure callbacks are invoked.
Update 1
The issue is not consistent sometimes it works, sometimes the callbacks are invoked after an hour, sometimes after 3 hours etc..
This is happening when there is no internet connection.
Update 2
The issue was reported here and it is closed. I am not sure how to guarantee the correctness of data created offline.
There does not seem to be anything wrong with your code, try perhaps the onCompleteListener callback. So add
.addOnCompleteListener((Task<Void> task) -> {
if(task.getException() != null) {
emitter.onError(task.getException());
}
if(task.isComplete()) { //try task.isSuccessful() if this is what you are looking for
emitter.onComplete();
}
});
If this does not solve your issue perhaps, use an emitter like so:
Completable completable$ = Completable.create((CompletableEmitter emitter) -> {
firebaseFirestore.collection(collection).document(document)
.delete()
.addOnSuccessListener((Void aVoid) -> emitter.onComplete())
.addOnFailureListener((Exception e) -> emitter.onError(e))
.addOnCompleteListener((Task<Void> task) -> {
if(task.getException() != null) {
emitter.onError(task.getException());
}
if(task.isComplete()) { //try task.isSuccessful()
emitter.onComplete();
}
});
});
return completable$;
Okay so I did a simple version of your question but instead of adding a post, it adds a User. The concept is the same.
Here is the method to add a user. It returns an Observable<DocumentReference> just to reference where the user was added.
public Observable<DocumentReference> insertToFirebaseFirestore$() {
UserEntity userEntity = new UserEntity();
userEntity.setEmail("myemail#myemail.com");
userEntity.setBio("I'm a cool cat!");
userEntity.setDisplayName("KoolKat!");
//Notice here I am using an ObservableEmitter instead of Subscriber like you did
return Observable.create((ObservableEmitter<DocumentReference> emitter) -> {
this.firebaseFirestore.collection("tempUsers")
.add(userEntity)
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
emitter.onError(e);
}
})
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
//this gets triggered when I run
emitter.onNext(documentReference);
}
})
.addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
//this also gets triggered when I run
emitter.onNext(task.getResult());
}
});
});
}
When I run this, and place breakpoints inside onSuccess and onComplete. Both of them are triggered and I can see the output.
I call the method from the Activity as follows.
...onCreate method
insertToFirebaseFirestore$()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io()) //observe on io thread cause I don't need it to updateUI.
.subscribe((DocumentReference val) ->{
Log.e("USERACTIVITY", "You have uploaded " + val.getId());
});
The LogcatPrints
12-13 09:47:47.942 15007-15059/com.example.debug E/USERACTIVITY: You have uploaded sFBsF4ZmwGaDdxCEKuF6
12-13 09:47:57.563 15007-15059/com.example.debug E/USERACTIVITY: You have uploaded sFBsF4ZmwGaDdxCEKuF6.
From what I have see with yours, perhaps use an emitter within
your Observable.create.
If that doesn't work try doing the firestore call without wrapping
it in an observable
If all else, might be a connection issue, since you say it happens
intermittently
I came across this with react native.
For inserts the key is to create a new document.
example:
const userRef = firebase.firestore()
.collection("users")
.doc();
userRef.set({name: someName});
This will create the document offline and sync when you come back online.
Further calls such as this will work offline
userRef.collection("Locations").add({location: "Austin,TX"});
I am performing a network request where I send files and a message.
I would like to have an option to cancel current request. I have found two similar questions and both suggests that observable.subscribe(Observer) returns Subscription object which has method unsubscribe().
Here is the first one
And the second one
In my case, I use observable.subscribe(Observer) which is void. Here is my code:
Observable<MessengerRaw> observable = mModel.sendMessage(message, companion, description, multiParts);
observable.subscribe(new Observer<MessengerRaw>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(MessengerRaw value) {
if (getView() != null) {
((MessengerActivity) getView()).resetMessegeView();
((MessengerActivity) getView()).updateMessageList();
}
}
#Override
public void onError(Throwable e) {
getData().remove(0);
if (getView() != null) {
((MessengerActivity) getView()).updateMessageList();
}
}
#Override
public void onComplete() {
hideProgress();
}
});
So how do I unsubscribe/cancel my request?
Thank you.
In RxJava2, you can get Disposable object in onSubscribe callback method of oserver, which you can use to dispose subscription.
In RXJava You must use subscriptions for unsubscribe
private Subscription mSubscription;
/.../
Observable<MessengerRaw> observable = mModel.sendMessage(message, companion, description, multiParts);
Subscription subscription = observable.subscribe(new Observer<MessengerRaw>() {/.../});
When you want to unsubscribe you can call
if(!subscription.isUnsubscribed()){
subscription.unsubscribe();
}
In RXJava 2 observable.subscribe(new Observer<MessengerRaw>() {/.../}); returns Disposable object, you can call dispose();