How To Make Rxerrorhandlingcalladapterfactory?
Solution 1:
Disclaimer: I'm the author of the blog post you referenced
The original post was meant as a proof of concept and for RxJava 2 so it's easier for me to explain with that version too, but I'll try and cover more ground. I'm guessing you're using version 1, since you talk about adapter-rxjava
and not adapter-rxjava2
. That said, the implementation for version 1 should be pretty straight forward and just a matter of using the right imports.
Here's what I've done using RxJava 2:
classRxErrorHandlingCallAdapterFactoryextendsCallAdapter.Factory {
privatefinal RxJava2CallAdapterFactory original;
privateRxErrorHandlingCallAdapterFactory() {
original = RxJava2CallAdapterFactory.create();
}
publicstatic CallAdapter.Factory create() {
returnnewRxErrorHandlingCallAdapterFactory();
}
@Overridepublic CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
returnnewRxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit));
}
privatestaticclassRxCallAdapterWrapper<R> implementsCallAdapter<R, Object> {
privatefinal Retrofit retrofit;
privatefinal CallAdapter<R, Object> wrapped;
publicRxCallAdapterWrapper(Retrofit retrofit, CallAdapter<R, Object> wrapped) {
this.retrofit = retrofit;
this.wrapped = wrapped;
}
@Overridepublic Type responseType() {
return wrapped.responseType();
}
@Overridepublic Object adapt(Call<R> call) {
Objectresult= wrapped.adapt(call);
if (result instanceof Single) {
return ((Single) result).onErrorResumeNext(newFunction<Throwable, SingleSource>() {
@Overridepublic SingleSource apply(@NonNull Throwable throwable)throws Exception {
return Single.error(asRetrofitException(throwable));
}
});
}
if (result instanceof Observable) {
return ((Observable) result).onErrorResumeNext(newFunction<Throwable, ObservableSource>() {
@Overridepublic ObservableSource apply(@NonNull Throwable throwable)throws Exception {
return Observable.error(asRetrofitException(throwable));
}
});
}
if (result instanceof Completable) {
return ((Completable) result).onErrorResumeNext(newFunction<Throwable, CompletableSource>() {
@Overridepublic CompletableSource apply(@NonNull Throwable throwable)throws Exception {
return Completable.error(asRetrofitException(throwable));
}
});
}
return result;
}
private RetrofitException asRetrofitException(Throwable throwable) {
// We had non-200 http errorif (throwable instanceof HttpException) {
HttpExceptionhttpException= (HttpException) throwable;
Responseresponse= httpException.response();
return RetrofitException.httpError(response.raw().request().url().toString(), response, retrofit);
}
// A network error happenedif (throwable instanceof IOException) {
return RetrofitException.networkError((IOException) throwable);
}
// We don't know what happened. We need to simply convert to an unknown errorreturn RetrofitException.unexpectedError(throwable);
}
}
}
No major changes, just some playing around with return types. Now if you look at the RxJava2CallAdapter
it implements CallAdapter<R, Object>
, so we need to account for this.
Then I've added some checks for the instance types to make sure we're returning the right things.
The really important part is to make sure you import the right packages. Retrofit adapters check for specific classes. One problem I had was having the wrong imports and had situations where I was checking if the Throwable
was an instance of com.jakewharton.retrofit2.adapter.HttpException
, while it was actually an instance of retrofit2.adapter.rxjava2.HttpException
.
Hope this helps
Solution 2:
Updated anwer of Fred to kotlin
style and
rxjava1:
com.squareup.retrofit2:adapter-rxjava:2.5.0
with io.reactivex:rxjava:1.3.8
RxErrorHandlingCallAdapterFactory.kt
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.HttpException
import retrofit2.Retrofit
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
import rx.Observable
import java.io.IOException
import java.lang.reflect.Type
/**
* Created by Nikolay Unuchek on 28.11.2016.
*/internalclassRxErrorHandlingCallAdapterFactoryprivateconstructor() : CallAdapter.Factory() {
privateval original: RxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create()
overridefunget(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
return RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit) ?: returnnull)
}
privateclassRxCallAdapterWrapper<R>(
privateval retrofit: Retrofit,
privateval wrapped: CallAdapter<R, *>
) : CallAdapter<R, Any> {
overridefunresponseType(): Type {
return wrapped.responseType()
}
overridefunadapt(call: Call<R>): Any {
val result = wrapped.adapt(call)
if (result is Observable<*>) {
return result.onErrorResumeNext { throwable -> Observable.error(asRetrofitException(throwable as Throwable)) }
}
return result
}
privatefunasRetrofitException(throwable: Throwable): RetrofitException {
// We had non-200 http errorif (throwable is HttpException) {
val response = throwable.response()
return RetrofitException.httpError(response.raw().request.url.toString(), response, throwable)
}
// A network error happenedreturnif (throwable is IOException) {
RetrofitException.networkError(throwable)
} else RetrofitException.unexpectedError(throwable)
// We don't know what happened. We need to simply convert to an unknown error
}
}
companionobject {
funcreate(): CallAdapter.Factory {
return RxErrorHandlingCallAdapterFactory()
}
}
}
RetrofitException.kt
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException
classRetrofitExceptionprivateconstructor(
message: String?,
/**
* The request URL which produced the error.
*/val url: String?,
/**
* Response object containing status code, headers, body, etc.
*/val response: Response<*>?,
/**
* The event kind which triggered this error.
*/val kind: Kind,
exception: Throwable
) : RuntimeException(message, exception) {
overridefuntoString(): String {
returnsuper.toString() + " : " + kind + " : " + url + " : " + response?.errorBody()?.string()
}
/**
* Identifies the event kind which triggered a [RetrofitException].
*/enumclassKind{
/**
* An [IOException] occurred while communicating to the server.
*/
NETWORK,
/**
* A non-200 HTTP status code was received from the server.
*/
HTTP,
/**
* An internal error occurred while attempting to execute a request. It is best practice to
* re-throw this exception so your application crashes.
*/
UNEXPECTED
}
companionobject {
funhttpError(url: String, response: Response<*>, httpException: HttpException): RetrofitException {
val message = response.code().toString() + " " + response.message()
return RetrofitException(message, url, response, Kind.HTTP, httpException)
}
funnetworkError(exception: IOException): RetrofitException {
return RetrofitException(exception.message, null, null, Kind.NETWORK, exception)
}
fununexpectedError(exception: Throwable): RetrofitException {
return RetrofitException(exception.message, null, null, Kind.UNEXPECTED, exception)
}
funasRetrofitException(throwable: Throwable): RetrofitException {
if (throwable is RetrofitException) {
return throwable
}
// We had non-200 http errorif (throwable is HttpException) {
val response = throwable.response()
return httpError(response.raw().request().url().toString(), response, throwable)
}
// A network error happenedreturnif (throwable is IOException) {
networkError(throwable)
} else unexpectedError(throwable)
// We don't know what happened. We need to simply convert to an unknown error
}
}
}
rxjava2:
com.squareup.retrofit2:adapter-rxjava2:2.6.0
with io.reactivex.rxjava2:rxjava:2.2.9
RxErrorHandlingCallAdapterFactory.kt
importby.gramophone.api.errorhandling.RetrofitException.Companion.asRetrofitException
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.functions.Function
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import java.lang.reflect.Type
/**
* Created by Nikolay Unuchek on 28.11.2016.
*/internalclassRxErrorHandlingCallAdapterFactoryprivateconstructor() : CallAdapter.Factory() {
privateval original = RxJava2CallAdapterFactory.create()
overridefunget(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
return RxCallAdapterWrapper(original.get(returnType, annotations, retrofit) ?: returnnull)
}
privateclassRxCallAdapterWrapper<R>(privateval wrapped: CallAdapter<R, *>) : CallAdapter<R, Any> {
overridefunresponseType(): Type {
return wrapped.responseType()
}
overridefunadapt(call: Call<R>): Any {
returnwhen (val result = wrapped.adapt(call)) {
is Single<*> -> result.onErrorResumeNext(Function { throwable -> Single.error(asRetrofitException(throwable)) })
is Observable<*> -> result.onErrorResumeNext(Function { throwable -> Observable.error(asRetrofitException(throwable)) })
is Completable -> result.onErrorResumeNext (Function{ throwable -> Completable.error(asRetrofitException(throwable)) } )
else -> result
}
}
}
companionobject {
funcreate(): CallAdapter.Factory {
return RxErrorHandlingCallAdapterFactory()
}
}
}
RetrofitException.kt
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException
classRetrofitExceptionprivateconstructor(
message: String?,
/**
* The request URL which produced the error.
*/val url: String?,
/**
* Response object containing status code, headers, body, etc.
*/val response: Response<*>?,
/**
* The event kind which triggered this error.
*/val kind: Kind,
exception: Throwable
) : RuntimeException(message, exception) {
overridefuntoString(): String {
returnsuper.toString() + " : " + kind + " : " + url + " : " + response?.errorBody()?.string()
}
/**
* Identifies the event kind which triggered a [RetrofitException].
*/enumclassKind{
/**
* An [IOException] occurred while communicating to the server.
*/
NETWORK,
/**
* A non-200 HTTP status code was received from the server.
*/
HTTP,
/**
* An internal error occurred while attempting to execute a request. It is best practice to
* re-throw this exception so your application crashes.
*/
UNEXPECTED
}
companionobject {
funhttpError(url: String, response: Response<*>, httpException: HttpException): RetrofitException {
val message = response.code().toString() + " " + response.message()
return RetrofitException(message, url, response, Kind.HTTP, httpException)
}
funnetworkError(exception: IOException): RetrofitException {
return RetrofitException(exception.message, null, null, Kind.NETWORK, exception)
}
fununexpectedError(exception: Throwable): RetrofitException {
return RetrofitException(exception.message, null, null, Kind.UNEXPECTED, exception)
}
funasRetrofitException(throwable: Throwable): RetrofitException {
if (throwable is RetrofitException) {
return throwable
}
// We had non-200 http errorif (throwable is HttpException) {
val response = throwable.response()
return httpError(response.raw().request().url().toString(), response, throwable)
}
// A network error happenedreturnif (throwable is IOException) {
networkError(throwable)
} else unexpectedError(throwable)
// We don't know what happened. We need to simply convert to an unknown error
}
}
}
Post a Comment for "How To Make Rxerrorhandlingcalladapterfactory?"