RxHttp网络请求

Posted Wei_Leng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RxHttp网络请求相关的知识,希望对你有一定的参考价值。

RxHttp

项目地址:kongpf8848/RxHttp 

简介: 基于 RxJava2+Retrofit+OkHttp4.x 封装的网络请求类库,亮点多多,完美兼容 MVVM(ViewModel,LiveData),天生支持网络请求和生命周期绑定,天生支持多 BaseUrl,支持文件上传下载进度监听,支持断点下载,支持 Glide 和网络请求公用一个 OkHttpClient

更多:作者   提 Bug   

标签:

基于 RxJava2+Retrofit 2.9.0+OkHttp 4.9.0 实现的轻量级,完美兼容 MVVM 架构的网络请求封装类库,小巧精致,简单易用,网络请求原来如此简单:smirk::smirk::smirk:

亮点

  • 代码量极少,类库体积不足 100kb,但足以胜任大部分 APP 的网络请求任务,浓缩的都是精华啊^

  • 完美兼容 MVVM,MVC 架构,兼容 Kotlin 和 Java,Kotlin+MVVM+RxHttp 结合起来使用更酸爽,MVVM 官方推荐,抱紧 Google 大腿就对了

  • 完美解决泛型类型擦除的棘手问题,还原泛型的真实类型

  • 天生支持网络请求和 Activity,Fragment 生命周期绑定,界面销毁时自动取消网络请求回调

  • 天生支持多 BaseUrl,支持动态传入 Url

  • 支持自定义 OkHttpClient.Builder,可高度自定义网络请求参数,支持 Https 证书单向校验(客户端校验服务端证书)

  • 支持 Glide 等和 Http 请求公用一个 OkHttpClient,充分利用 OkHttpClient 的线程池和连接池,大部分情况下一个 App 一个 OkHttpClient 就够了

  • 支持 GET,POST,PUT,DELETE 等请求方式,支持文件上传及进度监听,支持同时上传多个文件,支持 Uri 上传

  • 支持文件下载及进度监听,支持大文件下载,支持断点下载

使用要求

项目基于 androidX,Java8+,minSdkVersion>=21

使用

  • 在项目根目录的 build.gradle 文件中添加:
    allprojects 
      repositories 
          mavenCentral()
      
    
    
  • 在具体 Module 的 build.gradle 文件中添加:
    implementation 'io.github.kongpf8848:RxHttp:1.0.12'
    

配置(可选)

  RxHttpConfig.getInstance()
    /**
     * 失败重试次数
     */
    .maxRetries(3)
    /**
     * 每次失败重试间隔时间
     */
    .retryDelayMillis(200)
    /**
     * Https 证书校验,单向校验,即客户端校验服务端证书,null 则为不校验
     */
     //.certificate(AssetUtils.openFile(applicationContext,"xxx.cer"))
    /**
     * 设置 OkHttpClient.Builder(),RxHttp 支持自定义 OkHttpClient.Builder()
     */
    .getBuilder().apply 
        connectTimeout(60, TimeUnit.SECONDS)
        readTimeout(60, TimeUnit.SECONDS)
        writeTimeout(60, TimeUnit.SECONDS)
        /**
         * DEBUG 模式下,添加日志拦截器,建议使用 RxHttp 中的 FixHttpLoggingInterceptor,使用 OkHttp 的 HttpLoggingInterceptor 在上传下载的时候会有 IOException 问题
         */
        if (BuildConfig.DEBUG) 
            addInterceptor(FixHttpLoggingInterceptor().apply 
                level = FixHttpLoggingInterceptor.Level.BODY
            )
        
    

