Android 新人写的网络框架 有大神提点意见吗?

2018-08-23 10:27:34 +08:00
 hongch

用 kotlin 写的

retrofit+rxjava+okhttp+gson

错误统一处理,数据层剥离,mvvm,dto->vo

求大神给点优化意见 虽然写的不怎么样😁但是还是想求几个 star

github 地址 https://github.com/honglvt/NetWorkUtil

9518 次点击
所在节点    Android
6 条回复
hongch
2018-08-23 14:15:28 +08:00
谢谢各位大佬的 star
hongch
2018-08-23 14:58:56 +08:00
# 历时 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
kazeik
2018-08-24 09:24:12 +08:00
藕合性太强。没有自定义的 callback.
shangshicc
2018-08-27 17:55:40 +08:00
建议修改标题为自己对第三方框架的封装,表示刚看这个标题后以为你自己写了一个类似 okhttp 的库
hongch
2018-09-03 08:35:00 +08:00
- -
Fit7z
2018-09-03 15:09:25 +08:00
这只是常规操作,请坐下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/482410

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX