Single observer is not calling onError() - java

I've written the following code in my MainActivity of an Android app. When I run the following code, it doesn't throw any exception and onError() also doesn't get called. However I see onSuccess: testing starts twice but I don't see onSuccess: testing ends. Why isn't onError() being called and/or why isn't the app crashing?
Single.timer(1000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.computation())
.subscribeWith(new DisposableSingleObserver<Long>() {
#Override
public void onSuccess(Long initiationTimeStamp) {
String s = null;
Log.d(TAG, "onSuccess: testing starts");
Log.d(TAG, "onSuccess:test "+ s.isEmpty());
Log.d(TAG, "onSuccess: testing ends");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
});

You are calling s.isEmpty() on a NULL String, thats why it ends at first print. That said onSuccess() does not throw anything, so it just stops execution when NullPointerException is thrown (it is silently handled inside RxJava for you). As soon as you subscribe to observable, you get initial value in onSuccess(), then if it changes or you resubscribe you get another value in onSuccess(), thats why it gets called twice. And because onError() is for errors occurring along the operation chain, you do not get an error in onSuccess() when exception is thrown.
This behaviour is intentional. According to Rx Contract, an observer should not receive both onSuccess() and onError(). You need to handle the exception in onSuccess() by yourself.
For example:
Single.timer(1000, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.computation())
.subscribeWith(new DisposableSingleObserver<Long>() {
#Override
public void onSuccess(Long initiationTimeStamp) {
try {
String s = null;
Log.d(TAG, "onSuccess: testing starts");
Log.d(TAG, "onSuccess:test "+ s.isEmpty());
Log.d(TAG, "onSuccess: testing ends");
}
catch (Throwable ex) {
// tell the upstream we can't accept any more data (OPTIONAL)
dispose();
// pass error to error handler
onError(ex);
}
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
});
Good luck :)

onError is for errors that happen along the operator chain. What you do in onSuccess is already at the end of it, and will not be reported in onError.

Related

Exception handling while running threads

I have a an application which has a kafka consumer and updates Elastic search according to the data it receives.
My issue is that when ever ES goes down, the kafka consumer stops completely and doesn't restart.
I believe its due to how my ES code is running:
public CompletionStage<SearchResponse> executeSearch(SearchRequest searchRequest) {
CompletableFuture<SearchResponse> f = new CompletableFuture<>();
client.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {
#Override
public void onResponse(SearchResponse searchResponse) {
f.complete(searchResponse);
}
#Override
public void onFailure(Exception e) {
throw new Exception(); // I am guessing because of this
}
});
return f;
}
If I change my onFailure method to:
public void onFailure(Exception e) {
f.complete(null);
}
It works perfectly but I dont understand why throwing an exception leads to this.
Any help would be greatly appreciated.
For those that need a solution, I changed my code to below for it to work with exception:
public void onFailure(Exception e) {
f.completeExceptionally(new Exception());
}
Also potentially relevant are the exceptionally method and the handle method from CompletableFuture
CompletableFuture.exceptionally((ex)-> ) - https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function-
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#handle-java.util.function.BiFunction-

How do I delay RxJava2 subscription?

I have the following code, which returns a lot of data in the response, so much that I get a NullPointerException when it's being loaded in the android Activity when I scroll down too fast (since not all the data has been initialized yet), no problems if I wait a second and then scroll.
I want a way to delay the subscribe part, so that the Response<GetFeedTopicsResponseBody> is entirely populated with data (none is not initialized) when I call setAdapter. I tried checking response.isSuccessful but that does not work because no problem with the response itself, just the data takes time to deserialize into Java objects from JSON. I also tried onComplete in subscribe but that does not work either.
So I want either a way in RxJava2 to have a boolean value switch to notify the following subscription once it is complete, it will subscribe.
mGetFeedTopicsDisposable = ApiClient.getInstance()
.doGetFeedTopicsQuery(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
.subscribe((Response<GetFeedTopicsResponseBody> response) -> {
if (response.body() != null) {
List<Topic> topics = response.body().getTopics();
if (topics != null) {
mBinding.fragmentTopicListRecyclerTopics.setAdapter(TopicListAdapter.getInstance(topics));
if (response.body().isPaginated()) {
mRequestBuilder.setCursor(response.body().getCursor());
}
}
}
}, (Throwable ex) -> {
Log.e(TAG, ex.getMessage());
});
The error message I specifically got was:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.models.User.getThumbnailImageUrl()' on a null object reference
where this User object is set as a field of the Topic object which is added into the list of topics retrieved with getTopics(). If I don't scroll, I don't get this NullPointerException and the thumbnail urls for the Users are loaded properly.
Question : How do I delay RxJava2 Subscription?
Example :
I have added repeat(...) for better understanding.
io.reactivex.Observable
.just(new Object())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.delay(10, TimeUnit.SECONDS)
.repeat(2)
.doOnSubscribe(disposable -> Log.d("Delay Example ","Observer subscribed at : "+ DateFormat.getDateTimeInstance().format(new Date()) + " and execute after 10 seconds"))
.subscribe(new DefaultObserver<Object>() {
#Override
public void onNext(Object o) {
Log.d("Delay Example ","on Next : "+ DateFormat.getDateTimeInstance().format(new Date()));
}
#Override
public void onError(Throwable e) {}
#Override
public void onComplete() {
Log.d("Delay Example ","on Complete : "+ DateFormat.getDateTimeInstance().format(new Date()));
}
});
Output:
You see, on Next is called twice with 10 second delay.
Here, you can do adapter related operations in onComplete. :)
Hope this answers the question that you've asked.

