V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hongch
V2EX  ›  Android

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

  •  
  •   hongch · 2018-08-23 10:27:34 +08:00 · 4761 次点击
    这是一个创建于 2066 天前的主题,其中的信息可能已经有所发展或是发生改变。

    用 kotlin 写的

    retrofit+rxjava+okhttp+gson

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

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

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

    6 条回复    2018-09-03 15:09:25 +08:00
    hongch
        1
    hongch  
    OP
       2018-08-23 14:15:28 +08:00
    谢谢各位大佬的 star
    hongch
        2
    hongch  
    OP
       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
        3
    kazeik  
       2018-08-24 09:24:12 +08:00
    藕合性太强。没有自定义的 callback.
    shangshicc
        4
    shangshicc  
       2018-08-27 17:55:40 +08:00
    建议修改标题为自己对第三方框架的封装,表示刚看这个标题后以为你自己写了一个类似 okhttp 的库
    hongch
        5
    hongch  
    OP
       2018-09-03 08:35:00 +08:00
    - -
    Fit7z
        6
    Fit7z  
       2018-09-03 15:09:25 +08:00
    这只是常规操作,请坐下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5247 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 05:53 · PVG 13:53 · LAX 22:53 · JFK 01:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.