传统的网络央求写法

val builder = OkHttpClient.Builder()
builder.connectTimeout(CONNECTappearance_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUni接口测验面试题t.SECONDS)
.writeThttp 302imeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.cookieJar(...)
.addInterceptor(接口和抽象类的差异...)
val retrofit = retrofit.Builder()
.baseUrl(baseUrl)
.client(b接口测验的流程和过程uilder.build())
.add缓存视频怎样转入本地视频CallAdapterFactory(callAdapterFactory)
.addConverterFactory(converterFactory)
.build()
...
...接口是什么
diapproachalog.show()
HttpClientManager.getInstancOKHttpe()
.getHttpClient()
.createRetrofitApi(
URLappearance.ROOT_URL,
HttpApi::class.java).login("phone","phoneCode").enqueue(Mhttp://192.168.1.1登录yCallback() {
override fun success()
override fun onfailed()
})

高雅完成


// 包含dialog,央求失利一致处接口协议接口协议,一次发送单个、多个央求,看起来是不是很酣畅! 
http&l接口测验t;LoginBeokhttp3下载an> {
request { model.login("phone","phoneCode").waitT() }
success { codeSuccess.postValue(this) }
}

让我okhttp面试们初步吧 ↓↓↓

0. 构建HttpClient


class HttpClient()http://192.168.1.1登录 {
privat接口卡e object Client{
val builder = HttpClient()
}
companion object {
fun getInstance() = Client.builder
}
/**
* 缓存retrofit针对同一个域app安装下载名下相同的Apiokhttp面试回答Service不会重复创立retrofit方针
*/
privaokhttp优点te val apiMap by lazy {
ArrayMap<String, Any>()
}
private val API_KEY = "apiKey"
private var interceptors = arrayListOf<Interceptor>()
private var converterFactorys = arrayListOf<Converter.Factor缓存视频在手机哪里找y>()缓存视频在手机哪里找
/**
* 拦截器
*/
fuHTTPn sokhttp3源码剖析etInterceptors(list: MutableList<Interceptor>?) : HttpClient {
interceptors.clear()
if (!list.isNullOrEmpty()) {
interceptors.addAl缓存清理l(list)
}
return this
}
/**
* 解接口的效果析器
*/
fun setConverterFactorys(list: MutableList<Converter.Factory>?) : H缓存文件在哪里ttpClient {
converterFactorys.clear()
// 确保有一个默许的解析器
converterFa缓存ctorys.add(GsonConverterFactory.create())
if (!list.isNullOrEmpty()) {
converterFactorys.addAll(list)
}
return this接口测验
}
/**
* 根据 apiClass 与 baseUrl 创approach建 不同的Api
* @param baseUrl String            根目录
* @param clazz Class<T>            详细的api
* @param needAddHeader Boolean     是否需求添加公共的头
* @param showLog Boolean           是否需求闪现log
*/
fun <T> createRetrofitApi(
baseUrl: String = URL.ROOT_URL,
clazz: Class<T> = HttpApi::class.java,
needAddHeader: Boolean = true,
showLog: Boolean = true
): T {
val key = getApiKey(baseUrl, clazz)
val api = apiMap[key] as T
if (api == null) {
L.e(API_KEY, "RetrofitApi --->>> "$key"不存在,需求创立新的")
val builder = OkHttpClient.Builder()
builder
.connectTimeout(Content.HTTP_TIME, TimeUnit.SECONDS)
.readTimeout(Conteokhttp面试nt.HTTP_TIME, TimeUnihttp 404t.SECONDS)
if (needAddHeader) {
builder.addInterceptor(MyHttpInterceptor()) // 头部拦截器
}
if (interceptors.isNotEmpty()) {
interceptors.forEach {
builder.addInterceptor缓存视频怎样转入本地视频(it)
}
}
if (showLog) {
builder.addIntercappointmenteptor(LogInterceptor {
L.iokhttp3源码剖析(Content.HTappleTP_TAG, it)
}.apply {
level = LogInterceptor.Level.BODY
})
}
val rBuilder = Retrofit.Builder()
.baseUrl(baseUrl)
.client(builder.build())
if (converterFactorys.isEmpty()) {appstore // 确保有一个默许的解析器
con接口crc过错计数verterFactorys.add(GsonConverterFactory.creatokhttp3下载e())
}
converterFactorys.forEach {
rBuilder.addConverterFactory(it)
}
val newAapi = rBuilder.build().cokhttp源码解析reate(clazz)
apiMap[key] =接口测验 newAapi
return newAapi
}
return api
}
override fun <K&gthttp署理; geokhttp运用过程tApiKey(baseUrl: String, apiClass: Class<K>) =
"apiKey_${baseUrl}_${apiClass.name}"
/**
* 清空悉数拦截器
*/
funhttp://192.168.1.1登录 clearInterceptor() : H缓存视频在手机哪里找ttpClient {
interceptors.clear()
return this
}
/**
* 清空悉数解析器
*/
fun clearConverterFactory() : HttpClient {
converterFactorys.clear()
return this
}
/**
* 清空悉数api缓存
*/
fun clearAllApi() : HttpClient {
L.e(Conteappstorent.HTTP_TAG, "清空悉数api缓存")
apiMap.clear()
rehttp 302turn this
}
}
/**
*缓存视频在手机哪里找 HttpApi
*/
interface HttpApi {
/okhttp3源码剖析/ 不建议这样操作,因为每个项目的央求事务逻辑不完全相同,自己定制高度可控(具缓存视频体往下接口是什么看)
suspend fun sth(): CommonBean<String>
// 这种方法okhttp封装即使不用协程也可以运用,推荐此方法
fun lhttps和http的差异ogin(@Body body: RequestBody): Call<CommonBean<LoginBean>>
}
// 在 HttpApi中 编写 默许的获取 HttpApi的方法
val defaultApi: HttpApi
get() {
return HttpClient.getInstance().createRetrofitApi()
}

