先看看最后使用效果。
ApiManager.apiService
.update("1.0")
.composeDefault()//统一处理异常,请求后台异常throw ApiException ,异常信息为后台给的异常内容
.subscribeExtApi({
//成功返回
toastInfo(it.toString())
}, { e ->
toastInfo("更新失败")
}, {
//请求完成
},isToast = true
)
1、先封装retrofit,
object CommonConst {
const val REQUEST_OUTTIME = 10_000L
//超时时间 ms
const val DOWNLOAD_OUTTIME = 16_000L
const val UPLOAD_OUTTIME = 16_000L
//retrofit baseurl 必须以“/”结尾
var BASE_URL = "http://www.baidu.com/"
}
class RequestApiManager() {
private var retrofit: Retrofit? = null
private var client: OkHttpClient? = null
private object SingletonHolder {
val INSTANCE = RequestApiManager()
}
fun initRetrofit(clientBuilder: (builder: OkHttpClient.Builder) -> Unit = {}, retrofitBuilder: (builder: Retrofit.Builder) -> Unit = {}, baseUrl: String = CommonConst.BASE_URL) {
if (client == null) {
client = OkHttpClient.Builder().apply {
connectTimeout(CommonConst.REQUEST_OUTTIME, TimeUnit.MILLISECONDS)
if (BuildConfig.DEBUG) {
//添加日志
addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY })
}
clientBuilder(this);
}.build()
}
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
// .addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.apply {
retrofitBuilder(this);
}
.build()
}
}
//这里返回一个泛型类,主要返回的是定义的接口类
fun <T> createService(clazz: Class<T>): T {
if (retrofit == null) {
initRetrofit();
}
return retrofit!!.create(clazz)
}
companion object {
val instance: RequestApiManager
get() = SingletonHolder.INSTANCE
}
}
2、interface 接口
interface ApiService {
//检查版本更新
@GET("http://xxxx/checkForceVersion")
fun update(@Query("user_version") version: String): Observable<BaseBean<String>>
}
3、初始化retrofit
object ApiManager {
var apiService by NotNullSingle<ApiService>()
//application 初始化
fun initApiService() {
apiService = RequestApiManager.instance.apply {
initRetrofit({ clientBuilder ->
//添加拦截器
}, { retrofitBuilder ->
}, "http://www.test.com")
}.createService(ApiService::class.java)
}
}
4、封装通用的失败处理。
请求返回是BaseBean,code为12000是成功,其余则是是失败,抛出异常ApiException。
class BaseBean<T> : Serializable {
var code: Int = 0
var msg: String = ""
var data: T? = null
}
//请求数据返回失败
class ApiException constructor(var code: Int, msg: String?, result: String?) : RuntimeException(msg)
fun <T> Observable<T>.composeDefault(): Observable<T> =
subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io()).map { t ->
var msg = "请求异常"
when (t) {
is BaseBean<*> -> {
msg = t.msg
if (t.code != 12000) {
throw ApiException(t.code, msg, GsonUtil().toJson(t))
}
}
}
t
}
5、处理成功失败回调。
这里使用
可自己添加扩展,这里只添加了是否toast失败信息。
/**
* @param onNext 成功
* @param onError 失败 不传默认toast ApiException的异常信息,可以传onError 能toast自己的信息 例如:{toastInfo("添加失败")}
* @param onComplete 完成
* @param isToast 是否toast失败信息
*/
fun <T> Observable<T>.subscribeExtApi(onNext: (result: T) -> Unit,
onError: ((e: Throwable) -> Unit)? = null,
onComplete: (() -> Unit)? = null,
onSubscribe: ((disposable: Disposable) -> Unit)? = null,
isToast: Boolean = true): Disposable {
return LambdaObserver<T>(
{ result ->
onNext(result)
},
{ e ->
errorToast(e) {
onError?.invoke(e) ?: run {
if (isToast) toastErrorNet(e)
}
}
},
{
onComplete?.invoke()
//todo 完成取消进度框
},
{
onSubscribe?.invoke(it)
//todo 开始请求显示进度框
})
.apply {
subscribe(this)
}
}
private fun errorToast(e: Throwable, block: () -> Unit) {
if (e is ApiException) {
block()
} else if (e is ConnectException || e is UnknownHostException) {
toastInfo("网络未连接")
} else if (e is TimeoutException || e is SocketTimeoutException) {
toastInfo("网络超时")
} else if (e is JsonSyntaxException) {
toastInfo("数据解析异常")
} else {
Logger.d("onError#${e.message}")
if (BuildConfig.DEBUG) {
toastInfo("未知异常${e.message}")
} else {
toastInfo("请求异常")
}
}
e.printStackTrace()
}