先看看最後使用效果。java
ApiManager.apiService
.update("1.0")
.composeDefault()//統一處理異常,請求後臺異常throw ApiException ,異常信息爲後臺給的異常內容
.subscribeExtApi({
//成功返回
toastInfo(it.toString())
}, { e ->
toastInfo("更新失敗")
}, {
//請求完成
},isToast = true
)
複製代碼
一、先封裝retrofit,api
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
}
}
複製代碼
二、interface 接口bash
interface ApiService {
//檢查版本更新
@GET("http://xxxx/checkForceVersion")
fun update(@Query("user_version") version: String): Observable<BaseBean<String>>
}
複製代碼
三、初始化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)
}
}
複製代碼
四、封裝通用的失敗處理。app
請求返回是BaseBean,code爲12000是成功,其他則是是失敗,拋出異常ApiException。ui
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
}
複製代碼
五、處理成功失敗回調。this
這裏使用 可本身添加擴展,這裏只添加了是否toast失敗信息。
url
/**
* @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()
}
複製代碼