RxHttp – 輕量級、可擴展、易使用、完美兼容MVVM、MVC架構的網路封裝類庫
前言
RxHttp是基於RxJava2+Retrofit 2.9.0+OkHttp 4.9.0實現的輕量級,完美兼容MVVM架構的網路請求封裝類庫,小巧精緻,簡單易用,輕輕鬆鬆搞定網路請求。
GitHub
//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,如//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)
/**
* 下載地址,如//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
//github.com/kongpf8848/RxHttp