1. waitT 扩展函数


// 异常格式化
class ApiException : Exce缓存视频兼并app下载ption {
var msg: Striokhttp源码解析ng? = "网络不给力啊缓存视频兼并app下载,再试一次吧!"
var errorC缓存视频怎样转入本地视频ode = Content.REQUEST_SUCCESS_CODE
private constructor(applicationcode: Int, throwable: Throwable) : super(throwable) {
msg = throwable.message
errorCode = code
}
constructor(message: String?, code: Int = _500_SERVICE) : super(message) {
msg = message
errorCode = code
}
companiHTTPon object {
const val _500_SERVICE = 500
const val UNKNOW_ERROR = -1
fun formatEx缓存视频怎样转入本地视频ception(e: Throwable): ApiException {
val apiException: Apokhttp3下载iException
when (e) {
is HttpException ->okhttp面试 {
apiException = ApiException(e.code(), e)
apiException.msg = "网络不给力啊,再试okhttp运用过程一次吧!"
}
is SocketTimeoutEokhttp优点xception -> {
apiException = ApiEapplexception(_500_SERVICE, "网络不给力啊,再试缓存视频怎样转入本地视频一次吧!")
}
... 其他异常处理
is ApiException -> {
apiException = e
}
else -> {
apiException = ApiException(UNKNOW_ERROR, e)
apiException.okhttp封装msg = "不知道异常,请联appointment络管理员"
}
}
return apiException
}
}
/**
* 央求基类
*/
@Parcelize
class CommonBean<T>(
var success: Boolean = false,
var result: @http 302RawValue T? = null,
var error: ErrorBean? = nullokhttp面试回答,
var message: String? = "",
) : Parcelable, Serializable
/**
* 过错 回来实体类
* @property errorCode Int
* @property errorKapplicationey String?
* @property errorMessage String
* @constru缓存视频兼并app下载ctor
*/
@Parcelize
data class ErrorBean(
var errorCode: Int = -1,appstore
var errorKey: String? = "",
var errorMessage: String = ""
) :缓存视频怎样转入本地视频 Parcelable, Serializable
/okhttp面试/ 这种方法 根据自己项目的缓存视频变成本地视频需求定制,100% 自主可控
susappreciatepend fun <T> Call<CommonBean<T>>.waitT(): CommonBean<T> {
return suspendCoroutine {
enqueue(objehttpwatchct : Callback<CommonBean&lt缓存;T>>APP {
override fun onResponse(
call: Call<Commonokhttp运用过程Bean<T>>,okhttp运用
response: Rappearesponse<CommonBean<T>>
) {
val body = response.body()
if (body is ResponseBody) { // ResponseBody状APP
if (response.isSuccessful) {
it.resume(body)
} else {
it.resumeWithException(ApiException("网络不给力啊,再试一次吧"))
}
} else { // 其他实体类的状况
if (response.isSucceokhttp运用ssful) { // 央求成功
vahttp://www.baidu.coml isSuccess = body?.success ?: false // 事务逻辑OK
if (body !application= null && isSuccess) { // 事务逻http 404辑OK
it.resume(body)
} else { // 央求成功 但是事务接口逻辑是不对的
val errorBean = body?appearance.error
it.resumeWithException(ApiException(errorBean?.errorMessage))
}
} else { // 服务器抛出异常的状况接口测验,详细根据事务逻辑进行判定,如我这里有必要需求独自处理401的异常
if (response.code() == 401) {
// 服务接口类型器直接抛出 401异常,手动处理
it.resumeW接口是什么ithException(ApiException("当时账号在其他设备登录", 401))
} else {
it.resumeWitokhttp是干什么用的hException(ApiException("网络不给力啊,再试一次吧"))
}
}
}
}
override fun onFailure(call: Call<T_CommonBean<T>>, t: Throwable) {
t.printStackTrace()
L.e(Content.HTTP_TAG, "onFailure 接口异常okhttp是干什么用的 ---> ${call.request().url()}")
it.resumeWithExhttp协议ception(ApiException.formatExc缓存视频eption(t))
}
})
}
}

2. BaseViewokhttp运用Model


// 在 BaseAc缓存视频怎样转入本地视频tivityappointment 或 BaseFragment中 register之后 一致处理即可
class BaseViewModel : VieHTTPwModel() {
/**
* Dialhttp协议og 网络央求
*/
data class DialogRespBean(
var title: Stringappointment? = "加载中",
var isCancelable: Booleaokhttp源码解析n = t接口测验rue
)
/**
* 网络央求失利,响应到UI上的 Bean
* @property state Int              差异刷缓存视频怎样转入本地视频新(加载)或加载更多
* @property message String         过错描述
* @conappearstructor
*/
data class NetErrorRespBean(
var stahttp 500te: Int = Content.REFRESH,
var m缓存视频essage: Sthttpclientring? = "网络不给力啊,再试一次http署理吧"
)
/**
* 闪现dialog
*/
val showDialog by lazy {
MutableLiveDatapplea<DialogRespBean>()
}
/**接口crc过错计数
* 毁掉dialog
*/
val dismissDialog by lazy {
MutableLiveData<Void>()
}
/**
* 网络央求https和http的差异过错
*/
val networkError by lazy {
MutableLiveData<NetErrorRespBean>()
}
/**
* 中止悉数操作
*/
val stopAll by lazy {
MutableLiveData<Void&g缓存视频怎样转入本地视频t;()
}
/**
* 当时账号在其他设备登录
*/
val loginOut by lazy {
Mutabl缓存视频eLiveData<String?>()
}
/**
* Token 失效 或 登录时间已过期
*/
val tokenError by lazy {application
MutableLiveData<String?>()
}
// IO 往下看
open fun lunchByIO(
cont接口的效果ext: CoroutineContext = IO,
block: suspeapplicationnd CoroutineScope.() -> Unit
) =okhttp源码解析 viewModelScope.launch(context) { block() }
}

3. HttpExtend 与 DSL

准备工作

val IO: CoroutineContext
g缓存et() {
return Dispa缓存视频兼并app下载tchers.I缓存视频兼并O
}
val Main: CoroutineContext
get() {
return Dispatchers.Main
}
/**
* 切换到 IO 线程
*/
suspend fun IO(block: suspend CoroutineScope.() -> Unihttp协议t) {
withContext(IO) {
block()
}
}
/**
* 切换到 UI 线程
*/
suspend f缓存的视频怎么保存到本地un U缓存视频I(block: suspend CoroutineScope.() -> Unit) {
withContext接口测验的流程和过程(Mai缓存文件在哪里n) {
block()
}
}接口协议
@Parcelize
data class HttpLoader(
var state: Int = Content.REFRESH, // 用来差异是改写仍是加载更多接口的效果
var showDialog: Boolean = true, // 央求是否闪现 dialog
var autoDismissDialog: Boolean接口测验 = t接口卡rue, // 央求成缓存文件在哪里功后是否主动毁掉dialog
var dialogTitle: String = "加载中", // dialog title
var dialogCancel: Boolean = true // dhttps和http的差异ialog 是否缓存的视频怎么保存到本地可以取消
) : Parcelable
// 这里 是 单工作 MutableLiveData 的处理,这不是本文的重点,所以简略略过,可以直接 postValue
fun <T> MutableLiveData<T>.clear() {缓存文件在哪里
value = null
}

HttpExtend


internal fun <T> BaseViewModel.http(block: HttpExtend<T>.() -> Unit) {
val httpExtend = HttpExtend<T>(this)
block.invoke(httpExtend)
}
internal fun BaseViewModel.http2(block: HttpExtend<Nothing>.() -> Unit) {
http(block)
}
/** 一次发送一个 */
internal fun &lt接口和抽象类的差异;T> HttpEapplicationxtend<T>.requeapplicationst(startBlock: suspend CoroutineShttpclientcope.() -> CommonBean<T>?) {
start(startBlock)
}
/** 一次发送多个 *apple/
/** 不能直接缓存视频怎样转入本地视频更新UI,先切换到UI线程再操作UI  --->>> UI { } */
internal fun HttpExtenhttp://www.baidu.comd<Nothihttp 500ng>.reokhttp运用过程quest2(startBlock: suspend CoroutineScope.() -> Unit) {
starokhttp运用t2(startBlock)
}
internal fun <T> HttpExtend<T>.loadinghttp署理(loaderB缓存视频怎样转入相册lock: () -okhttp运用过程> HttpLoader) {
dialog(loaderBlock)
}
internal fun <T> HttpExtend<T>.success(resultBlock: T?.() -> U接口测验nit) {
callback(resultBlock)
}
internal fun <T> Http接口类型Extend<T>.failed(errorBlock: Exception?.() -> Unit) {
error(errorBlock)
}
internal fun <T> HttpExtend<T>.fihttp 302nally(finaly: (okhttp面试) -> Unit) {
end(finaly)
}
class HttpExtend<Tappstore>(var viewModel: BaseViewModel) {
priokhttp是干什么用的vate var httpLoader = HttpLoader()
// 央求缓存成功回调
privaappointmentte var httpCallBack: (T?.() -> Unit)?okhttp运用 = null
private varAPP httpError: (Exception?.() -> Unit)? = null
private var httpFinally: (() -&接口测验面试题gt; Unit)? = null
infix fun dialog(httpLoader: () -> HttpLoader) {
this.httpLoader = httpLoader()
}
private fun showDialog() {
if (httpLoader.showDialog) viewModel.showDialog.postValue(
DialogRespBean(
httpLoader.dialogTitle,
httpLoader.dialogCancel
)
)
}
// 一次央求一个
iappstorenfix fun start(startBlock: suspend CoroutineScope.() -> T_Coapplemmokhttp3下载onBean<T>?) {
showDialog()
viewModel.lhttp 404unchByIO {
thttp 500ry {
val request = startBloOKHttpck()
UI {
httpCallBack?.inokhttp封装voke(request?.result)
}
} catch (e: Exception) {
callError(e)
} finally {
callokhttp是干什么用的Finally()
}
}
}
// 一次OKHttp央求多个
infokhttp运用过程ix fun start2(startB接口卡lock: suspend CoroutineScope.() -> Unit) {
showDialog()
viewModel.lunchByIO {
try {
startBlock()
} catch (e: Exception) {
callError(e)apple
} finally {
callFinally()
}
}
}
// 央求 回调
infix fun callback(resultBlock: T?.() -> Unit) {
httpCallBack = resuokhttp3下载ltBlock
}
// 央求 失利 处理
infix fun error(errorB缓存视频兼并app下载lock: Exception?.() -> Unit) {
httpError = errorBlock
}
// 不论央求是否成功或失利,都会调用
infix fun end(end: () -> Unit) {
htt缓存视频兼并app下载pFinally = end
}
// 处理异常  当然你也可以运用密封类
private suspend fun ca缓存视频怎样转入本地视频llError(e: Exception)http://www.baidu.com {
e.printStackTrace()
UI {
// waiokhttp3源码剖析tT 扩展函数抛出的异常相关
val接口 apiException = ApiException.formatExceptio接口n(e)
// 详细根据事务逻辑而定
when (apiException.errorCode) {
401 -> {
L.e(Content.HTTP_TAG, "callError ---> Tokhttp源码解析oken失效 或 登录时间已过期")
viewModel.tokenError.postValue(apiException.msg)
}
888 -> {
// 当时账号在其他设备登录
L.e(Content.HTT缓存P_TAG, "callError ---> 当时账号在其他设备登录")
viewModel.loginOut.https和http的差异postValue(apiExcepti接口类型on.msg)
}
elsapplee -> { // 一般的服务器央求失利处理
L.e(Content.HTTP_TAG, "callError ---&gtapp安装下载; 央求失利")
vi缓存视频ewModel.networkError.postValue(
NetErrorRespBean(
httpLoader.state,
apiExc接口测验的流程和过程epti接口的效果on.msg
)
)
httpErr接口协议or?.invoke(apiException)
}
}
viewModel.dismissDialog.clear() // 出现溃散,不论怎么都将dialog毁掉
}
}
// 究竟实施
private suspend fun callFiappearnally() {
UI {
if (httpLoader.autoDismissDialog && httpLoaderokhttp3下载.showDial缓存视频在手机哪里找og) {
viewMookhttp面试del.dismissDialog.接口crc过错计数clear()
}
httpFihttp 500nally?.invoke()
}
}
}

