suspend fun <T> loadHttp(
// 默认实现{},即参数可空
start: () -> Unit = {},
// suspend 修饰的函数,返回值ResultData<T>,没有默认实现,即参数不可空
// 函数的参数为T,没有默认实现,即参数不可空
end: () -> Unit = {},
request: suspend () -> BaseResponse<T>
): Result<T> {
// 在主线程(Dispatchers.Main)执行
try {
// 1.函数开始执行,先调用start()方法,给View做准备工作,如:显示loading
start()
// 2.发起网络请求
val data = withTimeout(10*000) {//使用的时候自行设置超时时间
request()
}
if (data.errorCode == 0) {
// 3.请求成功,返回响应
if (data.data == null) {
return Result.success("" as T)
}
return Result.success(data.data)
} else {
return Result.failure(ApiException(data.errorCode, data.errorMsg))
}
} catch (e: Throwable) {
// 可根据具体异常显示具体错误提示
return Result.failure(e)
} finally {
end()
}
}
BaseResponse//根据后台返回 适当修改。
data class BaseResponse<out T>(val errorMsg: String, val errorCode: Int, val data: T) : Serializable
RetrofitBuilder//拦截器请根据需求增加或者变动
object RetrofitBuilder {
val cookieJar = PersistentCookieJar(
SetCookieCache(),
SharedPrefsCookiePersistor(MyApp.getApplication().baseContext)
)
private val cacheFile = File(MyApp.getApplication().cacheDir, "mycache")
private val cache = Cache(cacheFile, 1024 * 1024 * 50)// 50M 的缓存大小
private val okHttpClient: OkHttpClient
get() {
// create http client
val httpClient = OkHttpClient.Builder()
.cookieJar(cookieJar)
// .addInterceptor(ReceivedCookiesInterceptor(MyApp.getApplication()))
// .addInterceptor(AddCookiesInterceptor(MyApp.getApplication()))
.addInterceptor(Interceptor { chain ->
val original = chain.request()
//header
val request =
original.newBuilder().method(original.method, original.body).build()
return@Interceptor chain.proceed(request)
}).readTimeout(30, TimeUnit.SECONDS)
httpClient.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
httpClient.run {
cache(cache)
connectTimeout(5, TimeUnit.SECONDS)
readTimeout(5, TimeUnit.SECONDS)
writeTimeout(5, TimeUnit.SECONDS)
retryOnConnectionFailure(true)//错误重连
}
return httpClient.build()
}
private fun getRetrofit(BASEURL: String): Retrofit {
return Retrofit.Builder().client(okHttpClient).baseUrl(BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(FlowCallAdapterFactory.create()) //kotlin flow 支持
.build() //Doesn't require the adapter
}
fun <T> createService(clazz: Class<T>, baseUrl: String): T =
getRetrofit(baseUrl).create(clazz)
inline fun <reified T> create(baseUrl: String): T = createService(T::class.java, baseUrl)
fun clearCookie(){
cookieJar.clear()
}
}
错误统一处理:
open class ApiException : Exception {
var errCode: Int
var errMsg: String
constructor(error: ERROR, e: Throwable? = null) : super(e) {
errCode = error.code
errMsg = error.errMsg
}
constructor(code: Int, msg: String, e: Throwable? = null) : super(e) {
this.errCode = code
this.errMsg = msg
}
}
/**
* 无网络连接异常
*/
class NoNetWorkException : IOException {
var errCode: Int
var errMsg: String
constructor(error: ERROR, e: Throwable? = null) : super(e) {
errCode = error.code
errMsg = error.errMsg
}
}
enum class ERROR(val code: Int, val errMsg: String) {
/**
* 对应HTTP的状态码
*/
/**
* 当前请求需要用户验证
*/
UNAUTHORIZED(401, "当前请求需要用户验证"),
/**
* 资源不可用。服务器理解客户的请求,但拒绝处理它
*/
FORBIDDEN(403, "资源不可用"),
/**
* 无法找到指定位置的资源
*/
NOT_FOUND(404, "无法找到指定位置的资源"),
/**
* 在服务器许可的等待时间内,客户一直没有发出任何请求
*/
REQUEST_TIMEOUT(408, "请求超时"),
/**
* 服务器遇到了意料不到的情况,不能完成客户的请求
*/
INTERNAL_SERVER_ERROR(500, "服务器错误"),
/**
* 服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答
*/
BAD_GATEWAY(502, "非法应答"),
/**
* 服务器由于维护或者负载过重未能应答
*/
SERVICE_UNAVAILABLE(503, "服务器未能应答"),
/**
* 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答
*/
GATEWAY_TIMEOUT(504, "服务器未能应答"),
/**
* 未知错误
*/
UNKNOWN(1000, "未知错误"),
/**
* 解析错误
*/
PARSE_ERROR(1001, "解析错误"),
/**
* 网络错误
*/
NETWORD_ERROR(1002, "网络异常,请尝试刷新"),
/**
* 协议出错
*/
HTTP_ERROR(1003, "404 Not Found"),
/**
* 证书出错
*/
SSL_ERROR(1004, "证书出错"),
/**
* 连接超时
*/
TIMEOUT_ERROR(1006, "连接超时"),
/**
* 未登录
*/
UNLOGIN(-1001, "未登录"),
/**
* 未知Host
*/
UNKNOW_HOST(1007, "未知Host");
}
/**
* 统一错误处理工具类
*/
object ExceptionHandler {
fun handleException(e: Throwable): ApiException {
val ex: ApiException
if (e is ApiException) {
ex = ApiException(e.errCode, e.errMsg, e)
if (ex.errCode == ERROR.UNLOGIN.code){
//登录失效
}
} else if (e is NoNetWorkException) {
TipsToast.showTips("网络异常,请尝试刷新")
ex = ApiException(ERROR.NETWORD_ERROR, e)
} else if (e is HttpException) {
ex = when (e.code()) {
ERROR.UNAUTHORIZED.code -> ApiException(ERROR.UNAUTHORIZED, e)
ERROR.FORBIDDEN.code -> ApiException(ERROR.FORBIDDEN, e)
ERROR.NOT_FOUND.code -> ApiException(ERROR.NOT_FOUND, e)
ERROR.REQUEST_TIMEOUT.code -> ApiException(ERROR.REQUEST_TIMEOUT, e)
ERROR.GATEWAY_TIMEOUT.code -> ApiException(ERROR.GATEWAY_TIMEOUT, e)
ERROR.INTERNAL_SERVER_ERROR.code -> ApiException(ERROR.INTERNAL_SERVER_ERROR, e)
ERROR.BAD_GATEWAY.code -> ApiException(ERROR.BAD_GATEWAY, e)
ERROR.SERVICE_UNAVAILABLE.code -> ApiException(ERROR.SERVICE_UNAVAILABLE, e)
else -> ApiException(e.code(), e.message(), e)
}
} else if (e is JsonParseException
|| e is JSONException
|| e is ParseException
|| e is MalformedJsonException
) {
ex = ApiException(ERROR.PARSE_ERROR, e)
} else if (e is ConnectException) {
ex = ApiException(ERROR.NETWORD_ERROR, e)
} else if (e is javax.net.ssl.SSLException) {
ex = ApiException(ERROR.SSL_ERROR, e)
} else if (e is java.net.SocketException) {
ex = ApiException(ERROR.TIMEOUT_ERROR, e)
} else if (e is java.net.SocketTimeoutException) {
ex = ApiException(ERROR.TIMEOUT_ERROR, e)
} else if (e is java.net.UnknownHostException) {
ex = ApiException(ERROR.UNKNOW_HOST, e)
} else {
ex = if (!e.message.isNullOrEmpty()) ApiException(1000, e.message!!, e)
else ApiException(ERROR.UNKNOWN, e)
}
return ex
}
}
以上直接复制过去就能用,非常好用。
下面代码是调用。一行代码搞定,很愉快就调用了,而且还好理解,方便接手的人维护。不像有些博主写的框架,调用一层套一层,搞到后面接手的人很懵逼。
lifecycleScope.launch {
loadHttp(start = {
LoadingView.getInstance(this@SplashActivity).showPopupWindow()
},
request = {
RetrofitBuilder.create<Api>(Api.BASE_URL).getBannerJson()
},
end = {
LoadingView.getInstance(this@SplashActivity).dismiss()
}).fold({
Loge.e(it.toString())
}, {
val exception = ExceptionHandler.handleException(it)
Loge.e(exception.errMsg + ":" + exception.errCode)
})
}
或者这样直接调用: 一行简洁代码搞定。
loadHttp{
RetrofitBuilder.create<Api>(Api.BASE_URL).getBannerJson()
}.onSuccess {
//请求成功处理
banner.value = it
}.onFailure {
//请求失败处理
val exception = ExceptionHandler.handleException(it)
Loge.e(exception.errMsg + ":" + exception.errCode)
}
最后做个补充,直接取消协程就会自动取消网络请求了。
val job = lifecycleScope.launch {
loadHttp {
RetrofitBuilder.create<Api>(Api.BASE_URL).getBannerJson()
}
}
job.cancel()//协程取消网络请求直接会取消、
或者使用 lifecycleScope.cancel()
当然,也可以直接取消lifecycleScope 来统一取消所有的网络请求,其他的viewModelScope等 同理
viewModelScope.cancel()
使用kotlin 谷歌内置了很多有用的高阶函数可以让代码更加简洁,
suspend fun <T> loadHttp2( //可以通过取消协程的方法来取消网络请求。比如使用lifecycleScope请求网络,当lifecycleScope取消的时候,网络请求同时取消了
timeout: Long = 10000,
request: suspend () -> BaseResponse<T>
): Result<T> {
// 在主线程(Dispatchers.Main)执行
return kotlin.runCatching {
var data: T = "" as T
val response = withTimeout(timeout) {
request()
}
if (response.errorCode == 0) {
if (response.data == null) data = "" as T else data = response.data
}
data
}
}
调用loadhttp2
job = lifecycleScope.launch {
loadHttp2 {
RetrofitFactory.instance.service.getBannerJson()
}.onSuccess {
Loge.e(it.toString())
}.onFailure {
val apiException = ExceptionHandler.handleException(it)
Loge.e(apiException.errMsg + ":" + apiException.errCode)
}
}
job.cancel //(取消网络请求)
分享几个好用缓存处理的拦截器。
//网络状态拦截
build.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
if (NetworkUtil.isConnected(SumAppHelper.getApplication())) {
val request = chain.request()
return chain.proceed(request)
} else {
throw NoNetWorkException(ERROR.NETWORD_ERROR)
}
}
})
// build.addNetworkInterceptor { chain ->
//
// val request = chain.request();
// val response = chain.proceed(request);
//
// var cacheControl = request.cacheControl().toString();
// if (TextUtils.isEmpty(cacheControl)) {
// cacheControl = "public, max-age=60";
// }
// return@addNetworkInterceptor response.newBuilder()
// .header("Cache-Control", cacheControl)
// .removeHeader("Pragma")
// .build();
//
//
// }
build.addInterceptor { chain ->
var request = chain.request();
if (!NetworkUtil.isConnected(SumAppHelper.getApplication())) {
val offlineCacheTime = 6000;//离线的时候的缓存的过期时间
request = request.newBuilder()
// .cacheControl(new CacheControl
// .Builder()
// .maxStale(60,TimeUnit.SECONDS)
// .onlyIfCached()
// .build()
// ) 两种方式结果是一样的,写法不同
.header(
"Cache-Control",
"public, only-if-cached, max-stale=" + offlineCacheTime
)
.build();
}
return@addInterceptor chain.proceed(request);
}
build.addNetworkInterceptor { chain ->
val request = chain.request();
val response = chain.proceed(request);
val onlineCacheTime = 60;//在线的时候的缓存过期时间,如果想要不缓存,直接时间设置为0
return@addNetworkInterceptor response.newBuilder()
.header("Cache-Control", "public, max-age=" + onlineCacheTime)
.removeHeader("Pragma")
.build()
}