rxjava+LiveData VS coroutine + flow, +Retrofit+Okhttp3, different use of the two schemes

1. Premise

The author recently studied kotlin. He just saw the collaborative process and flow. He found that these two things can replace rxjava and LiveData. Then I'll find the difference and experience the difference between the two things together. Of course, if you use JAVA, be honest and practical, RXJAVA+LiveData. Coprocessing is unique to kotlin in Android.

2. Comparison

It's a mule or a horse. Take it out for a walk. Let's compare these two different things a little
I won't let go of the basic concepts. You can learn about them first. I'll only mention some important ones here.

RXJAVA: chain programming, convenient thread switching, asynchronous operation,
Disadvantages: higher learning cost, more operators and more callbacks, but more flexible

Coroutine: the "thread" in the thread, which realizes asynchronous operation, convenient thread switching, and structural concurrency

LiveData: responsive programming, following the life cycle.
Disadvantages: setvalue cannot be set in the io thread, while postvalue will lose data

Flow: cold flow one-to-one, statteflow heat flow is similar to liveData, but it can still send data in io thread. It has back pressure function

3. Analysis

If you are still developing Android with Java, please make changes. Kotlin is so delicious! After learning kotlin, is it a bit wasteful that you don't use it for the unique cooperation process and Flow
We can see that we often use asynchronous operation, chain programming and thread switching in RXJAVA, which can also be perfectly realized in the collaborative process. It is easy to read when writing code (you can look below)
The biggest hole in LiveData, postValue, can be made up by stateflow, so there is no big problem when we use Flow
Don't say so much, the code will more intuitively see where the problem occurs

4. Code comparison

Let's write an asynchronous operation in RXJAVA and listen for the response in the Fragment
0.Fragment

  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    val retrofitViewModel = ViewModelProvider(requireActivity()).get(retrofitViewModel::class.java)
  retrofitViewModel.TextLiveData.observe(this,{
            Log.d(TAG, "onCreate: $it")
        })
 retrofitViewModel.viewModelScope.launch {
            retrofitViewModel.TextFlow.collect {
                Log.d(TAG, "onCreate:Let's take a look at the situation after the subscription: $it")
                //Add ViewMethod
            }
        }
}

1.RXJAVA+LiveData+viewModel

class retrofitViewModel : ViewModel() {
	val TextLiveData by lazy {
        MutableLiveData<Int>().also {
            Observable.create(ObservableOnSubscribe<Int?> { emitter ->
                emitter.onNext(1)
                emitter.onComplete()
            }).subscribeOn(Schedulers.io())
                    .subscribe(object : Observer<Int?> {
                        override fun onSubscribe(d: Disposable) {
                            println("onSubscribe=$d")
                        }


                        override fun onError(e: Throwable) {
                            println("onError=$e")
                        }

                        override fun onComplete() {
                            println("onComplete")
                        }

                        override fun onNext(value: Int?) {
                            //it.value = value this is not possible
                            it.postvalue(value)
                        }
                    })

        }
    }
}

2. Synergy + StateFlow
class retrofitViewModel : ViewModel() {

  val TextFlow by lazy {
        MutableStateFlow(0).also {
            viewModelScope.launch(Dispatchers.IO) {
                it.value = 1
            }
        }
    }

Let's look at the results

It can be clearly seen that the two things realize the same function. Setting a value asynchronously enables the Fragment to listen to the value change and respond. However, the area in the amount of code can be large. Moreover, StateFlow must have an initial value

Let's take another look at the differences in concurrency between coroutines and Rxjava

RXJAVA uses flapMap for Concurrency:

fun RxJavaParallel(){
        val list = Arrays.asList(1,2,3)
        Observable.fromIterable(list).flatMap{integer->
            Log.d(TAG, "RxJavaParallel: TASK_$integer Start:"+System.currentTimeMillis())

            getObservable(integer)
        }.subscribe(object : Observer<String?> {
            override fun onSubscribe(d: Disposable) {
                println("onSubscribafgsdfgre=$d")
            }


            override fun onError(e: Throwable) {
            }

            override fun onComplete() {

            }

            override fun onNext(value: String?) {
                //TODO("Not yet implemented")
                Log.d(TAG, "onNext: Receive sent tasks:$value")
            }
        })
    }

