I'm very new to Java, android and Rxjava. I recently noticed that in an existing project (not written by me) a chat notification that is supposed to be received isn't received. Thus I started to do some tracing. Below is part of the code.
Note: Notifications that do get received seems to always go to onSuccess in the file FCMServices
I've put breakpoints pretty much everywhere in the code below. What I noticed was for the notifications that I do not receive onSuccess and onError do not get called but onComplete does. However I find that strange as I thought either onSuccess or onError must be called before onComplete.
My understanding of those functions is based on http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html
//FCMService.java
currentConversationRepo.getCurrentConversation()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MaybeObserver<CurrentConversation>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
currentChatDisposable = d;
}
#Override
public void onSuccess(#NonNull CurrentConversation currentConversation) {
System.out.println("This is SUCCESS");
if (channelSid == null && author == null && usedAdId == null){
buildNotifyNotification(body, action, "", userId);
}
if (channelSid != null && author != null) {
if (!channelSid.equals(currentConversation.getChannelSid())) {
createChatNotification(author, channelSid, body);
}
}
currentChatDisposable.dispose();
}
#Override
public void onError(#NonNull Throwable e) {
System.out.println("Error getting current conversation: " + e.getMessage());
currentChatDisposable.dispose();
}
#Override
public void onComplete() {
System.out.println("This is onComplete");
currentChatDisposable.dispose();
}
});
I then started to do some tracing of where onComplete was called and appears that it was called by another onSuccess from the class TestObserver in reactivex.io
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/observers/TestObserver.html
//TestObserver.java
#Override
public void onSuccess(T value) {
onNext(value);
onComplete();
}
Which was in turn called by the onSuccess in MaybeFlatMapBiSelector class. (Also a reactivex.io class I believe)
//MaybeFlatMapBiSSelector.java
#Override
public void onSuccess(U value) {
T t = this.value;
this.value = null;
R r;
try {
r = ObjectHelper.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
actual.onError(ex);
return;
}
actual.onSuccess(r);
}
This turned out to be from the MaybeObserver interface
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html#onComplete--
My question is what exactly are the onSuccess of TestObserver and MaybeFlatMapBiSelector doing? And if it is even possible based on the information I have provided, why is it that some notifications goes to onComplete without going to onSuccess or onError in FCMServices.java
Have you tried to comment currentChatDisposable.dispose(); ? I've had the same issue not long ago where I was disposing of my disposable too early and no data where showing
Usually you call .dispose() when onPause() or onDestroy() of the lifecycle
PS: In case you didn't know Maybe in RxJava return either a single value, nothing at all or an exception.
Related
I am new to doing asynchronous programming in Android Java. I am wondering if there is a way to run another Callback after an initial Callback function has completed. Right now, I think they are running in parallel even though the second relies on the first.
First Callback:
// GETTING USER
private interface FirestoreUserCallback {
void onCallback (User myUser);
}
private void getUser(final FirestoreUserCallback firestoreCallback) {
Task<DocumentSnapshot> task = fStore.collection("users").document(fAuth.getCurrentUser().getUid()).get();
task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
user = documentSnapshot.toObject(User.class);
firestoreCallback.onCallback(user);
Log.d(TAG, "user created");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "user creation failed");
}
});
}
Second Callback:
// GETTING ALL DOCUMENTS
private interface FirestoreDocumentCallback {
void onCallback (List<TableEntries> myEntries);
}
private void getDocuments (final FirestoreDocumentCallback firestoreDocumentCallback) {
fStore.collection("result")
.document(Integer.toString(user.getCompanyNumber())) // need to use User object returned from the first Callback
.collection("SAM").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<TableEntries> results = new ArrayList<>();
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
// add objects to results ArrayList ...
Log.d(TAG, document.getId() + " => " + document.getData());
}
firestoreDocumentCallback.onCallback(results);
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
onCreate:
getUser(new FirestoreUserCallback () {
#Override
public void onCallback(User myUser) {
user = myUser;
}
});
getDocuments(new FirestoreDocumentCallback() {
#Override
public void onCallback(List<TableEntries> myEntries) {
entries = myEntries;
}
});
getDocuments() relies on the user variable being given its value from the first Callback. I'm receiving this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
Callbacks are looking fine. You just need to check if your value is null or not before accessing it. Just add a null check
if(doubleValue!=null)
Using RxJava. First, we fetch the user and then fetch the documents. Rx-Java has an operator flatmap. flatmap is used to execute the sequential tasks, where the second task is dependent on the data from the first task.
final CompositeDisposable disposable = new CompositeDisposable();
//function to fetch user data
Single<User> getUser(){
return API.getUserData(...);
}
//function to fetch ducuments
Sinlge<UserDetail> getDocuments(int userId){
return API.getUserDetail(userId, ...);
}
//Subscribe
disposable.add(getUser()
.flatmap(user-> return getDocuments(...))
.subscribeOn(Scheduler.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObservable(){
#Override
public void onSuccess(UserDetail userDetail){
Log.v("Api result", "Successful";
//Do some work
}
#Override
public void onError(Throwable e)
Log.v("Api result", "Error Returned");
}
}));
If either of the API call fails, onError() is called. If first API fails, second API call is not executed and onError() is called.
The simplest solution for your use-case is to pass both queries to Tasks.whenAllSuccess() method, as explained in my answer from the following post:
Firestore - Merging two queries locally
So once the task is complete, you can use the elements from both queries. Another solution might be to use Android Jetpack with LiveData along with ViewModel, as the Android team recommends.
Calling subscribe on the Google Nearby Messages API for Android results in the Exception:
Attempting to perform a high-power operation from a non-Activity Context
My code:
public void subscribe(final Promise promise) {
_messagesClient = Nearby.getMessagesClient(reactContext.getApplicationContext(), new MessagesOptions.Builder().setPermissions(NearbyPermissions.BLE).build());
_subscribeOptions = new SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.setCallback(new SubscribeCallback() {
#Override
public void onExpired() {
super.onExpired();
emitErrorEvent(EventType.BLUETOOTH_ERROR, true);
}
}).build();
Log.d(getName(), "Subscribing...");
if (_messagesClient != null) {
if (_isSubscribed) {
promise.reject(new Exception("An existing callback is already subscribed to the Google Nearby Messages API! Please unsubscribe before subscribing again!"));
} else {
_messagesClient.subscribe(_listener, _subscribeOptions).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Exception e = task.getException();
Log.d(getName(), "Subscribed!" + e.getLocalizedMessage());
if (e != null) {
_isSubscribed = false;
promise.reject(e);
} else {
_isSubscribed = true;
promise.resolve(null);
}
}
});
}
} else {
promise.reject(new Exception("The Messages Client was null. Did the GoogleNearbyMessagesModule native constructor fail to execute?"));
}
}
Note: The promise Parameter is from React Native, I'm trying to create a wrapper for the API.
At the Log.d event inside my OnCompleteListener, it prints:
Subscribed!2803: Attempting to perform a high-power operation from a non-Activity Context
I do have the API Key and the required Permissions (BLUETOOTH, BLUETOOTH_ADMIN) in my AndroidManifest.xml.
On iOS the API calls work fine.
Solved it! The context
reactContext.getApplicationContext()
is not a valid Activity Context for the Nearby API! I had to use
getCurrentActivity()
which is a method from the base class ReactContextBaseJavaModule.
I am looking at a code that I have to work on. And basically I have to add a validation to a listener of a button.
The code has already multiple validations. They are kind of set in a cascade.
The listener of the buttons calls an asyncCallBack method that if everything is ok, on the onsuccess part of the method calls for the next one, an that one on the next one, until it reaches the end and goes to the next page. I am not a fan of this approach because it is kind of messy. What would the best way to do that using best practices.
An example of the code:
Button btnOK = new Button("Aceptar");
btnOK.addListener(Events.Select, new Listener<ButtonEvent>() {
public void handleEvent(ButtonEvent e) {
myService.getInfo1(1, txt, "N",
new AsyncCallback<List<InfoService>>() {
public void onFailure(Throwable caught) {
// goes back
return
}
public void onSuccess(
List<Object> result) {
// do some validation with the result
validation2();
}
}
}
}
public void validation2(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation3();
}
...
}
}
public void validation3(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation4();
}
...
}
}
Is there a better way of doing this, it seems messy and hard to follow. Adding another validation is complicated. It doesnt seem like a good practice.
Create 1 method in the servlet that calls all the validation methods and do just one call in the client ?
public void validation()
{
boolean ok = validation1();
if (ok) ok = validation2();
return validation;
}
Using mirco services is sometimes hard to deal with. As #Knarf mentioned, this is a way to go. But sometime you may want to handle the calls on the client side. Another one will be using this tiny framework: sema4g. It will help you to solve your problem.
A solution might look like that:
First create the sem4g commands:
private SeMa4gCommand createGetInfoCommand() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<List<InfoService>> proxy = new MethodCallbackProxy<List<InfoService>>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<InfoService> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService.getInfo1(1, txt, "N", proxy);
}
};
}
do that for all your calls;
private SeMa4gCommand createCommandGetDireccionCanalesElectronicos() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<MyResult> proxy = new MethodCallbackProxy<MyResult>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<MyResult> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService. getDireccionCanalesElectronicos(id, proxy);
}
};
}
Once you have done this for all your calls, create a sema4g context and run it:
try {
SeMa4g.builder()
.addInitCommand(new InitCommand() {
#Override
public void onStart() {
// Enter here your code, that
// should be executed when
// the context is started
})
.addFinalCommand(new FinalCommand() {
#Override
public void onSuccess() {
// Enter here the code, that will
// be executed in case the context
// ended without error
}
#Override
public void onFailure() {
// Enter here the code, that will
// be executed in case the context
// ended with an error
})
.add(createGetInfoCommand())
.add(createCommandGetDireccionCanalesElectronicos())
.build()
.run();
} catch (SeMa4gException e) {
// Ups, something wrong with the context ...
}
For more informations, read the documentation. If you have questions, feel free to ask: SeMa4g Gitter room.
Hope that helps.
I'm trying to find a way to handle asynchronous network requests in Swift. My plan is to isolate these calls in their own class, so I need a way to handle callbacks. In Android, I'm doing something like the following using an Interface:
apiService.fetch(data, new Callback() {
#Override
public void onResponse(Response r) {
// success
}
#Override
public void onFailure(Error e) {
// error
}
});
Can someone point me in the direction on how I can handle this similarly in Swift 3? Thanks!
It's possible to have multiple completion handlers. Here is a short example:
func request(url:String, success:#escaping(Any) -> Void, failure:#escaping(Any?) -> Void)
{
let task = URLSession.shared.dataTask(with: URL.init(string: url)!) { (data, response, error) in
if let responseError = error
{
failure(responseError)
}
else if let responseData = data //Make additional checks if there is an error
{
success(responseData) //Call when you are sure that there is no error
}
else
{
failure(nil)
}
}
task.resume()
}
Example of usage:
self.request(url: "https://jsonplaceholder.typicode.com/posts", success: { (data) in
//Do if success
}) { (error) in
//Do if error
}
i'm having trouble implementing rxJava in order to check if there is internet connection on android i'm doing it like this:
on my launcher activity i have this in onCreate:
AndroidObservable.bindActivity(this,
Observable.just(Utils.isActiveInternetConnection(Launcher.this)))
.subscribeOn(Schedulers.newThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
i have an Utils class it is a final class with static methods the methods in that the observable is using are this ones:
public static boolean isActiveInternetConnection(Context context) {
if (isNetworkAvailable(context)) {
try {
HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
urlc.setRequestProperty("User-Agent", "Test");
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(1500);
urlc.connect();
return (urlc.getResponseCode() == 200);
} catch (IOException e) {
Log.e("network", "Error checking internet connection", e);
}
} else {
Log.d("network", "No network available!");
}
return false;
}
private static boolean isNetworkAvailable(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
return true;
}
else {
return false;
}
}
i'm receiving android.os.NetworkOnMainThreadException and i can't find why, thanks in advance.
Observable.just(...) is called immediately on the calling thread (the main thread, in this case). Your code is effectively just an inlined version of this:
boolean activeConn = Utils.isActiveInternetConnection(Launcher.this);
AndroidObservable.bindActivity(this,
Observable.just(activeConn))
.subscribeOn(...)
...
You've tried to move it off the main thread by calling subscribeOn() - but the call has already happened.
The way we handle this (and I'm not sure that this is the best way, but it works) is to defer the network or blocking call until subscription happens, set up the observable to run on the correct threads, and then subscribe:
AndroidObservable.bindActivity(this,
Observable.defer(new Func0<Boolean>() {
#Override
public Observable<Observable<Boolean>> call() {
return Observable.just(Utils.isActiveInternetConnection(Launcher.this));
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
AdamS is correct, however RxJava 2 now offers Observable.fromCallable() to defer an observable operation till subscription.
A good reference:
https://caster.io/lessons/fromcallable-converting-slow-methods-into-an-observable/
Some example code from my use-case:
Single.fromCallable(new Callable<Response>() {
#Override
public Response call() throws Exception {
return NetworkGateway.networkRequest();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
I guess just just calls the method synchronously as it expects the boolean value and it tries to get it.
I am rather bad at RxJava but you may try something like this:
Observable<Boolean> onlineObservable = Observable.create(new Observable.OnSubscribe<Boolean>() {
#Override
public void call(Subscriber subscriber) {
subscriber.onNext(Utils.isActiveInternetConnection(context));
}
});
onlineObservable.subscribeOn(Schedulers.newThread()).subscribe(result -> {...});
this is my retrieve data from DataBase by RXAndroid code:
Observable.create(new Observable.OnSubscribe<List<GRO_VO>>() {
#Override
public void call(Subscriber<? super List<GRO_VO>> subscriber) {
String jsonIn;
jsonIn =retrieveDataFromDB();
Gson gson = new Gson();
Type listType = new TypeToken<List<GRO_VO>>() {
}.getType();
eventJoinList = gson.fromJson(jsonIn, listType);
Log.d("RX",jsonIn);
subscriber.onNext(eventJoinList);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<GRO_VO>>() {
#Override
public void call(List<GRO_VO> eventJoinList) {
Log.d("RX", ".subscribe");
recyclerView.setAdapter(new EventJoinAdapter(eventJoinList));
}
});
I think the just operator will emit data immediately, so it's is not useful to retrieve data from a database via network. It is very easy to use, but it can only be used for data that's already in the ram of the device.
I also had that problem, like #Baniares had, but after I use the create operator, all the problem gone...
From the RXJava documentation:
static <T> Observable<T> create(Observable.OnSubscribe<T> f)
Returns an Observable that will execute the specified function when a Subscriber subscribes to it.
Using the create operator can establish the standard process:
1 .subscribe(...) Subscriber(the sub class of class Observer) start the connection to Observable.
2 .subscribeOn(Schedulers.io()) collect a backGround Thread from the RX-ThreadPool
3 .create(...) retrieve data from server...during some netWork..etc
4 .observeOn(AndroidSchedulers.mainThread()) this means data will be set by the UI-Thread
5 Once we get the data , we can set the data in the onNext( ) method in the .subscribe( ) , the data will be set on the UI by UI-Thread since we make UI-thread do the work on the .observerOn(AndroidSchedulers.mainThread())
And note that if you use .create( ) operator,you must finished your observable in the .create( ) , others operator such like map,flatMap will not be executed after the .create( ) operator.
A very important concept we must need to know before start to use RXJava/RXAndroid. RX is a callback-based library, you tell RX what it should to do in what condition, it invoke your pass-in function(or in JAVA I may should to call them anonymous inner class... ) to achieve what you want.