Updated Fred's answer on kotlin style and
rxjava1:
com.squareup.retrofit2:adapter-rxjava:2.5.0 io.reactivex:rxjava:1.3.8 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 internal class RxErrorHandlingCallAdapterFactory private constructor() : CallAdapter.Factory() { private val original: RxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create() override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? { return RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit) ?: return null) } private class RxCallAdapterWrapper<R>( private val retrofit: Retrofit, private val wrapped: CallAdapter<R, *> ) : CallAdapter<R, Any> { override fun responseType(): Type { return wrapped.responseType() } override fun adapt(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 } private fun asRetrofitException(throwable: Throwable): RetrofitException {
RetrofitException.kt
import retrofit2.HttpException import retrofit2.Response import java.io.IOException class RetrofitException private constructor( message: String?, val url: String?, val response: Response<*>?, val kind: Kind, exception: Throwable ) : RuntimeException(message, exception) { override fun toString(): String { return super.toString() + " : " + kind + " : " + url + " : " + response?.errorBody()?.string() } enum class Kind { NETWORK, HTTP, UNEXPECTED } companion object { fun httpError(url: String, response: Response<*>, httpException: HttpException): RetrofitException { val message = response.code().toString() + " " + response.message() return RetrofitException(message, url, response, Kind.HTTP, httpException) } fun networkError(exception: IOException): RetrofitException { return RetrofitException(exception.message, null, null, Kind.NETWORK, exception) } fun unexpectedError(exception: Throwable): RetrofitException { return RetrofitException(exception.message, null, null, Kind.UNEXPECTED, exception) } fun asRetrofitException(throwable: Throwable): RetrofitException { if (throwable is RetrofitException) { return throwable }
rxjava2:
com.squareup.retrofit2:adapter-rxjava2:2.6.0 with io.reactivex.rxjava2:rxjava:2.2.9
RxErrorHandlingCallAdapterFactory.kt
import by.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. */ internal class RxErrorHandlingCallAdapterFactory private constructor() : CallAdapter.Factory() { private val original = RxJava2CallAdapterFactory.create() override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? { return RxCallAdapterWrapper(original.get(returnType, annotations, retrofit) ?: return null) } private class RxCallAdapterWrapper<R>(private val wrapped: CallAdapter<R, *>) : CallAdapter<R, Any> { override fun responseType(): Type { return wrapped.responseType() } override fun adapt(call: Call<R>): Any { return when (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 } } } companion object { fun create(): CallAdapter.Factory { return RxErrorHandlingCallAdapterFactory() } } }
RetrofitException.kt
import retrofit2.HttpException import retrofit2.Response import java.io.IOException class RetrofitException private constructor( message: String?, val url: String?, val response: Response<*>?, val kind: Kind, exception: Throwable ) : RuntimeException(message, exception) { override fun toString(): String { return super.toString() + " : " + kind + " : " + url + " : " + response?.errorBody()?.string() } enum class Kind { NETWORK, HTTP, UNEXPECTED } companion object { fun httpError(url: String, response: Response<*>, httpException: HttpException): RetrofitException { val message = response.code().toString() + " " + response.message() return RetrofitException(message, url, response, Kind.HTTP, httpException) } fun networkError(exception: IOException): RetrofitException { return RetrofitException(exception.message, null, null, Kind.NETWORK, exception) } fun unexpectedError(exception: Throwable): RetrofitException { return RetrofitException(exception.message, null, null, Kind.UNEXPECTED, exception) } fun asRetrofitException(throwable: Throwable): RetrofitException { if (throwable is RetrofitException) { return throwable }