RxHttp - 轻量级、可扩展、易使用、完美兼容MVVM、MVC架构的网络封装类库

南极冰川雪 2021-02-23 13:03:52
扩展 轻量级 量级 易使用 rxhttp


前言

RxHttp是基于RxJava2+Retrofit 2.9.0+OkHttp 4.9.0实现的轻量级,完美兼容MVVM架构的网络请求封装类库,小巧精致,简单易用,轻轻松松搞定网络请求。

GitHub

https://github.com/kongpf8848/RxHttp

亮点

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

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

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

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

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

  • 支持自定义OkHttpClient.Builder,可高度自定义网络请求参数

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

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

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

使用要求

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

使用

implementation 'com.github.kongpf8848:RxHttp:1.0.11'

配置(可选)

 RxHttpConfig.getInstance()
/**
* 失败重试次数
*/
.maxRetries(3)
/**
* 每次失败重试间隔时间
*/
.retryDelayMillis(200)
/**
* 自定义OkHttpClient.Builder(),RxHttp支持自定义OkHttpClient.Builder(),
* 如不定义,则使用RxHttp默认的OkHttpClient.Builder()
*/
.builder(OkHttpClient.Builder().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界面
    abstract class MVCHttpCallback<T> {
    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等网络请求
    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中调用网络接口
    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
    }
    
    • 定义网络仓库,封装网络接口
    /**
    * 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 <reified T> wrapHttpCallback(): MvvmHttpCallback<T> {
    return object : MvvmHttpCallback<T>() {
    }
    }
    inline fun <reified T> newCallback(liveData: MutableLiveData<TKState<T>>): HttpCallback<TKResponse<T>> {
    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 <reified T> httpGet(
    context: Context,
    url: String,
    params: Map<String, Any?>?,
    tag: Any? = null
    ): MutableLiveData<TKState<T>> {
    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
    /**
    *将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)
    }
    }
    }
    
    • 经过一系列封装,最后在View层如Activity中ViewModel调用Repository中的接口
     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,如果本文对你有帮助,可以考虑给我点赞哦

Demo

https://github.com/kongpf8848/RxHttp

版权声明
本文为[南极冰川雪]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/rainboy2010/p/14435411.html

  1. JS mechanism 3: stack, heap, garbage collection
  2. [grid compression evaluation] meshquan, meshopt, Draco
  3. Deep understanding of Vue modifier sync [Vue sync modifier example]
  4. WebView for front end engineers
  5. React form source code reading notes
  6. Deep thinking about modern package manager -- why do I recommend pnpm instead of NPM / yarn?
  7. On the sequence of event capture and event bubbling
  8. Help you build a systematic understanding of the front end scaffolding
  9. commander.js Principle analysis
  10. Common usage of nginx
  11. H5 jump to wechat app
  12. Front end algorithm interview must brush questions series [14]
  13. Thinking of cross end practice
  14. Vue server rendering principle analysis and introduction
  15. [KT] vscode plug in development example series (2)
  16. Design ideas of react and Vue framework
  17. JavaScript String.prototype.replaceAll 兼容性导致的问题
  18. JavaScript String.prototype.replaceAll Problems caused by compatibility
  19. 爱奇艺体育:体验Serverless极致扩缩容,资源利用率提升40%
  20. Iqiyi Sports: experience the ultimate expansion and contraction of serverless, and increase the utilization rate of resources by 40%
  21. 对前端异常window error捕获的全面总结
  22. A comprehensive summary of front end exception window error capture
  23. 成功解决Problem while trying to mount target]\“. HTTP response code is 400
  24. Problem while trying to mount target] \ ". HTTP response code is 400
  25. 放弃okhttp、httpClient,选择了这个牛逼的神仙工具!贼爽
  26. 前端面试每日 3+1 —— 第679天
  27. How to add elements at the beginning of an array in JS?
  28. Give up okhttp and httpclient and choose this awesome immortal tool! Thief Shuang
  29. Front end interview daily 3 + 1 - day 679
  30. 【2021 第一期】日常开发 26 个常见的 JavaScript 代码优化方案
  31. Daily development of 26 common JavaScript code optimization schemes
  32. 前端的字符串时间如何自动转换为后端Java的Date属性,介绍springMVC中如何解决时间转换问题
  33. How to automatically convert the front-end string time to the back-end Java date attribute, and how to solve the time conversion problem in spring MVC are introduced
  34. 前端面试常考题:JS垃圾回收机制
  35. ReactDOM.render串联渲染链路(一)
  36. 更简洁、更快速!腾讯云 Serverless 云函数创建流程再次升级!
  37. 粗涉Webpack
  38. Frequently asked questions in front end interview: JS garbage collection mechanism
  39. ReactDOM.render Serial rendering link (1)
  40. More concise and faster! Tencent cloud serverless cloud function creation process upgrade again!
  41. 更简洁、更快速!腾讯云 Serverless 云函数创建流程再次升级!
  42. About webpack
  43. More concise and faster! Tencent cloud serverless cloud function creation process upgrade again!
  44. 详解vue静态资源打包中的坑与解决方案
  45. 一篇搞懂TCP、HTTP、Socket、Socket连接池
  46. 字节跳动2021前端技术岗发布+最新内部面试题
  47. Detailed explanation of Vue static resource packaging and Solutions
  48. Understanding TCP, HTTP, socket, socket connection pool
  49. 2008-2021 front end technical post release + latest internal interview questions
  50. 4. Vue基本指令
  51. 4. Vue basic instruction
  52. Java 发起 http 请求
  53. Java initiates HTTP request
  54. 网站由http升级为https图文教程
  55. Upgrade the website from HTTP to HTTPS
  56. 更简洁、更快速!腾讯云 Serverless 云函数创建流程再次升级!
  57. More concise and faster! Tencent cloud serverless cloud function creation process upgrade again!
  58. 混合开发入门 Vue结合Android/iOS开发仿京东项目App
  59. Hybrid development entry Vue combined with Android / IOS to develop app imitating Jingdong project
  60. 无缝对接 Tableau,这家月活跃用户 5000+ 的大型银行如何实现自助式分析?