    fun getObservable(integer: Int): Observable<String?>? {
        return Observable.create(ObservableOnSubscribe<String?> { emitter ->
            emitter.onNext("The first" + integer + "The first prism task of the ball")
            Thread.sleep((1 * 1000).toLong())
            emitter.onComplete()
            Log.d(TAG, "RxJavaParallel: TASK_ finish:"+System.currentTimeMillis())

        }).subscribeOn(Schedulers.newThread())
    }

Concurrency:

    fun CoroutineParallel(){
        viewModelScope.launch {
            launch {
                Log.d(TAG, "CoroutineParallel: TASK_1 Start:"+System.currentTimeMillis())
                delay(1000)
                Log.d(TAG, "CoroutineParallel: TASK_1 Finish:"+System.currentTimeMillis())
            }
            launch {
                Log.d(TAG, "CoroutineParallel: TASK_2 Start:"+System.currentTimeMillis())
                delay(1000)
                Log.d(TAG, "CoroutineParallel: TASK_2 Finish:"+System.currentTimeMillis())
            }

        }
    }

result:

It can be seen that everyone does the same thing, delaying 1 second (blocking) and then outputting, but coprocessing does have many advantages in concept understanding and code reading

5. Add Retrofit+Okhttp3 (final version)

Rxjava+Retrofit is our commonly used network request framework
When the collaborative process came out, Retrofit also quickly supported the collaborative process
Then let's look at the two differences

Build okhttp client and retrofit

 fun createGithubApi(): NormalInterface {
                val logger = HttpLoggingInterceptor()
                logger.level = HttpLoggingInterceptor.Level.BASIC

                val client = OkHttpClient.Builder()
                        .addInterceptor(logger)
                        .connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)//connection timed out
                        .readTimeout(20 * 1000, TimeUnit.MILLISECONDS)//Reading timeout, can I read the content
                        .retryOnConnectionFailure(true)//Reconnection
                        .build()
                return Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .client(client)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build()
                        .create(NormalInterface::class.java)
            }

Define the API interface. Note that retrofit returns Observable. And the coroutine is RepoSearchResponse

interface NormalInterface :BaseApi{
   //This is RXJAVA
    @GET("search/repositories?sort=stars")
    fun searchRepos_X(
            @Query("q") query: String,
            @Query("page") page: Int,
            @Query("per_page") itemsPerPage: Int
    ): Observable<RepoSearchResponse>

    //This is a collaborative process
    @GET("search/repositories?sort=stars")
    suspend fun searchRepos(
            @Query("q") query: String,
            @Query("page") page: Int,
            @Query("per_page") itemsPerPage: Int
    ): RepoSearchResponse
data class RepoSearchResponse(
        @SerializedName("total_count") val total: Int = 0,
)
}

Here's the point. Let's enable coroutines in viewModel and add them to StateFlow

  val TextFlow_Retrofit by lazy {
        MutableStateFlow(RepoSearchResponse()).also {
            viewModelScope.launch(Dispatchers.IO) {
                it.value = retrofitUse.createGithubApi().searchRepos("Android", 0, 20)

            }
        }
    }

Another key point is that LiveData in viewModel uses Rxjava+Retrofit to send network requests

    //Rxjava+LiveData+retrofit+okhttp
    val TextLiveData_Retrofit by lazy {
        MutableLiveData<RepoSearchResponse>().also {

            retrofitUse.createGithubApi_X().searchRepos_X("Android", 0, 20).
            subscribeOn(Schedulers.io())
                    .subscribe(object:Observer<RepoSearchResponse>{
                        override fun onSubscribe(d: Disposable?) {
                           // TODO("Not yet implemented")
                        }
                        override fun onNext(value: RepoSearchResponse?) {
                            //TODO("Not yet implemented")
                            it.value = value
                        }
                        override fun onError(e: Throwable?) {
                           // TODO("Not yet implemented")
                        }
                        override fun onComplete() {
                           // TODO("Not yet implemented")
                        }
                    })
        }
    }

Look at the code, you should also understand almost... I find that there are still few online comparisons. Although this comparison is very basic, it also makes people more intuitive to find these two different situations
To sum up:
The network request of RXJAVA+Retrofit is really easy to use. The operators and learning in RxJava are relatively difficult, and it's more troublesome to package, but at the code level, it doesn't feel as comfortable to read as the collaborative process. kotlin is really fragrant
After learning about the use of LiveData and Flow, I feel that it is more suitable for Flow in Kotlin
Well, although the official name is kotlin First, the higher-order functions, coroutines and Flow are more basic and important features. I hope I can make persistent efforts and continue to learn

Tags: Java Android

Posted on Mon, 20 Sep 2021 01:17:26 -0400 by nuttynibbles