基础使用

  • GET/POST/PUT/DELETE/上传请求
   RxHttp.getInstance()
    /**
     * get:请求类型,可为 get,post,put,delete,upload,分别对应 GET/POST/PUT/DELETE/上传请求
     * context:上下文,可为 Context,Activity 或 Fragment 类型,当 context 为 Activity 或 Fragment 时网络请求和生命周期绑定
     */
    .get(context)
    /**
     * 请求 url,如 https://www.baidu.com
     */
    .url("xxx")
    /**
     *请求参数键值对,类型为 Map<String, Any?>?,如 hashMapOf("name" to "jack")
     */
    .params(map)
    /**
     *每个网络请求对应的 tag 值,可为 null,用于后续手动根据 tag 取消指定网络请求
     */
    .tag("xxx")
    /**
     * HttpCallback:网络回调,参数 xxx 为返回数据对应的数据模型,
     * 类似 RxJava 中的 Observer,onComplete 只有在 onNext 回调之后执行,如发生错误则只会回调 onError 而不会执行 onComplete
     */
    .enqueue(object : HttpCallback<xxx>() 
        /**
         * http 请求开始时回调
         */
        override fun onStart() 

        

        /**
         * http 请求成功时回调
         */
        override fun onNext(response: xxx?) 

        

        /**
         * http 请求失败时回调
         */
        override fun onError(e: Throwable?) 

        

        /**
         * http 请求成功完成时回调
         */
        override fun onComplete() 

        

        /**
         * 上传进度回调,请求类型为 upload 时才会回调
         */
        override fun onProgress(readBytes: Long, totalBytes: Long) 

        
    )
  • 下载请求
   RxHttp.getInstance()
      /**
       * download:请求类型,下载请求
       * context:上下文,如不需要和生命周期绑定,应该传递 applicationContext
       */
      .download(context)
      /**
       * 保存路径
       */
      .dir(dir)
      /**
       *保存文件名称
       */
      .filename(filename)
      /**
       * 是否为断点下载,默认为 false
       */
      .breakpoint(true)
      /**
       * 下载地址,如 http://study.163.com/pub/ucmooc/ucmooc-android-official.apk
       */
      .url(url)
      /**
       * 请求 Tag
       */
      .tag(null)
      /**
       * 下载回调
       */
      .enqueue(object: DownloadCallback() 
          /**
           * 下载开始时回调
           */
          override fun onStart() 

          

          /**
           * 下载完成时回调
           */
          override fun onNext(response: DownloadInfo?) 

          

          /**
           * 下载失败时回调
           */
          override fun onError(e: Throwable?) 

          

          /**
           * 下载完成之后回调
           */
          override fun onComplete() 

          

          /**
           * 下载进度回调
           */
          override fun onProgress(readBytes: Long, totalBytes: Long) 

          

      )
  • 取消请求
    /**
     * tag:Any?,请求 Tag,对应网络请求里的 Tag 值
     * 如不为 null,则取消指定网络请求,
     * 如为 null,则取消所有网络请求
     */
    RxHttp.getInstance().cancelRequest(tag)

项目实战

此处假设服务端返回的数据格式为"code":xxx,"data":T,"msg":"",其中 code 为响应码,整型,等于 200 时为成功,其余为失败,data 对应的数据类型为泛型(boolean,int,double,String,对象 ,数组[ ]等类型)

 
    "code": 200,
    "data":T,
    "msg": ""

对应的 Response 类为

class TKResponse<T>(val code:Int,val msg: String?, val data: T?) : Serializable 
    companion object
        const val STATUS_OK=200
    
    fun isSuccess():Boolean
        return code== STATUS_OK
    

  • MVC 项目

    • 定义 MVCHttpCallback,用于将网络请求结果回调给 UI 界面