Single.just exception

I'm stuck to understand why RxJava does not capture my exception and it just crashes. The exception occurs while getting the object to emit.
Here is how my code looks like:
TestService testService = new TestService();
Single.just(testService.testGetUser())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<User>() {
#Override
public void onSuccess(#NonNull User user) {
Log.d(TAG, "onSuccess(): Launching Home Activity");
Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
startActivity(intent);
}
#Override
public void onError(#NonNull Throwable e) {
Log.d(TAG, "onError()");
}
});
My TestService class looks like:
public class TestService {
public User logInUser(LogInData logInData) throws RuntimeException {
Log.d(TAG, "logInUser(): Logging user in");
User user = this.guideMeAroundData.logInUser(logInData);
return user;
}
}
In my logInUser() method I'm throwing an exception if the user is not valid. This causes my whole Android app to crash.
It looks like this is not the correct way to do it. Could anybody please tell how should I handle an scenario where an exception is thrown while getting the object(s) to be emitted?
This is a very common (and baffling) misunderstanding of Java syntax and operations. The code
Single.just(testService.testGetUser())
is equivalent to
User tmp1 = testService.testGetUser();
Single.just(tmp1)
where testGetUser executes before RxJava even gets involved thus any exception it throws happens before that.
You are probably looking for the
Single.fromCallable(() -> testService.testGetUser())
or simply define the Retrofit API as Single<User> logInUser(LogInData data).
Unchecked exceptions are catched by the RxJava for the most part.
But for checked exceptions you need to provide your own try{}catch(){}
block.
One example would be -
String transform(String input) throws IOException;
Observable.just("Hello!")
.map(input -> {
try {
return transform(input);
} catch (Throwable t) {
throw Exceptions.propagate(t);
}
})
Check out this post for info, it explained very well.
http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/

Using a ListenableFuture Azure call inside an AsyncTask

