Android Notes (18): Combining Retrofit2 and Rxjava3 for Compose components to achieve network access

1. Retrofit2

The Retrofit2 library (https://square.github.io/retrofit/) launched by Square has changed the way of network access. It implements the encapsulation of network requests. The Retrofit library uses a callback processing method, so that by submitting a request and corresponding parameter configuration through the interface, you can get the corresponding response, and parse the data obtained in the response into a specific data format, such as parsing JSON data into objects.
The process of Retrofit accessing network resources:
Insert image description here

2. RxJava3

RxJava3 (https://github.com/ReactiveX/RxJava) is a java implementation of reactive programming (Reactive Extensions). It implements an asynchronous programming interface based on the observer pattern. The RxJava library is used to compose asynchronous and event-based programs using observable sequences.
Insert image description here

Observableobservable

is a topic that can represent any object. It can obtain data or other status values ​​from the data source. Observable objects emit streams of data. As long as an observer starts to receive it, Observable will provide data and emit a data stream. Observables can have multiple subscribers.
Common observable streams in RxJava3 are shown in the following table:

kind illustrate
io.reactivex.rxjava3.core.Flowable 0...N flow, supports responsive flow and back pressure and is executed according to the onSubscribe onNext (onError or onComplete) attribute, where onNext can be executed multiple times, onError and onComplete are mutually exclusive.
io.reactivex.rxjava3.core.Observable 0...N stream, backpressure is not supported. The onSubscribe onNext (onError or onComplete) order is executed. onNext can be executed multiple times. onError and onComplete are mutually exclusive.

Operator

Responsible for modifying and transforming events emitted by Observable observable objects. Each Operator operation is actually a method/function with an Observable object as an input parameter. For each item of data emitted by the Observable object, it will apply the data in the Operator method/function, and then return the processing result in the form of an Observable object. . So what is returned is another Observable object. This Observable object can continue to emit backwards or end.

There can be several operators, in the following form:

dataSource.operator1()
.operator2()
.operator3()

These operators form an upstream and downstream relationship.

Observer

Observer An observer subscribes to the sequence data of an Observable object and reacts to each item of the Observable object. Observers are responsible for processing events and are consumers of events. Notify observers whenever the associated Observable emits data. Observers process data one by one.

Back pressure strategy

Because Observable and Observer are implemented in different threads to send data and receive data respectively. Since the processing time in different threads is accompanied by the complexity of the problem, the speed of data processing between the two will be different. If the data emitted by the observed object is much faster than the data processing speed of the observer object, the data will be put into the cache or discarded directly. Both of these methods have their drawbacks. Therefore, a "Back Pressure" strategy needs to be developed to solve the problem of inconsistent data transmission speeds between the observer and the observer in asynchronous scenarios. Therefore, what is usually called backpressure is a strategy to control the flow rate in an asynchronous environment. Common back pressure strategies are shown in the following table:

Back pressure strategy illustrate
MISSING Indicates that the Flowable created through the create method does not specify a backpressure strategy, and the data emitted through OnNext will not be cached or discarded. The downstream must process the operator.
ERROR When backpressure occurs, a MissingBackpressureException signal will be sent to prevent downstream consumption from being unable to continue.
BUFFER When back pressure occurs, the data will be cached until the downstream digestion of data is completed.
DROP When back pressure occurs, if the downstream cannot continue to consume data, the most recently transmitted value will be discarded.

3. Network access processing examples

Assume that there is already a network resource http://127.0.0.1:5000/json/students.json (can also be written as: http://localhost:5000/json/students.json). The accessed content is as follows Display:
Insert image description here
In the following example, the Retrofit2+RxJava3+Compose component will be combined to achieve access to the above resources and displayed in a list. The running result is similar to the picture below:
Insert image description here

1.AndroidManifest.xml configures network access

To access the network, you need to set Internet access permissions and set clear text access permissions in the application:

<uses-permission android:name="android.permission.INTERNET" />
<application
    android:usesCleartextTraffic="true" ...>
</application>

2. Increase dependence

Add the following dependencies in build.gradle.kt of the project module:

//retrofit框架
implementation (“com.squareup.retrofit2:retrofit:2.9.0”)
implementation (“com.squareup.retrofit2:converter-gson:2.9.0”)

//Add dependency on RxJava library
implementation (“io.reactivex.rxjava3:rxjava:3.1.5”)

//Add support for RxJava library in Android
implementation("io.reactivex.rxjava3:rxandroid:3.0.2")

//Add Retrofit to support RxJava3’s CallAdapter
implementation(“com.squareup.retrofit2:adapter-rxjava3:2.9.0”)

implementation(“androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1”)

The version number can also be readjusted according to the latest version.

3. Define entity classes

data class Student(val id:String,
val name:String,
val gender:String,
val age:Int)

4. Define network access

(1) Define network service access interface

interface StudentService{
    
    
    @GET("students.json")
    fun getStudents(): Flowable<List<Student>>
}

Indicates accessing the students.json resource to obtain an RxJava3 Flowable observable object. This observable object encapsulates a list containing student records.

(2) Define network service creation class

object StudentServiceCreator{
    
    
    private val urlStr="http://10.0.2.2:5000/json/"

    private val retrofit = Retrofit.Builder()
        .baseUrl(urlStr)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
        .build()

    fun <T> createService(serviceClass:Class<T>):T = retrofit.create(serviceClass)
}

The web server is a local server. Since 127.0.0.1 of the mobile simulator has been occupied, if you want to access the local server on the mobile terminal, you can access it through 10.0.2.2.
Define the Retrofit object, and set the conversion object for parsing JSON data and the adapter for concurrent processing in the object.

5. Define the view model

Define the view model, call the related classes for network access processing, and obtain network resources.

class StudentViewModel: ViewModel() {
    
    
    private val students:SnapshotStateList<Student> = mutableStateListOf()
    private val creator  = StudentServiceCreator.createService(StudentService::class.java)
    fun doNetwork(urlStr:String){
    
    
        creator.getStudents()
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe{
    
    it:List<Student>->
                    if(students.isEmpty()){
    
    
                        students.addAll(it)
                    }
               }
    }
    fun getData() = students
}

6. Define the interface

(1) Define a combinable function for displaying single items of student records in the list

@Composable
fun StudentItemCard(student: Student){
    
    
    Card(modifier = Modifier
        .fillMaxWidth()
        .wrapContentHeight()
        .padding(5.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 5.dp),
        colors = CardDefaults.cardColors(
            containerColor = Color.Blue,
            contentColor = Color.White)){
    
    
        Column(modifier= Modifier
            .fillMaxWidth()
            .wrapContentHeight()
            .padding(5.dp)){
    
    
            Text(text = "${
      
      student.id}",fontSize = 24.sp)

            Row(modifier = Modifier.padding(15.dp)){
    
    
                Text(text = "${
      
      student.name}",fontSize = 24.sp)
                Spacer(modifier = Modifier.padding(5.dp))

                Text(text = "${
      
      student.gender}",fontSize = 24.sp)
                Spacer(modifier = Modifier.padding(5.dp))

                Text(text = "${
      
      student.age}",fontSize = 24.sp)
            }
        }
    }
}

(2) Define the list of student records

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(stuVM: StudentViewModel = viewModel()){
    
    
    var students = stuVM.getData()
    val displayState = remember{
    
     mutableStateOf(false) }

    Scaffold(floatingActionButton = {
    
    
        FloatingActionButton(onClick = {
    
    
            displayState.value = true
            //访问网络资源
            stuVM.doNetwork("http://10.0.2.2:5000/json/students.json")
            //获取学生记录
            students = stuVM.getData()
        }) {
    
    
            Icon(Icons.Filled.Refresh,contentDescription = null)
        }
    }){
    
    
        Column(horizontalAlignment = Alignment.CenterHorizontally){
    
    
            Text(modifier = Modifier.fillMaxWidth(),
                text = "学生记录列表",
                textAlign = TextAlign.Center,
                fontSize = 28.sp)
            if(displayState.value){
    
    
                LazyColumn{
    
    
                    items(students){
    
    it: Student ->
                        StudentItemCard(student = it)
                    }
                }
            }
        }
    }
}

7. Define the main activityMainActivity

Call the interface in the main activity

class MainActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            Ch09_DemoTheme {
    
    
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
    
    
                    MainScreen()
                 }
            }
        }
    }
}

references

Chen Yi Chapter 8 Android Network Application "Android Mobile Application Development (Micro Course Edition)" P258-P293 Tsinghua University Press

Guess you like

Origin blog.csdn.net/userhu2012/article/details/135003669