# 历时 2 天,完成了用 kotlin 写网络框架
## 1.错误统一处理</br>
## 2.和服务端约定 response 格式,剥离出 data</br>
## 3.MVVM</br>
## 4.DTO-VO 转换</br>
## 5.Activity 层几乎没有代码,极度简洁</br>
---
#先上效果图 </br>
1 )首先新建一个 VM 类,用于网络请求</br>
```
class MainVM {
fun getData(callback: DesCallBack<HCVO>) {
return ApiClient
.instance
.getApiService()
.test()
.compose(RxStreamHelper().io_Main())
.map {
it.transform()
}
.subscribe(Destiny(callback))
}
}
```
</br>
2 )利用 map 操作符将 DTO 转为业务所需的 VO </br>
```
.map {
it.transform()
}
```
3 )然后通过 callback 的方式将 data 暴露给 activity,具体业务场景不同,有时候可能需要 Observer 的 complete</br>
4 )最后调用 VM 的方法可以看到 View 层只有几行代码,极大程度降低了业务与逻辑的冗余度,</br>
```
fun request() {
val mainVM = MainVM()
mainVM.getData(object : DesCallBack<HCVO> {
override fun success(any: HCVO) {
Log.i("success",
any.name)
}
override fun failed(e: Throwable) {
}
})
}
```
</br>
如果用上 databinding Activity 只需要请求网络就可以了 是不是很方便?
---
# 具体实现</br>
## 一、Retrofit 的封装</br>
kotloin 对于 coder 来说,简化了大量代码,双重检查单例只需要一行代码</br>
```
val instance: ApiClientby lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){
ApiClient()
}
```
</br>
这样就保证了全局只有一个 retrofit 对象
</br>
接口层没什么变化,不过是 kotlin 的语法不一样
这样就完成了 retrofit 的封装
---
## 二、服务端返回的数据处理</br>
### 1 )首先定义一个基础类型,这里是服务端返回的格式,分别为 code,msg,和 data,在后期我们要将 data 单独剥离出来给具体的业务使用,code 和 msg 与业务无关
```
open class BaseModel<T> {
var code: Int? = null
var msg: String? = null
var data: T? = null
}
```
### 2 )错误处理,错误分为服务端错误和本地错误</br>
1.本地错误:在 onErrorResumeNext()的时候返回自定义的 exception,通过 Observable.error()发射出去</br>
具体的 CustomException 如图所示,判断 throwable 类型,自定义的 exception</br>
```
class CustomException {
companion object {
fun handleException(e: Throwable): ApiException {
if (e is HttpException) {
var ex = ApiException(e.code(), e.message(), e)
when (ex.code) {
UNAUTHORIZED -> ex.msg("未授权的请求")
FORBIDDEN -> ex.msg("禁止访问")
NOT_FOUND -> ex.msg("服务器地址未找到")
REQUEST_TIMEOUT -> ex.msg("请求超时")
GATEWAY_TIMEOUT -> ex.msg("网关响应超时")
INTERNAL_SERVER_ERROR -> {
ex.msg("服务器出错")
ex.msg("无效的请求")
}
BAD_GATEWAY -> ex.msg("无效的请求")
SERVICE_UNAVAILABLE -> ex.msg("服务器不可用")
ACCESS_DENIED -> ex.msg("网络错误")
HANDEL_ERROR -> ex.msg("接口处理失败")
else -> {
if (TextUtils.isEmpty(ex.msg)) {
ex.msg(e.message!!)
}
if (TextUtils.isEmpty(ex.msg) && e.getLocalizedMessage() != null) {
ex.msg(e.getLocalizedMessage())
}
if (TextUtils.isEmpty(ex.msg)) {
ex.msg("未知错误")
}
}
}
return ex
} else if (e is JSONException || e is ParseException) {
var ex = ApiException(PARSE_ERROR, e.message, e)
ex.msg("解析错误")
return ex
} else if (e is ConnectException) {
var ex = ApiException(NETWORK_ERROR, e.message, e)
ex.msg("连接失败")
return ex
} else if (e is javax.net.ssl.SSLHandshakeException) {
var ex = ApiException(SSL_ERROR, e.message, e)
ex.msg("证书验证失败")
return ex
} else if (e is java.security.cert.CertPathValidatorException) {
var ex = ApiException(SSL_NOT_FOUND, e.message, e)
ex.msg("证书路径没找到")
return ex
} else if (e is SSLPeerUnverifiedException) {
var ex = ApiException(SSL_NOT_FOUND, e.message, e)
ex.msg("无有效的 SSL 证书")
return ex
} else if (e is ConnectTimeoutException) {
var ex = ApiException(TIMEOUT_ERROR, e.message, e)
ex.msg("连接超时")
return ex
} else if (e is java.net.SocketTimeoutException) {
var ex = ApiException(TIMEOUT_ERROR, e.message, e)
ex.msg("连接超时")
return ex
} else if (e is java.lang.ClassCastException) {
var ex = ApiException(FORMAT_ERROR, e.message, e)
ex.msg("类型转换出错")
return ex
} else if (e is NullPointerException) {
var ex = ApiException(NULL, e.message, e)
ex.msg("数据有空")
return ex
} else if (e is FormatException) {
var ex = ApiException(-200, e.message, e)
ex.msg("服务端返回数据格式异常")
return ex
} else if (e is UnknownHostException) {
var ex = ApiException(NOT_FOUND, e.message, e)
ex.msg("服务器地址未找到,请检查网络或 Url")
return ex
} else {
var ex = ApiException(UNKNOWN, e.message, e)
ex.msg("未知异常")
return ex
}
}
}
}
```
### 3 )数据剥离,用到了 flatMap,此处解析服务端有关的错误,如果 code 等于 200,则代表接口请求成功,否则通过 Observable.error()抛出</br>
```
.flatMap { tBaseModel ->
if (tBaseModel.code == 200) {
Observable.just(tBaseModel.data!!)
} else Observable.error(ApiException(tBaseModel.code!!, tBaseModel.msg!!))
}
```
### 4 )线程调度,在子线程请求,在主线程处理</br>
```
upstream
. subscribeOn(
Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
```
通过 4 个操作符之后我们完成了以上任务,最后通过 compose 操作将这个操作集合合并为一个简化代码
github 地址:
https://github.com/honglvt/NetWorkUtil