I have following code
private void tryToLauch() {
try {
launch();
} catch (MyException e) {
postError(e.getErrorMessage());
e.printStackTrace();
}
}
How can I convert it to Rx that will retry in some period in case of exception ?
Given that your method have as return type void, I suggest you to use a Completable.
You can try this solution, using RxJava 2
Completable myCompletable = Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
launch();
}
}).retry(3 /*number of times to retry*/, new Predicate<Throwable>() {
#Override
public boolean test(Throwable throwable) throws Exception {
return throwable instanceof MyException;
}
});
Then subscribe to the Completable
myCompletable.subscribeOn(SubscribeScheduler)
.observeOn(ObserveScheduler)
.subscribe(this::onComplete, this::onError);
Hope this helps.
Related
I use retrofit2 with rxjava extension.
I have a list of REST API urls and want to do this:
for each
check whether a corresponding file locally exists
if yes: call the API and store the response or the HTTP error
if not: store a customized error
return the list of those results
My problem is: apply returns (with an empty RequestResult) before the server response is received. I think, I understand why, but I don't know how to fix it, because I need to return a RequestResult and not the Retrofit observable.
How can this be solved?
Here is my code:
#GET
Observable<Response<ResponseBody>> enroll(#Url String url);
class RequestResult {
CustomException error;
Response<ResponseBody> response;
}
Observable<ClassOfListItem> observable = Observable.fromIterable(listOfItems);
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
Observable<Response<ResponseBody>> callObservable = restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
callObservable
.subscribe(new DisposableObserver<Response<ResponseBody>>() {
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
onPremiseEnrollmentResult.response = responseBodyResponse;
}
#Override
public void onError(Throwable e) {
onPremiseEnrollmentResult.error = new CustomException(e);
}
#Override
public void onComplete() {
}
});
}
else {
requestResult.error = new CustomException("file not found");
}
return Observable.just(requestResult);
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
)
The flatMap() operator allows you to turn one observable into a different observable. You have a nested observer chain inside your apply() which is not part of the observer chain, so it will be empty because it has not completed yet.
To fix this, when the file exists, return the observable.
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
return restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
}
return Observable.error( new CustomException("file not found") );
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
If you need to capture both errors and successes into the list, then you can add map() operator to wrap RequestResult around the response and onErrorResumeNext() to wrap RequestResult around the error before the toList() operator.
If you are making api call on background thread then what you can do is invoke it synchronously....in your case your retrofit api method would change to following
Call<Response<ResponseBody>> enroll(#Url String url);
and you'd invoke by calling restAPI.enroll(listItem.url).execute()
I would like to wrap a synchronous method from parse.com mainly ParseObject.save() into a RxJava wrapper. I have come up with the below:
public Observable<Void> uploadFix(final ParseObject parseObject) {
return Observable.defer(new Func0<Observable<Void>>() {
#Override
public Observable<Void> call() {
try {
return Observable.just(fix.save());
} catch (ParseException e) {
return Observable.error(e);
}
}
});
}
This is giving me an error: Observable cannot be applied to void.
Basically is there any way to wrap this call with RxJava and get notified if the save is successful?
fix.save() returns void so you can't use it as an argument to Observable.just(). You can return a boolean instead.
public Observable<Boolean> uploadFix(final ParseObject parseObject) {
return Observable.defer(new Func0<Observable<Boolean>>() {
#Override
public Observable<Boolean> call() {
try {
fix.save();
return Observable.just(true);
} catch (ParseException e) {
return Observable.error(e);
}
}
});
}
you could also use a Completable. It is used when you don't except a return-value. If RxJava for Android will bump-up to version 2, you can not use Observabl anymore, because null values are not allowed anymore.
Please look at my example. I am using RxJava2-RC5 for testing. Test should complete within 2 seconds + overhead.
#org.junit.Test
public void name() throws Exception {
Completable completable = Completable.fromAction(() -> doWorkSync());
TestObserver<Void> test = completable.test();
test.assertComplete();
}
private void doWorkSync() {
// simulate work
try {
Thread.sleep(2_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
The Log.d(...) expression in the catch(...) block below gets executed (I can see the log output in Android Studio), but the debugger won't stop at the breakpoint set at that very same line. Why is that? The debugger stops at other breakpoints.
Observable.create(new Observable.OnSubscribe<MobileBankIdSessionResponse.MobileBankIdSession>() {
#Override
public void call(final Subscriber<? super MobileBankIdSessionResponse.MobileBankIdSession> subscriber) {
Schedulers.newThread().createWorker().schedule(new Action0() {
#Override
public void call() {
try {
MobileBankIdSessionResponse r = User.getMobileBankIdSession(reference, nationalIdentity).toBlocking().first();
String progressCode = r.getResponse().progress.progressCode;
if (StringUtils.equals(progressCode, "COMPLETE")) {
subscriber.onNext(r.getResponse());
subscriber.onCompleted();
} else if (StringUtils.equals(progressCode, "USER_SIGN")
|| StringUtils.equals(progressCode, "OUTSTANDING_TRANSACTION")) {
Schedulers.newThread().createWorker().schedule(this, 2, TimeUnit.SECONDS);
} else if (StringUtils.equals(progressCode, "NO_CLIENT")) {
subscriber.onError(new Throwable("Fel vid signering"));
}
} catch (Exception e) {
Log.d("AtError", "here");
subscriber.onError(e);
}
}
});
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<MobileBankIdSessionResponse.MobileBankIdSession>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable throwable) {
mUserLoggedInOutSubject.onNext(Pair.create(throwable.getMessage(), LoginStates.ERROR));
}
#Override
public void onNext(MobileBankIdSessionResponse.MobileBankIdSession mobileBankIdSession) {
setSession(mobileBankIdSession.session, nationalIdentity);
}
});
To anyone who came here for answer, It is possible you have not called .subscribe(e ->{}); on the Single ,Flowable etc Object you are using.
I'm pretty new to RxJava and Retrofit and am trying to write my API calls with it. All the API calls return a JSON body on error which is in the general format as,
{"errors":[{"code":100, "message":"Login/Password not valid", "arguments":null}]}
Currently my code for the login API call (others are also similar) is,
mConnect.login(id, password)
.subscribe(new Subscriber<Token>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted()");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError(): " + e);
if (e instanceof HttpException) {
// dump e.response().errorBody()
}
}
#Override
public void onNext(Token token) {
Log.d(TAG, "onNext(): " + token);
}
});
When I get an error at the onError(), I would like to automatically decode the JSON in the error body to a POJO instead and use that. Is there a way to do this preferably in one place for all other API calls. Any help is appreciated.
I would suggest the use of a reusable Transformer along with the onErrorResumeNext operator to encapsulate your logic. It'd look something like this:
<T> Observable.Transformer<T, T> parseHttpErrors() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable.onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpErrorPojo errorPojo = // deserialize throwable.response().errorBody();
// Here you have two options, one is report this pojo back as error (onError() will be called),
return Observable.error(errorPojo); // in this case HttpErrorPojo would need to inherit from Throwable
// or report this pojo back as part of onNext()
return Observable.just(errorPojo); //in this case HttpErrorPojo would need to inherit from <T>
}
// if not the kind we're interested in, then just report the same error to onError()
return Observable.error(throwable);
}
});
}
};
}
Pay attention to the comments in the code, since you have to make the decision whether you want to report the parsed response onError() or onNext().
Then you can use this transformer anywhere in your API calls like this:
mConnect.login(id, password)
.compose(this.<Token>parseHttpErrors()) // <-- HERE
.subscribe(new Subscriber<Token>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted()");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError(): " + e);
if (e instanceof HttpErrorPojo) {
// this will be called if errorPojo was reported via Observable.error()
}
}
#Override
public void onNext(Token token) {
Log.d(TAG, "onNext(): " + token);
if (token instanceof HttpErrorPojo) {
// this will be called if errorPojo was reported via Observable.just()
}
}
});
Deserialize may be an issue too. You can use the retrofit converter to deserialize it (or do it yourself).
My solution adds a bit to the one from murki:
<T> Observable.Transformer<T, T> parseHttpErrors() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable.onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Throwable throwable) {
if ( throwable instanceof HttpException ) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL) // write your url here
.addConverterFactory(GsonConverterFactory.create())
.build();
Converter<ResponseBody, Error> errorConverter =
retrofit.responseBodyConverter(Error.class, new Annotation[0]);
// Convert the error body into our Error type.
try {
Error error = errorConverter.convert(((HttpException) throwable).response().errorBody());
// Here you have two options, one is report this pojo back as error (onError() will be called),
return Observable.error(new Throwable(error.getMessage()));
}
catch (Exception e2) {
return Observable.error(new Throwable());
}
}
// if not the kind we're interested in, then just report the same error to onError()
return Observable.error(throwable);
}
});
}
};
}
and then at the onError(),
#Override
public void onError(Throwable e) {
progressBar.setVisibility(View.GONE); // optional
if ( !TextUtils.isEmpty(e.getMessage()) ) {
// show error as you like
return;
}
// show a default error if you wish
}
is there a more elegant way to do an assert throws exception in Android then this?
public void testGetNonExistingKey() {
try {
alarm.getValue("NotExistingValue");
fail( );
} catch (ElementNotFoundException e) {
}
}
Something like this does not work?!
#Test(expected=ElementNotFoundException .class)
Thanks, Mark
Are you using a junit4 test runner? The #Test annotation won't work if you're running a junit3 test runner. Check the version that you're using.
Secondly, the recommended way to check for exceptions in your code is to use a Rule (introduced in junit 4.7).
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void throwsIllegalArgumentExceptionIfIconIsNull() {
// do something
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Icon is null, not a file, or doesn't exist.");
new DigitalAssetManager(null, null);
}
You can continue to use the #Test(expected=IOException.class), but the above has the advantage that if an exception is thrown before the exception.expect is called, then the test will fail.
I did something very similar to hopia's answer with a couple of improvements. I made it return the exception object so that you can check its message or any other properties, and I declared a Testable interface to replace Runnable because Runnable doesn't let your code under test throw checked exceptions.
public interface Testable {
public void run() throws Exception;
}
public <T extends Exception> T assertThrows(
final Class<T> expected,
final Testable codeUnderTest) throws Exception {
T result = null;
try {
codeUnderTest.run();
fail("Expecting exception but none was thrown.");
} catch(final Exception actual) {
if (expected.isInstance(actual)) {
result = expected.cast(actual);
}
else {
throw actual;
}
}
return result;
}
Here's an example of calling it.
InvalidWordException ex = assertThrows(
InvalidWordException.class,
new Testable() {
#Override
public void run() throws Exception {
model.makeWord("FORG", player2);
}
});
assertEquals(
"message",
"FORG is not in the dictionary.",
ex.getMessage());
If you're using Kotlin, you can take advantage of reified types to avoid passing the Exception subclass as an argument:
inline fun <reified T : Exception> assertThrows(runnable: () -> Any?) {
try {
runnable.invoke()
} catch (e: Throwable) {
if (e is T) {
return
}
Assert.fail("expected ${T::class.qualifiedName} but caught " +
"${e::class.qualifiedName} instead")
}
Assert.fail("expected ${T::class.qualifiedName}")
}
#Test
fun exampleTest() {
val a = arrayOf(1, 2, 3)
assertThrows<IndexOutOfBoundsException> {
a[5]
}
}
This is how I do it. I create a static method called assertThrowsException that takes in as arguments an expected exception class and a Runnable which contains the code under test.
import junit.framework.Assert;
public SpecialAsserts {
public void assertThrowsException(final Class<? extends Exception> expected, final Runnable codeUnderTest) {
try {
codeUnderTest.run();
Assert.fail("Expecting exception but none was thrown.");
} catch(final Throwable result) {
if (!expected.isInstance(result)) {
Assert.fail("Exception was thrown was unexpected.");
}
}
}
}
This is the sample code to use the special assert in your test class (that extends AndroidTestCase or one of its derivatives):
public void testShouldThrowInvalidParameterException() {
SpecialAsserts.assertThrowsException(InvalidParameterException.class, new Runnable() {
public void run() {
callFuncThatShouldThrow();
}
});
}
Yes, there's a lot of work, but it's better than porting junit4 to android.
With junit3 the following might help.
public static void assertThrows(Class<? extends Throwable> expected,
Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
if (!expected.isInstance(t)) {
Assert.fail("Unexpected Throwable thrown.");
}
return;
}
Assert.fail("Expecting thrown Throwable but none thrown.");
}
public static void assertNoThrow(Runnable runnable) {
try {
runnable.run();
} catch (Throwable t) {
Assert.fail("Throwable was unexpectedly thrown.");
}
}