```kotlin

abstract class MVCHttpCallback 

    private val type: Type

    init 
        val arg = TypeUtil.getType(javaClass)
        type = TypeBuilder
                .newInstance(TKResponse::class.java)
                .addTypeParam(arg)
                .build()
    

    fun getType(): Type 
        return this.type
    

    /**
     * 请求开始时回调,可以在此加载 loading 对话框等,默认为空实现
     */
    open fun onStart() 

    /**
     * 抽象方法,请求成功回调,返回内容为泛型,对应 TKResponse 的 data
     */
    abstract fun onSuccess(result: T?)

    /**
     * 抽象方法,请求失败回调,返回内容为 code(错误码),msg(错误信息)
     */
    abstract fun onFailure(code: Int, msg: String?)

    /**
     * 上传进度回调,默认为空实现
     */
    open fun onProgress(readBytes: Long, totalBytes: Long) 

    /**
     * 请求完成时回调,请求成功之后才会回调此方法,默认为空实现
     */
    open fun onComplete() 


   * 定义网络接口,封装 GET/POST 等网络请求


   ```kotlin
   object MVCApi 

       /**
        * GET 请求
        * context:上下文
        * url:请求 url
        * params:参数列表,可为 null
        * tag:标识一个网络请求
        * callback:网络请求回调
        */
       inline fun <reified T> httpGet(
               context: Context,
               url: String,
               params: Map<String, Any?>?,
               tag: Any? = null,
               callback: MVCHttpCallback<T>
       ) 
           RxHttp.getInstance().get(context)
                   .url(url)
                   .params(params)
                   .tag(tag)
                   .enqueue(simpleHttpCallback(callback))
       

       /**
        * POST 请求
        * context:上下文
        * url:请求 url
        * params:参数列表,可为 null
        * tag:标识一个网络请求
        * callback:网络请求回调
        */
       inline fun <reified T> httpPost(
               context: Context,
               url: String,
               params: Map<String, Any?>?,
               tag: Any? = null,
               callback: MVCHttpCallback<T>
       ) 
           RxHttp.getInstance().post(context)
                   .url(url)
                   .params(params)
                   .tag(tag)
                   .enqueue(simpleHttpCallback(callback))
       

       ......

        inline fun <reified T> simpleHttpCallback(callback: MVCHttpCallback<T>): HttpCallback<TKResponse<T>> 
           return object : HttpCallback<TKResponse<T>>(callback.getType()) 
               override fun onStart() 
                   super.onStart()
                   callback.onStart()
               

               override fun onNext(response: TKResponse<T>?) 
                   if (response != null) 
                       if (response.isSuccess()) 
                           callback.onSuccess(response.data)
                        else 
                           return onError(ServerException(response.code, response.msg))
                       

                    else 
                       return onError(NullResponseException(TKErrorCode.ERRCODE_RESPONSE_NULL, TKErrorCode.ERRCODE_RESPONSE_NULL_DESC))
                   

               

               override fun onError(e: Throwable?) 
                   handleThrowable(e).run 
                       callback.onFailure(first, second)
                   
               

               override fun onComplete() 
                   super.onComplete()
                   callback.onComplete()
               

               override fun onProgress(readBytes: Long, totalBytes: Long) 
                   super.onProgress(readBytes, totalBytes)
                   callback.onProgress(readBytes, totalBytes)
               
           
       
 * 在 View 层如 Activity 中调用网络接口


 ```kotlin
 MVCApi.httpGet(
     context = baseActivity,
     url = TKURL.URL_GET,
     params = null,
     tag = null, 
     callback = object : MVCHttpCallback<List<Banner>>() 
     override fun onStart() 
         LogUtils.d(TAG, "onButtonGet onStart() called")
     

     override fun onSuccess(result: List<Banner>?) 
         Log.d(TAG, "onButtonGet onSuccess() called with: result = $result")
     

     override fun onFailure(code: Int, msg: String?) 
         Log.d(TAG, "onButtonGet onFailure() called with: code = $code, msg = $msg")
     

     override fun onComplete() 
         Log.d(TAG, "onButtonGet onComplete() called")
     

 )
 ```

 **具体使用可以参考 demo 代码,demo 中有详细的示例演示 MVC 项目如何使用 RxHttp**
  • MVVM 项目

    • 定义 Activity 基类 BaseMvvmActivity

      abstract class BaseMvvmActivity<VM : BaseViewModel, VDB : ViewDataBinding> : AppCompatActivity()
      
       lateinit var viewModel: VM
       lateinit var binding: VDB
      
       protected abstract fun getLayoutId(): Int
      
       final override fun onCreate(savedInstanceState: Bundle?) 
           onCreateStart(savedInstanceState)
           super.onCreate(savedInstanceState)
           binding = DataBindingUtil.setContentView(this, getLayoutId())
           binding.lifecycleOwner = this
           createViewModel()
           onCreateEnd(savedInstanceState)
       
      
       protected open fun onCreateStart(savedInstanceState: Bundle?) 
       protected open fun onCreateEnd(savedInstanceState: Bundle?) 
      
       /**
        * 创建 ViewModel
        */
       private fun createViewModel() 
           val type = findType(javaClass.genericSuperclass)
           val modelClass = if (type is ParameterizedType) 
               type.actualTypeArguments[0] as Class<VM>
            else 
               BaseViewModel::class.java as Class<VM>
           
           viewModel = ViewModelProvider(this).get(modelClass)
       
      
       private fun findType(type: Type): Type?
           return when(type)
               is ParameterizedType -> type
               is Class<*> ->
               findType(type.genericSuperclass)
               
               else ->
               null
               
           
       
      
      
      
    • 定义 ViewModel 的基类 BaseViewModel

      open class BaseViewModel(application: Application) : AndroidViewModel(application) 
      
       /**
        * 网络仓库
        */
       protected val networkbaseRepository: NetworkRepository = NetworkRepository.instance
      
       /**
        * 上下文
        */
       protected var context: Context = application.applicationContext
      
      
      
    • 定义网络仓库,封装网络接口 ```kotlin /**
    • MVVM 架构网络仓库
    • UI->ViewModel->Repository->LiveData(ViewModel)->UI */ class NetworkRepository private constructor()

      companion object

       val instance = NetworkRepository.holder
      

      private object NetworkRepository

       val holder = NetworkRepository()
      

      inline fun wrapHttpCallback(): MvvmHttpCallback 

       return object : MvvmHttpCallback<T>() 
      
       
      

      inline fun newCallback(liveData: MutableLiveData>): HttpCallback>

       val type = wrapHttpCallback<T>().getType()
       return object : HttpCallback<TKResponse<T>>(type) 
           override fun onStart() 
           liveData.value = TKState.start()
           
      
           override fun onNext(response: TKResponse<T>?) 
           liveData.value = TKState.response(response)
           
      
           override fun onError(e: Throwable?) 
           liveData.value = TKState.error(e)
           
      
           override fun onComplete() 
      
           /**
            * 亲,此处不要做任何操作,不要给 LiveData 赋值,防止 onNext 对应的 LiveData 数据被覆盖,
            * 在 TKState 类 handle 方法里会特别处理回调的,放心好了
            */
           
      
           override fun onProgress(readBytes: Long, totalBytes: Long) 
           liveData.value = TKState.progress(readBytes, totalBytes)
           
       
      

      inline fun httpGet(

       context: Context,
       url: String,
       params: Map<String, Any?>?,
       tag: Any? = null
      

      ): MutableLiveData>

       val liveData = MutableLiveData<TKState<T>>()
       RxHttp.getInstance()
           .get(context)
           .url(url)
           .params(params)
           .tag(tag)
           .enqueue(newCallback(liveData))
       return liveData
      

    inline fun <reified T> httpPost(
        context: Context,
        url: String,
        params: Map<String, Any?>?,
        tag: Any? = null
    ): MutableLiveData<TKState<T>> 
        val liveData = MutableLiveData<TKState<T>>()
        RxHttp.getInstance().post(context)
            .url(url)
            .params(params)
            .tag(tag)
            .enqueue(newCallback(liveData))
        return liveData
    

    inline fun <reified T> httpPostForm(
        context: Context,
        url: String,
        params: Map<String, Any?>?,
        tag: Any? = null
    ): MutableLiveData<TKState<T>> 
        val liveData = MutableLiveData<TKState<T>>()
        RxHttp.getInstance().postForm(context)
            .url(url)
            .params(params)
            .tag(tag)
            .enqueue(newCallback(liveData))
        return liveData
    

    inline fun <reified T> httpPut(
        context: Context,
        url: String,
        params: Map<String, Any?>?,
        tag: Any? = null
    ): MutableLiveData<TKState<T>> 
        val liveData = MutableLiveData<TKState<T>>()
        RxHttp.getInstance().put(context)
            .url(url)
            .params(params)
            .tag(tag)
            .enqueue(newCallback(liveData))
        return liveData
    

    inline fun <reified T> httpDelete(
        context: Context,
        url: String,
        params: Map<String, Any?>?,
        tag: Any? = null
    ): MutableLiveData<TKState<T>> 
        val liveData = MutableLiveData<TKState<T>>()
        RxHttp.getInstance().delete(context)
            .params(params)
            .url(url)
            .tag(tag)
            .enqueue(newCallback(liveData))
        return liveData
    


    /**
     *上传
     *支持上传多个文件,map 中对应的 value 类型为 File 类型或 Uri 类型
     *支持监听上传进度
        val map =Map<String,Any>()
        map.put("model", "xiaomi")
        map.put("os", "android")
        map.put("avatar",File("xxx"))
        map.put("video",uri)
     */
    inline fun <reified T> httpUpload(
        context: Context,
        url: String,
        params: Map<String, Any?>?,
        tag: Any? = null
    ): MutableLiveData<TKState<T>> 
        val liveData = MutableLiveData<TKState<T>>()
        RxHttp.getInstance().upload(context)
            .url(url)
            .params(params)
            .tag(tag)
            .enqueue(newCallback(liveData))
        return liveData
    


    /**
     * 下载
     * context:上下文,如不需要和生命周期绑定,应该传递 applicationContext
     * url:下载地址
     * dir:本地目录路径
     * filename:保存文件名称
     * callback:下载进度回调
     * md5:下载文件的 MD5 值
     * breakpoint:是否支持断点下载,默认为 true
     */
    fun httpDownload(context: Context, url: String, dir: String, filename: String, callback: DownloadCallback, md5: String? = null, breakPoint: Boolean = true, tag: Any? = null) 
        RxHttp.getInstance().download(context).dir(dir).filename(filename).breakpoint(breakPoint).md5(md5).url(url).tag(tag).enqueue(callback)
    


 ```
 * 定义 TKState 类,用于将网络回调转化为 LiveData

 ```kotlin
 /**
  *将 HttpCallback 回调转化为对应的 LiveData
 */
class TKState<T> 

    var state: Int = 0
    var code = TKErrorCode.ERRCODE_UNKNOWN
    var msg: String? = null
    var data: T? = null
    var progress: Long = 0
    var total: Long = 0

    @JvmOverloads
    constructor(state: Int, data: T? = null, msg: String? = "") 
        this.state = state
        this.data = data
        this.msg = msg
    

    constructor(state: Int, throwable: Throwable?) 
        this.state = state
        handleThrowable(throwable).run 
            this@TKState.code = first
            this@TKState.msg = second
        
    

    constructor(state: Int, progress: Long, total: Long) 
        this.state = state
        this.progress = progress
        this.total = total
    

    fun handle(handleCallback: HandleCallback<T>.() -> Unit) 
        val callback = HandleCallback<T>()
        callback.apply(handleCallback)
        when (state) 
            START -> 
            callback.onStart?.invoke()
            
            SUCCESS -> 
            callback.onSuccess?.invoke(data)
            
            FAIL -> 
            callback.onFailure?.invoke(code, msg)
            
            PROGRESS -> 
            callback.onProgress?.invoke(progress, total)
            
        
        if (state == SUCCESS || state == FAIL) 
            callback.onComplete?.invoke()
        
    

    open class HandleCallback<T> 
        var onStart: (() -> Unit)? = null
        var onSuccess: ((T?) -> Unit)? = null
        var onFailure: ((Int, String?) -> Unit)? = null
        var onComplete: (() -> Unit)? = null
        var onProgress: ((Long, Long) -> Unit)? = null

        fun onStart(callback: (() -> Unit)?) 
            this.onStart = callback
        

        fun onSuccess(callback: ((T?) -> Unit)?) 
             this.onSuccess = callback
        

        fun onFailure(callback: ((Int, String?) -> Unit)?) 
            this.onFailure = callback
        

        fun onComplete(callback: (() -> Unit)?) 
            this.onComplete = callback
        

        fun onProgress(callback: ((Long, Long) -> Unit)?) 
            this.onProgress = callback
        
    

    companion object 
        const val START = 0
        const val SUCCESS = 1
        const val FAIL = 2
        const val PROGRESS = 3

        fun <T> start(): TKState<T> 
         return TKState(START)
        

        fun <T> response(response: TKResponse<T>?): TKState<T> 
            if (response != null) 
            if (response.isSuccess()) 
                return TKState(SUCCESS, response.data, null)
             else 
                return error(ServerException(response.code, response.msg))
            

             else 
            return error(NullResponseException(TKErrorCode.ERRCODE_RESPONSE_NULL, TKErrorCode.ERRCODE_RESPONSE_NULL_DESC))
            

        

        fun <T> error(t: Throwable?): TKState<T> 
            return TKState(FAIL, t)
        

        fun <T> progress(progress: Long, total: Long): TKState<T> 
            return TKState(PROGRESS, progress, total)
        
    


 ```

 * 经过一系列封装,参考 demo 代码,最后在 View 层如 Activity 中 ViewModel 调用 Repository 中的接口
 ```kotlin
  viewModel.testPost(hashMapOf(
            "name" to "jack",
            "location" to "shanghai",
            "age" to 28)
    )
    .observeState(this) 
        onStart 
        LogUtils.d(TAG, "onButtonPost() onStart called")
        
        onSuccess 
        LogUtils.d(TAG, "onButtonPost() onSuccess called:$it")
        
        onFailure  code, msg ->
        ToastHelper.toast("onButtonPost() onFailure,code:$code,msg:$msg")
        
        onComplete 
        LogUtils.d(TAG, "onButtonPost() onComplete called")
        
    
 ```

 **具体使用可以参考 demo 代码,demo 中有详细的示例演示 MVVM 项目如何使用 RxHttp**

强烈建议下载 Demo 代码,Demo 中有详细的示例,演示 MVVM 及 MVC 架构如何使用 RxHttp,如有问题可私信我,简书 掘金

License

Copyright (C) 2021 kongpf8848

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

 

以上是关于RxHttp网络请求的主要内容,如果未能解决你的问题,请参考以下文章

为何这篇RxHttp Http请求框架会如此销魂,全文干货建议收藏!

RxHttp 让你眼前一亮的Http请求框架

RxHttp ,比Retrofit 更优雅的协程体验

可怕!RxHttp2.0重大更新!协程发请求,原来如此简单

RxHttp拦截器实现token过期处理

ivew Table 固定列设置后,底部拖拽的横轴被覆盖拉不动