RxHttp - 轻量级、可扩展、易使用、完美兼容MVVM、MV
前言
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