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. Mybatis profile depth
  2. 深入理解Java虚拟机是怎么实现synchronized的?
  3. On the relationship among object, class, abstract class, interface and inheritance in Java
  4. Spring security real combat dry goods: the core logic of oauth2 login to obtain token
  5. In depth understanding of how Java virtual machine implements synchronized?
  6. 关于Java中的对象、类、抽象类、接口、继承之间的联系
  7. Spring Security 实战干货:OAuth2登录获取Token的核心逻辑
  8. On the relationship among object, class, abstract class, interface and inheritance in Java
  9. Spring security real combat dry goods: the core logic of oauth2 login to obtain token
  10. Spring事务
  11. 解决Docker MySQL无法被宿主机访问的问题
  12. Spring Boot 构建一个RESTful Web服务
  13. Spring transactions
  14. Solve the problem that docker MySQL cannot be accessed by the host
  15. Build a restful web service with spring boot
  16. Java中针对Yaml格式数据操作记录
  17. Java for yaml format data operation record
  18. Spring Authorization Server 全新授权服务器整合使用
  19. Java 集合处理/ 空值处理/ 异常处理,使用心得分享!
  20. Integrated use of new authorization server of spring authorization server
  21. Java collection processing / null value processing / exception processing, use experience sharing!
  22. mysql Innodb_flush_log_at_trx_commit 和 sync_binlog
  23. mysql Innodb_ flush_ log_ at_ trx_ Commit and sync_ binlog
  24. 不能回滚的Redis事务还能用吗
  25. 不能回滚的Redis事务还能用吗
  26. Can redis transactions that cannot be rolled back be used
  27. 23种java设计模式
  28. Java、JavaScript、C、C++、PHP、Python都是用来开发什么?
  29. Docker overlay 清理空间
  30. 「Linux」- 安装 Opera 浏览器 @20210223
  31. java的byte和C#的byte的不同之处
  32. Can redis transactions that cannot be rolled back be used
  33. 23 Java design patterns
  34. What are Java, JavaScript, C, C + +, PHP and python used to develop?
  35. Docker overlay cleaning space
  36. "Linux" - install opera browser @ 20210223
  37. Differences between Java byte and C byte
  38. SAP UI5 JavaScript文件的lazy load - 懒加载
  39. Java 在Excel中添加筛选器并执行筛选
  40. LiteOS:盘点那些重要的数据结构
  41. Lazy load lazy load of SAP ui5 JavaScript files
  42. Add filter and execute filter in excel by Java
  43. Liteos: inventory those important data structures
  44. HDFS依然是存储的王者
  45. [MySQL]事务的MVCC原理与幻读
  46. 93.7%的程序员!竟然都不知道Redis为什么默认16个数据库?
  47. Java 集合处理/ 空值处理/ 异常处理,使用心得分享!
  48. Spring Authorization Server 全新授权服务器整合使用
  49. Spring Security 实战干货:OAuth2登录获取Token的核心逻辑
  50. Java中各种锁的原理解析
  51. java的byte和C#的byte的不同之处
  52. Java 在Excel中添加筛选器并执行筛选
  53. HDFS is still the king of storage
  54. Mvcc principle and unreal reading of [MySQL] transaction
  55. 93.7% of programmers! Why does redis default to 16 databases?
  56. Java collection processing / null value processing / exception processing, use experience sharing!
  57. Integrated use of new authorization server of spring authorization server
  58. Spring security real combat dry goods: the core logic of oauth2 login to obtain token
  59. Principle analysis of various locks in Java
  60. Differences between Java byte and C byte