I'm a bit new to Android, Java, and Azure, and I'm using the Azure MobileServiceClient class to try to call various APIs in my backend. The problem I'm having is that MobileServiceClient's methods all seem to be async, using ListenableFutures.
This would be just fine, except that I want to use these methods in a helper class that also does other stuff that ought to be async (so it extends AsyncTask). However, since the MobileServiceClient call is async, it causes the AsyncTask to return prematurely. I want the AsyncTask not to call its onPostExecute method until the MobileServiceClient method has returned.
How do I avoid this issue? Do I need to change my architecture? Is it possible to put a MobileServiceClient call inside another async task and have it block it?
#Override
protected Boolean doInBackground(Void... params) {
Log.i(TAG, "Doing background task");
if(mTaskType==tTaskType.LOGIN_TASK){
login();
//do other stuff here that should be async
}
return true;
}
private void login(){
Log.i(TAG, "Doing login task...");
ListenableFuture<JsonElement> result = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(result, new FutureCallback<JsonElement>() {
#Override
public void onFailure(Throwable exc) {
error.setError(ErrorHelper.Error.NETWORK_ERROR);
}
#Override
public void onSuccess(JsonElement result) {
}
});
}
I'm going to preface this with the caveat that I'm not very familiar with Android either. However from my experience on other platforms and a quick search of the APIs this is the approach I think you should take. I'm also not promising that code code snippets will compile as I haven't checked that, but they should be close to doing so.
Your login method should return a ListenableFuture<T> and then the doInBackground method can then add it's own callback which is executed when the login completes.
If you want something else to be able to wait for the doInBackground task to complete then that should also return a ListenableFuture<T> which can be done by using the Futures.transform method to chain together a series of async calls.
Here is what I think it should look like:
protected void doInBackground(Void... params) {
Log.i(TAG, "Doing background task");
if(mTaskType==tTaskType.LOGIN_TASK){
var loginFuture = ListenableFuture<UserDetail> login();
Futures.addCallback(loginFuture, new FutureCallback<UserDetail>() {
#Override
public void onSuccess(UserDetail userDetail)
{
// do other stuff here that should be async
// also optionally you could implement this as a transform
// style thing to and return another future from this `doInBackground`
// method so other parts of your code could know when it is completed.
}
#Override
public void onFailure(Throwable exc) {
// I'd quite likely move the error handling from the login method here
// as that way it can also handle any exceptions caused by the transform
// from json to user detail as well.
}
})
}
}
private ListenableFuture<UserDetail> login(){
Log.i(TAG, "Doing login task...");
ListenableFuture<JsonElement> loginFutureResult = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(loginFutureResult, new FutureCallback<JsonElement>() {
#Override
public void onFailure(Throwable exc) {
// This is just to keep with what your style is, for recording the error
// I think you might be better off handling it at a higher level and
// also you might want to check `exc` to see if it was an actual network
// error and not for example just failed credentials or something.
error.setError(ErrorHelper.Error.NETWORK_ERROR);
}
#Override
public void onSuccess(JsonElement result) {
Log.i(TAG, "The login was successful");
}
});
// lets pretend that instead of returning the JSON response
// you wanted to map it to a user detail before returning, just to show how to do that.
AsyncFunction<JsonElement, UserDetail> transformUserJsonFunction =
new AsyncFunction<JsonElement, UserDetail>() {
public ListenableFuture<UserDetail> apply(JsonElement userJson) {
// some code to map the json element to user detail
UserDetail userDetail = new UserDetail(userJson);
return Futures.immediateFuture(userDetail);
}
};
return Futures.transform(loginFutureResult, transformUserJsonFunction);
}
I hope that points you in the right direction.
Well, I did it with a flag - it's not pretty though. I'm still interested in any ways to do this that are more elegant or correct. Or is this actually the right way to do it?
private void login(){
Log.i(TAG, "Doing login task...");
isLoginFinished= false;
ListenableFuture<JsonElement> result = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(result, new FutureCallback<JsonElement>() {
#Override
public void onFailure(Throwable exc) {
error.setError(ErrorHelper.Error.NETWORK_ERROR);
isLoginFinished= true;
}
#Override
public void onSuccess(JsonElement result) {
Log.i(TAG, "Login call was successful, parsing result:" + result.toString());
isLoginFinished= true;
}
});
while(!isLoginFinished);
}

Usage of AsyncListener#onError

I don't understand when AsyncListener#onError method is called.
Javadoc doesn't help:
Notifies this AsyncListener that an asynchronous operation has failed to complete.
How could it fail? How can I reproduce this error?
UPDATE:
// in HttpServlet doGet method
AsyncContext asyncContext = req.startAsync();
asyncContext.addListener(new AsyncListener() {
// some code
#Override
public void onError(AsyncEvent event) {
// when is it called?
}
});
executeInSomeOtherThread(asyncContext);
What do I need to do in other thread to fail this async operation?
onError will be called if there was an Exception thrown while carrying out the asynchronous operation.
They are typically Throwables that extend java.io.IOException caused by I/O failures because of an unreliable connection or protocol level exceptions due to a logical error because of a mismatch between the client and the server.
You can get the Throwable when onError is invoked by calling:
event.getThrowable();
EDIT to address mjaggard's follow-on questions
Forgetting about AsyncContext for a second, consider the following class:
public class MyRunnableClass implements Runnable {
private Listener mListener;
interface Listener {
void onError(Throwable error);
}
public void setListener(Listener listener) {
mListener = listener;
}
#Override
public void run() {
// Some important code to be executed goes here
// Pretend an exception was caught in a try/catch/finally
// block that was doing an I/O operation
Throwable someError = new IOException();
if (mListener != null) {
mListener.onError(someError);
}
}
}
Is it more clear now how the listener's onError method will be invoked because an exception was raised when MyRunnableClass's run method was invoked?
MyRunnableClass mrc = new MyRunnableClass();
mrc.setListener(new Listener() {
#Override
public void onError(Throwable error) {
}
});
Executors.newSingleThreadScheduledExecutor().schedule(mrc, 1000L, TimeUnit.MILLISECONDS);
This is no different from how an AsyncContext holds onto a listener and notifies it if it encounters an exception that it wishes to report to the listener. How the run method gets invoked is really secondary to the fact that the owner of the code being executed is also the one that holds a reference to the listener.

Categories