Rx、kotlin、retrofit三者结合封装的网络请求库

源代码:RxKotlinRetrofitDemo


1.定义统一的返回结果类 ResultData

data class ResultData<T>(var code: Int, var message: String, var data: T?)

code:接口返回数据状态码(状态码与后台人员协商),0代表成功,其他值代表不同的错误类型

message:接口返回的信息,描述具体的接口返回状态--成功、失败信息

data:接口返回数据,不同接口返回不同的数据结构,使用泛型,可能为null

2.定义接口类 ApiService

interface ApiService {
    @POST("m/login")
    @FormUrlEncoded
    fun userLogin(@FieldMap options: Map<String, String>): Observable<ResultData<UserInfoVo>>
}

所有的网络请求接口都可以定义在该类中

3.接口类实现 

object Api {
    /**
     * 请求超时时间
     */
    private val DEFAULT_TIMEOUT = 5L

    private var SERVICE: ApiService? = null

    @JvmStatic
    val default: ApiService
        get() {
            if (SERVICE == null) {
                val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { message ->
                    //打印retrofit日志
                    Log.d("RetrofitLog", "retrofitBack = " + message)
                })
                loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

                //手动创建一个OkHttpClient并设置超时时间
                val client = OkHttpClient.Builder()
                        .addInterceptor(loggingInterceptor)
                        .addInterceptor(RequestInterceptor())
                        .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                        .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                        .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                        .build()

                SERVICE = Retrofit.Builder()
                        .client(client)
                        .addConverterFactory(GsonConverterFactory.create())
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                        .baseUrl(UriConsts.BASE_URL)
                        .build()
                        .create(ApiService::class.java)
            }
            return SERVICE!!
        }
}

4.Observable变换,统一处理返回结果

        使用lifecycleProvider,来取消网络请求,主要使用场景是在Activity、Fragment里进行网络请求时,应该在destroy的时候取消请求,防止短暂的内存泄露,所以Activity应该继承自RxAppCompatActivity,Fragment继承自RxFragment,具体关于lifecycleProvider的可以参考https://github.com/trello/RxLifecycle
        needCertification是针对需要认证的网络接口(例如:带token的接口请求),可以在接收到token失效的code信息做刷新token的操作,这里就没具体的实现,各位看官可以自己去实现自己的逻辑代码。

class HttpSimpleTransformer<T, E>(private var lifecycleProvider: LifecycleProvider<E>? = null,
                                  private var needCertification: Boolean = false,
                                  private var httpSimpleSubscriber: HttpSimpleSubscriber<T>? = null)
    : ObservableTransformer<ResultData<T>, T> {

    companion object {
        private val TAG = "HttpSimpleTransformer"
    }

    override fun apply(upstream: Observable<ResultData<T>>): ObservableSource<T> {
        if (lifecycleProvider is RxAppCompatActivity) {
            val rxAppCompatActivity = lifecycleProvider as RxAppCompatActivity
            upstream.compose(rxAppCompatActivity.bindUntilEvent(ActivityEvent.DESTROY))
        } else if (lifecycleProvider is RxFragment) {
            val rxFragment = lifecycleProvider as RxFragment
            upstream.compose(rxFragment.bindUntilEvent(FragmentEvent.DESTROY))
        }
        val observable = upstream
                .flatMap { tResultData -> flatResponse(tResultData) }
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe {
                    //不能破坏链式结构,否则不起作用
                    if (httpSimpleSubscriber is ProgressSubscriber) {
                        Log.d(TAG, "doOnSubscribe")
                        val progressSubscriber = httpSimpleSubscriber as ProgressSubscriber
                        progressSubscriber.showProgressDialog()
                    }
                }
        if (null != httpSimpleSubscriber) {
            observable.subscribe(httpSimpleSubscriber!!)
        }
        return observable
    }

    private fun flatResponse(response: ResultData<T>): Observable<T> {
        return Observable.create(ObservableOnSubscribe<T> { subscriber ->
            try {
                if (response.code == HttpStatus.SUCCESS) {//请求成功
                    if (null != response.data) {
                        subscriber.onNext(response.data!!)
                    } else {
                        subscriber.onError(NullDataException("返回数据为空"))
                    }
                } else {//请求失败
                    if (needCertification) {
                        //需要认证,需要认证的接口根据需要选择是否要做统一的额外逻辑处理
                        subscriber.onError(ApiException(response.code))
                    } else {
                        //这里抛出自定义的一个异常.可以处理服务器返回的错误.
                        subscriber.onError(ApiException(response.code))
                    }
                    return@ObservableOnSubscribe
                }
                //请求完成
                subscriber.onComplete()
            } catch (e: Exception) {
                Log.e(TAG, e.message)
                subscriber.onError(e)
            }
        })
    }
}

