GithubLink

How to write Android network request?

Kotlin coroutines

A lot of people talk about using Kotlin coroutines, but when they look at it, it doesn’t work very well.

  • Outside need an asyncUI {}, look at all unhappy
  • If the interface fails, you need to write logic at each call to handle it. Errors such as network timeouts cannot be handled globally.
  • The code is not easy to understand, for example: deferred1. Wait (TOAST) What is a wait(TOAST)?
  • There is no global progress box

The following code refers to playing with a network request using a Kotlin coroutine

AsyncUI {// Assume that these are two different API requests val deferred1 = bg {server.getapistore ().login1("173176360", } val deferred2 = bg {server.getapistore ().login2("173176360", "123456").execute()} Textview.text = "loading" delay(5, timeunit.seconds) // when the UI coroutine is done Val Response = Deferred1.wait (TOAST) Textview.text = Response.toString ()}Copy the code

Pure OkHttp

  • Code is also more troublesome to write, there is no hint function.
  • Errors such as network timeouts cannot be handled globally.
  • There is no global progress box
  • You need to switch threads to manipulate the UI
Callback = {onSuccess = {res -> // TODO} onFail = {error -> // TODO}} request.execute(callback)Copy the code

Encapsulation OkHttp

Some people encapsulate OkHttp themselves. It’s up to the individual.

If you have an interface that’s called 10 times, you have to write the address of the interface 10 times and ‘toClass<Response>()’, which is obviously not good.

  • The code is a bit redundant (service address and return parameter object class).
  • Errors such as network timeouts cannot be handled globally(No, not sure).
  • There is no global progress box(No, not sure).

The following code refers to RxHttp 2000+ STAR, a coroutine request, in just three steps

val response = RxHttp.get("/service/..." ).toclass <Response<Student>>().await() if (response.code == 200) {else {// get the data field to refresh the UI} else {// get the MSG field to refresh the UI}Copy the code

Dry goods

So, is there a way to

  • Global default progress box, and support custom progress box.
  • Global default error handling, but also support for custom error handling.
  • Multiple requests in turn can also be friendly support.
  • At the end of the page, the request is automatically cancelled.

Others: RxNet = RxJava3 + Retrofit2 + OkHttp3

Here is an example call. The object returned from the call is the type declared in the interface, with a full code Hint, which can be seen in the IDE

interface API { @GET("Simple? net=1&bz=1") fun simple(): Observable<ResponsePacket<String>> @GET("NetError? net=0&bz=1") fun netError(): Observable<ResponsePacket<String>> @GET("BzError? net=1&bz=0") fun bzError(): Observable<ResponsePacket<String>> @GET("authorizeFailed") fun authorizeFailed(): Observable<ResponsePacket<String>>} Private fun subscribeX() {// Simple mode, The API.simple().subscribex (context) {totoast. MakeText (context, "response:" + it.message, LENGTH_SHORT).show()} Api.simple ().subscribexBuilder (context).progress {null//null means no default and custom progress}.failed {  true//return error handled or not }.successOnly(true) .response { Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() } }Copy the code

Multiple requests are initiated in sequence

Additional references required

Implementation “com. Making. DonaldDu: XIntent: 1.5.3” / / Waterfall

// Multiple requests are initiated in sequence, and the process is automatically terminated without calling next. When switching between requests, . Box won't blink buttonMultReq setOnClickListener {Waterfall. Flow {apiSample. SubscribeX (context) {Log i. (" TAG ", "ApiSample1 ") next()// Enter the next flow with any type of data such as: next("DATA") } }.flow { apiSample.subscribeX(context) { Log.i("TAG", "apiSample2") next() } }.flow { apiSample.subscribeX(context) { Log.i("TAG", "apiSample3") Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() } } }Copy the code

Delayed return result

Sometimes, if you want to slow down a bit, the animation needs time to show. For example, if you check your App for updates too quickly, the screen will flash and show you no updates. A 900ms delay can solve this problem (personally feel better).

    buttonDelay.setOnClickListener {
        val start = System.currentTimeMillis()
        apiSample.delayResponse(5000)
                .subscribeX(context) {
                    val cost = System.currentTimeMillis() - start
                    Log.i("TAG", "apiSample cost $cost")
                }
    }
Copy the code

implementation

To support the default progress box and global error handling, you need to implement two classes, StyledProgressGenerator and IErrorHandler. You already have a default implementation, and you just need to make some adjustments based on actual needs.

class SampleStyledProgressGenerator : StyledProgressGenerator {
    override fun generate(observer: IObserverX): StyledProgress? {
        val context = observer.context
        return if (context is FragmentActivity) {
            MultListenerDialog.getInstance(context, observer)
        } else null
    }
}

class SampleErrorHandler : BaseErrorHandler() {
    override fun showDialog(context: Context, msg: String): Dialog? {
        return AlertDialog.Builder(context)
                .setMessage(msg)
                .setPositiveButton("OK", null).show()
    }

    override fun isAuthorizeFailed(activity: Activity, error: IError): Boolean {
        return error.code == 9001
    }

    override fun onLogout(context: Context) {
        val msg = "onLogout"
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
        Log.i(TAG, msg)
    }

    override fun isDebug(): Boolean = true

    companion object {
        private val TAG = IErrorHandler::class.java.simpleName
    }
}
Copy the code

Progress box style adjustment

The default implementation is this class, MultListenerDialog, so refer to your own implementation. If the adjustment is small, you can create a Layout with the same name (R.layout.net_progress_dialog) and adjust it yourself.

Manually control the progress box

If you need to control the progress box somewhere other than an interface call, you can call the following two methods.

For example, compress the image first, then upload it. It is better to display the progress box before starting compression, and the progress box will be automatically closed after uploading. You can also achieve compression process after cancellation, not upload.

It is important to note that if the Activity also has methods of the same name, calling the following methods will execute the one defined in the Activity, not the one below.

fun FragmentActivity.showProgress(): MultListenerDialog {
    val dialog = MultListenerDialog.getInstance(this)
    dialog.showProgress()
    return dialog
}

fun FragmentActivity.dismissProgress(delay: Boolean = true) {
    MultListenerDialog.getInstance(this).dismissProgress(delay)
}
Copy the code

Introduction of depend on

dependencies {
    implementation 'com.github.DonaldDu:RxNet:x.x.x'//JitPack version
}
Copy the code

Other instructions

This project was renamed from RetrofitRxUtil, mainly because the previous name was not easy to understand, so it was changed to RxNet, which means a tool to quickly implement network requests with RxJava.

The previous project is not maintained and transferred to the new project.

My open source project

  • ApiHolder Multi-server Interface Adaptation (super simple)
  • DynamicServer dynamic configuration service

The last

Open source is not easy, writing articles is not easy, please give this article point like, if you can, and give a star, grateful