4. 究竟调用


class LoginModel() {
fun login(phone:String,phoneC接口crc过错计数ode:String) = defaultApi.login(phone,phonehttpclientCode)
fuhttpwatchn registrId(id:String) = defaultApi.registrId(id)
fun getUserInfo(id:String) = defaultApi.getUserInfo(iAPPd)
}
classhttp协议 LoginViewModel : BaseViewModel() {
val loginSuccess by lazy {
MutablhttpclienteLiveData<UserInfoBean>()
}
private val model by l缓存视频azy {
LoginModel()
}
// 一次发送一个
fun login(phone:String,phoneCode:String) {
http<LoginBean> {
request { modelokhttp3下载.login("phone","phoneCode").waihttp署理tT() }
sucokhttp源码解析cess { loginSuccess.postValue(this) }
}
}
// 一次发送多个 
fun login2(phone:String,phoneCode:Stri缓存视频ng) {
http2 {
loading { HttpLoader(showDialog = false, autoDismissDialo接口测验面试题g = false) }
request2 {
val loginBean = model.login("phone","phoneCode").wai缓存视频兼并tT()
val registrBean = model.registrId(loginBean.id).waitT()
val userBhttp://www.baidu.comean = model.getUserInfo(registrBean.id).wa接口crc过错计数itT()
// 央求成功
UI {
loginSuccess.postValue(userBean)
dismissDialo接口是什么g.clear()
}
}
failed {
dismissDialog.clear()
}
finally {
// do sth
}
}
}
}

思路便是酱紫,最重要的是 waitT 以及 HttpExtend异常处理, 搞定!