这里有两点需要注意的地方:

1、doOnSubscribe不能与前面的Observable链式调用结构分开,否则它里面的回调方法不会得到调用,这样带加载框的接口就不会显示加载框了

2、返回结果中data为null的情况不能直接调用subscriber.onNext(response.resultData),在Rxjava2.x里已经严格限定了不能传null(可以看源码),否则会保错,导致奔溃。这里采用自定义了一个空数据异常NullDataException,调用subscriber.onError(NullDataException("返回数据为空")),在自定义subscriber中再进行一轮处理,详解见subscriber里onError的处理

5.统一subscriber

abstract class HttpSimpleSubscriber<T> : Observer<T> {
    companion object {
        private val TAG = "HttpSimpleSubscriber"
    }

    override fun onSubscribe(d: Disposable) {

    }

    override fun onNext(t: T) {
        _onNext(t)
    }

    override fun onError(t: Throwable) {
        var errorCode = HttpStatus.ERROR
        var msg = "请求失败,请稍后再试..."
        if (t is NullDataException) {
            _onNext(null)
            return
        } else if (t is ApiException) {
            errorCode = t.errorCode
            msg = t.message ?: "请求失败,请稍后再试..."
        } else if (t is UnknownHostException) {
            errorCode = HttpStatus.SOCKET_TIMEOUT
            msg = "请检查网络"
        } else if (t is SocketTimeoutException) {
            errorCode = HttpStatus.SOCKET_TIMEOUT
            msg = "网络连接超时,请稍后再试..."
        } else if (t is ConnectException) {
            errorCode = HttpStatus.SOCKET_TIMEOUT
            msg = "网络连接失败,请稍后再试..."
        }
        Log.e(TAG, "errorCode:${errorCode} " + t)
        _onError(errorCode, msg)
    }

    override fun onComplete() {
        _onComplete()
    }

    abstract fun _onNext(retData: T?)

    open fun _onComplete() {

    }

    open fun _onError(errorCode: Int, msg: String) {
        Toast.makeText(MyApplication.context, msg, Toast.LENGTH_SHORT).show()
    }
}

        这里主要是对onError的处理,针对不同的错误类型,显示不同的错误信息给用户看。主要是由于服务器传递过来的错误信息直接给用户看的话,用户未必能够理解需要根据错误码对错误信息进行一个转换,在显示给用户。

        这里有个特殊的异常NullDataException,其实是接口请求成功了,只是data为null,所以在这里做了区分处理,调用的是_onNext(null)方法而不是_onError(errorCode, msg)方法。

        指定带加载框的网络请求这里封装了ProgressSubscriber,继承自HttpSimpleSubscriber,里面主要增加了显示、隐藏加载框的逻辑,详细看源代码。

6.Observable扩展方法

fun <T : Any, E> Observable<ResultData<T>>.compose1(lifecycleProvider: LifecycleProvider<E>? = null,
                                                    needCertification: Boolean = false,
                                                    httpSimpleSubscriber: HttpSimpleSubscriber<T>? = null): Observable<T> =
        compose(HttpSimpleTransformer(lifecycleProvider, needCertification, httpSimpleSubscriber))

通过该扩展方法可以讲Observable<ResultData<T>>转化为Observable<T>

    fun <E> userLogin(userName: String, password: String,
                      lifecycleProvider: LifecycleProvider<E>,
                      httpSimpleSubscriber: HttpSimpleSubscriber<UserInfoVo>? = null): Observable<UserInfoVo> {
        var requestParam = HashMap<String, String>()
        requestParam.put("username", userName)
        requestParam.put("password", MD5Util.MD5(password))
        return mApiService.userLogin(requestParam)
                .compose1(lifecycleProvider, httpSimpleSubscriber = httpSimpleSubscriber)
    }

源代码:RxKotlinRetrofitDemo

参考资料:
1.MVP实战心得(三)---封装Retrofit2.0+RxAndroid+RxBus http://www.jianshu.com/p/2a2464938b47
2.给 Android 开发者的 RxJava 详解 http://gank.io/post/560e15be2dca930e00da1083
4.Android RxJava+Retrofit完美封装 http://www.10tiao.com/html/227/201612/2650238160/1.html
5.Kotlin语言中文站 https://www.kotlincn.net/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值