Preface
RxHttp Is based on RxJava2+Retrofit 2.9.0+OkHttp 4.9.0 Lightweight implementation , Perfect compatibility MVVM Architecture of network request encapsulation class library , Cabinet and delicate , Simple and easy to use , Easy to handle network requests .
GitHub
https://github.com/kongpf8848/RxHttp
Bright spot
-
Very little code , Insufficient class library size 100kb, But it's good enough for most of APP Network request task for , Concentrated is the essence _^_
-
Perfect compatibility MVVM,MVC framework , compatible Kotlin and Java,Kotlin+MVVM+RxHttp The combination is more sour and refreshing ,MVVM The official recommendation , Hold tight Google The thighs are right
-
Perfect solution to the thorny problem of generic type erasure , Restore the real type of a generic
-
Born to support web requests and Activity,Fragment Life cycle binding , When the interface is destroyed, the network request callback will be cancelled automatically
-
Natural support BaseUrl, Supports dynamic incoming Url
-
Support customization OkHttpClient.Builder, Highly customizable network request parameters
-
Support Glide Share one with network request OkHttpClient, make the best of OkHttpClient Thread pool and connection pool of , Most of the time, one App One OkHttpClient That's enough
-
Support GET,POST,PUT,DELETE Etc , Support file upload and progress monitoring , Support to upload multiple files at the same time , Support Uri Upload
-
Support file download and progress monitoring , Support large file download , Support breakpoint download
Use requirement
Project based on AndroidX,Java8+,minSdkVersion>=21
Use
implementation 'com.github.kongpf8848:RxHttp:1.0.11'
To configure ( Optional )
RxHttpConfig.getInstance()
/**
* Failed retries
*/
.maxRetries(3)
/**
* Time interval between failed retries
*/
.retryDelayMillis(200)
/**
* Customize OkHttpClient.Builder(),RxHttp Support customization OkHttpClient.Builder(),
* If not defined , Then use RxHttp default OkHttpClient.Builder()
*/
.builder(OkHttpClient.Builder().apply {
connectTimeout(60, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(60, TimeUnit.SECONDS)
/**
* DEBUG In mode , Add log interceptor , It is recommended to use RxHttp Medium FixHttpLoggingInterceptor, Use OkHttp Of HttpLoggingInterceptor When uploading and downloading, there will be IOException problem
*/
if (BuildConfig.DEBUG) {
addInterceptor(FixHttpLoggingInterceptor().apply {
level = FixHttpLoggingInterceptor.Level.BODY
})
}
})
Based on using
- GET/POST/PUT/DELETE/ Upload request
RxHttp.getInstance()
/**
* get: Request type , for get,post,put,delete,upload, They correspond to each other GET/POST/PUT/DELETE/ Upload request
* context: Context , for Context,Activity or Fragment type , When context by Activity or Fragment Network request and lifecycle binding
*/
.get(context)
/**
* request url, Such as https://www.baidu.com
*/
.url("xxx")
/**
* Request parameter key value pairs , The type is Map<String, Any?>?, Such as hashMapOf("name" to "jack")
*/
.params(map)
/**
* Each network request corresponds to tag value , for null, Used for subsequent manual operation according to tag Cancel the specified network request
*/
.tag("xxx")
/**
* HttpCallback: Network callback , Parameters xxx To return the data model corresponding to the data ,
* similar RxJava Medium Observer,onComplete Only in onNext Execute after callback , If there is an error, it will only call back onError But not onComplete
*/
.enqueue(object : HttpCallback<xxx>() {
/**
* http Callback at the beginning of the request
*/
override fun onStart() {
}
/**
* http Callback on successful request
*/
override fun onNext(response: xxx?) {
}
/**
* http Callback on request failure
*/
override fun onError(e: Throwable?) {
}
/**
* http Call back when the request completes successfully
*/
override fun onComplete() {
}
/**
* Upload progress callback , The request type is upload It will be called back when it's done
*/
override fun onProgress(readBytes: Long, totalBytes: Long) {
}
})
- Download request
RxHttp.getInstance()
/**
* download: Request type , Download request
* context: Context , If you don't need to bind to the lifecycle , It should be delivered applicationContext
*/
.download(context)
/**
* Save the path
*/
.dir(dir)
/**
* Save the file name
*/
.filename(filename)
/**
* Whether it is a breakpoint download , The default is false
*/
.breakpoint(true)
/**
* Download address , Such as http://study.163.com/pub/ucmooc/ucmooc-android-official.apk
*/
.url(url)
/**
* request Tag
*/
.tag(null)
/**
* Download callback
*/
.enqueue(object: DownloadCallback() {
/**
* Callback at the start of download
*/
override fun onStart() {
}
/**
* Call back when the download is complete
*/
override fun onNext(response: DownloadInfo?) {
}
/**
* Call back when download fails
*/
override fun onError(e: Throwable?) {
}
/**
* When the download is complete, call back
*/
override fun onComplete() {
}
/**
* Download progress callback
*/
override fun onProgress(readBytes: Long, totalBytes: Long) {
}
})
- Cancel the request
/**
* tag:Any?, request Tag, Corresponding to... In the network request Tag value
* If not null, Cancel the specified network request ,
* If null, Cancel all network requests
*/
RxHttp.getInstance().cancelRequest(tag)
Project practice
Here we assume that the data format returned by the server is {"code":xxx,"data":T,"msg":""}, among code Is the response code , integer , be equal to 200 When it comes to success , The rest are failures ,data The corresponding data type is generic (boolean,int,double,String, object { }, Array [ ] Other types )
{
"code": 200,
"data":T,
"msg": ""
}
Corresponding Response Class is
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 project
- Definition MVCHttpCallback, It is used to call back the result of network request to UI Interface
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 } /** * Callback at the beginning of the request , You can load here loading Dialog, etc , Null implementation by default */ open fun onStart() {} /** * Abstract method , Request successful callback , The returned content is generic , Corresponding TKResponse Of data */ abstract fun onSuccess(result: T?) /** * Abstract method , Request failed callback , The returned content is code( Error code ),msg( error message ) */ abstract fun onFailure(code: Int, msg: String?) /** * Upload progress callback , Null implementation by default */ open fun onProgress(readBytes: Long, totalBytes: Long) {} /** * Callback on request completion , This method will not be called back until the request is successful , Null implementation by default */ open fun onComplete() {} }
- Define the network interface , encapsulation GET/POST Wait for network request
object MVCApi { /** * GET request * context: Context * url: request url * params: parameter list , for null * tag: Identify a network request * callback: Network request 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 request * context: Context * url: request url * params: parameter list , for null * tag: Identify a network request * callback: Network request 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) } } }
- stay View Layering as Activity Call the network interface
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") } })
For specific use, please refer to demo Code ,demo There are detailed examples in MVC How to use the project RxHttp
-
MVVM project
- Definition Activity Base class 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?) {} /** * establish 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 } } } }
- Definition ViewModel Base class of BaseViewModel
open class BaseViewModel(application: Application) : AndroidViewModel(application) { /** * Network warehouse */ protected val networkbaseRepository: NetworkRepository = NetworkRepository.instance /** * Context */ protected var context: Context = application.applicationContext }
- Define the network warehouse , Encapsulating network interfaces
/** * MVVM Architecture network warehouse * 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() { /** * Pro - , Don't do anything here , Don't give LiveData assignment , prevent onNext Corresponding LiveData Data is covered , * stay TKState class handle Method will handle callbacks in particular , Don't worry */ } 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 } /** * Upload * Support uploading multiple files ,map Corresponding value The type is File Type or Uri type * Monitor the upload progress 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 } /** * download * context: Context , If you don't need to bind to the lifecycle , It should be delivered applicationContext * url: Download address * dir: Local directory path * filename: Save the file name * callback: Download progress callback * md5: Download the file MD5 value * breakpoint: Whether breakpoint download is supported , The default is 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) } }
- Definition TKState class , Used to convert network callbacks to LiveData
/** * take HttpCallback The callback is converted to the corresponding 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) } } }
- After a series of packages , Last in View Layering as Activity in ViewModel call Repository The interface
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") } }
Specific use should also refer to demo Code ,demo There are detailed examples in MVVM How to use the project RxHttp