Κοινή χρήση τεχνολογίας

kotlin Οδηγός Μελέτης Ροής (3) Τελικό Κεφάλαιο

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Πρόλογος

Τα δύο πρώτα άρθρα εισήγαγαν τι είναι το Flow, πώς να το χρησιμοποιήσετε και σχετικές εξελίξεις στο χειριστή Το επόμενο άρθρο εισάγει κυρίως τη χρήση του Flow σε πραγματικά έργα.

Κύκλος ζωής ροής

Πριν παρουσιάσουμε τα πραγματικά σενάρια εφαρμογής του Flow, ας εξετάσουμε πρώτα το παράδειγμα χρονοδιακόπτη που παρουσιάστηκε στο πρώτο άρθρο του Flow Ορίσαμε μια ροή δεδομένων timeFlow στο ViewModel.

class MainViewModel : ViewModel() {

val timeFlow = flow {
    var time = 0
    while (true) {
        emit(time)
        delay(1000)
        time++
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Στη συνέχεια, στη Δραστηριότητα, λάβετε τη ροή δεδομένων που ορίστηκε προηγουμένως.

lifecycleOwner.lifecycleScope.launch {
    viewModel.timeFlow.collect { time ->
        times = time
        Log.d("ddup", "update UI $times")
    }
   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Επιτρέψτε μου να το εκτελέσω για να δω το πραγματικό αποτέλεσμα:

flow1.gif

Έχετε παρατηρήσει ότι όταν η εφαρμογή μεταβαίνει στο παρασκήνιο, το αρχείο καταγραφής εξακολουθεί να εκτυπώνεται. Αυτό δεν είναι σπατάλη πόρων.

lifecycleOwner.lifecycleScope.launchWhenStarted {
     viewModel.timeFlow.collect { time ->
         times = time
         Log.d("ddup", "update UI $times")
     }
   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Ας αλλάξουμε τη μέθοδο εκκίνησης της κορουτίνας από την εκκίνηση σε launchWhenStarted και ας την εκτελέσουμε ξανά για να δούμε το αποτέλεσμα:

flow2.gif

Μπορούμε να δούμε ότι όταν κάνετε κλικ στο κουμπί HOME και επιστρέφετε στο παρασκήνιο, το αρχείο καταγραφής δεν εκτυπώνεται πλέον μια ματιά:

flow3.gif

Μεταβαίνοντας στο προσκήνιο, μπορούμε να δούμε ότι ο μετρητής δεν ξεκινά από το 0, οπότε στην πραγματικότητα δεν ακυρώνει τη λήψη, απλώς διακόπτει τη λήψη δεδομένων στο παρασκήνιο Η γραμμή ροής εξακολουθεί να διατηρεί τα προηγούμενα δεδομένα Αντ' αυτού, το API έχει εγκαταλειφθεί.
Ας προσπαθήσουμε να μετατρέψουμε τον αντίστοιχο κώδικα:

lifecycleOwner.lifecycleScope.launch {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.timeFlow.collect { time ->
            times = time
            Log.d("ddup", "update UI $times")
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Εκτελέστε ξανά για να δείτε το αποτέλεσμα:

flow4.gif

Μπορούμε να δούμε ότι κατά την εναλλαγή από το φόντο στο προσκήνιο, τα δεδομένα ξεκινούν ξανά από το 0, πράγμα που σημαίνει ότι κατά την εναλλαγή στο παρασκήνιο, το Flow ακυρώνει την εργασία και όλα τα αρχικά δεδομένα διαγράφονται.

Χρησιμοποιούμε το Flow και μέσω του repeatOnLifecycle, μπορούμε να διασφαλίσουμε καλύτερα την ασφάλεια του προγράμματός μας.

Το StateFlow αντικαθιστά το LiveData

Οι προηγούμενες εισαγωγές είναι όλες παραδείγματα ψυχρής ροής ροής Στη συνέχεια, θα εισαγάγουμε μερικά κοινά σενάρια εφαρμογής θερμής ροής.
Εξακολουθείτε να χρησιμοποιείτε το προηγούμενο παράδειγμα χρονοδιακόπτη, τι θα συμβεί εάν η οθόνη εναλλάσσεται μεταξύ οριζόντιας και κάθετης οθόνης;

flow5.gif

Μπορούμε να δούμε ότι μετά την εναλλαγή μεταξύ οριζόντιων και κάθετων οθονών, η Δραστηριότητα δημιουργείται εκ νέου Μετά την εκ νέου δημιουργία, το timeFlow θα συλλέγεται εκ νέου και θα εκτελείται εκ νέου η ψυχρή ροή και στη συνέχεια θα ξεκινήσει ο χρονοδιακόπτης. μετρώντας από το 0. Πολλές φορές, θέλουμε να κάνουμε εναλλαγή μεταξύ οριζόντιων και κάθετων οθονών Αυτή τη στιγμή, ελπίζουμε ότι η κατάσταση της σελίδας θα παραμείνει αμετάβλητη, τουλάχιστον μέσα σε ένα συγκεκριμένο χρονικό διάστημα ζεστή ροή και δοκιμάστε:

val hotFlow =
    timeFlow.stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(5000),
        0
    )
    
    ```
lifecycleOwner.lifecycleScope.launch {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.hotFlow.collect { time ->
            times = time
            Log.d("ddup", "update UI $times")
        }
    }
}
```
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

Εδώ εστιάζουμε στις τρεις παραμέτρους της κατάστασης Η πρώτη είναι το εύρος της κορουτίνας, η δεύτερη είναι ο μέγιστος αποτελεσματικός χρόνος για τη διατήρηση της κατάστασης λειτουργίας της ροής, θα σταματήσει να λειτουργεί παράμετρος είναι η αρχική τιμή.

Εκτελέστε ξανά για να δείτε το αποτέλεσμα:

flow6.gif

Εδώ μπορούμε να δούμε το εκτυπωμένο αρχείο καταγραφής μετά την εναλλαγή μεταξύ οριζόντιας και κάθετης οθόνης Ο χρονοδιακόπτης δεν ξεκινά από το 0.
Έχουμε εισαγάγει παραπάνω πώς να τροποποιήσετε μια ψυχρή ροή σε μια ζεστή ροή Δεν έχουμε εισαγάγει ακόμη πώς το stateFlow μπορεί να αντικαταστήσει το LiveData.

private val _stateFlow = MutableStateFlow(0)
val stateFlow = _stateFlow.asStateFlow()

fun startTimer() {
    val timer = Timer()
    timer.scheduleAtFixedRate(object :TimerTask() {
        override fun run() {
            _stateFlow.value += 1
        }

    },0,1000)
}

```

viewModel.startTimer()

lifecycleOwner.lifecycleScope.launch {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.stateFlow.collect { time ->
            times = time
            Log.d("ddup", "update UI $times")
        }
    }
}
```
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Ορίζουμε μια hot ροή StateFlow και, στη συνέχεια, αλλάζουμε την τιμή stateFlow μέσω μιας μεθόδου startTimer(), παρόμοια με το LiveData setData Όταν κάνετε κλικ στο κουμπί, ξεκινήστε να αλλάζετε την τιμή StateFlow και συλλέγετε τις τιμές της αντίστοιχης ροής, παρόμοια με το. Μέθοδος LiveData Observe για την παρακολούθηση αλλαγών δεδομένων.
Ας ρίξουμε μια ματιά στο πραγματικό εφέ τρεξίματος:

flow7.gif

Σε αυτό το σημείο, έχουμε εισαγάγει τη βασική χρήση του StateFlow και τώρα θα εισαγάγουμε το SharedFlow.

SharedFlow

Για να κατανοήσουμε το SharedFlow, γνωρίζουμε πρώτα την έννοια των κολλητικών γεγονότων Κυριολεκτικά, όταν ένας παρατηρητής εγγραφεί σε μια πηγή δεδομένων, εάν η πηγή δεδομένων έχει ήδη τα πιο πρόσφατα δεδομένα, τότε τα δεδομένα θα προωθηθούν αμέσως στον παρατηρητή. Κρίνοντας από την παραπάνω εξήγηση, το LiveData συμμορφώνεται με αυτό το κολλώδες χαρακτηριστικό Τι γίνεται με το StateFlow; Ας γράψουμε μια απλή επίδειξη για επαλήθευση:


class MainViewModel : ViewModel() {

private val _clickCountFlow = MutableStateFlow(0)

val clickCountFlow = _clickCountFlow.asStateFlow()

fun increaseClickCount() {
    _clickCountFlow.value += 1
}
}
//MainActivity
```
val tv = findViewById<TextView>(R.id.tv_content)
val btn = findViewById<Button>(R.id.btn)
btn.setOnClickListener {
    viewModel.increaseClickCount()
}

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.clickCountFlow.collect { time ->
            tv.text = time.toString()
            Log.d("ddup", "update UI $time")
        }
    }
}
```

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

Πρώτα ορίζουμε ένα clickCountFlow στο MainViewModel, μετά στο Activity, αλλάζουμε τα δεδομένα clickCountFlow κάνοντας κλικ στο κουμπί και, στη συνέχεια, λαμβάνουμε το clickCountFlow και εμφανίζουμε τα δεδομένα στο κείμενο.
Ας ρίξουμε μια ματιά στο εφέ λειτουργίας:

flow8.gif

Μπορούμε να δούμε ότι κατά την εναλλαγή μεταξύ οριζόντιων και κατακόρυφων οθονών, η Δραστηριότητα δημιουργείται εκ νέου και το clickCountFlow συλλέγεται ξανά Δείτε ένα άλλο παράδειγμα Προσομοίωσης ενός σεναρίου κλικ για σύνδεση, κάντε κλικ στο κουμπί σύνδεσης για να συνδεθείτε και συνδεθείτε:

//MainViewModel
    private val _loginFlow = MutableStateFlow("")
    val loginFlow = _loginFlow.asStateFlow()
    fun startLogin() {
        // Handle login logic here.
        _loginFlow.value = "Login Success"
    }
//MainActivity

```
val tv = findViewById<TextView>(R.id.tv_content)
val btn = findViewById<Button>(R.id.btn)
btn.setOnClickListener {
    viewModel.startLogin()
}

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.loginFlow.collect {
            if (it.isNotBlank()) {
                Toast.makeText(this@MainActivity2, it, Toast.LENGTH_LONG).show()
            }
        }
    }
}
```
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Ο παραπάνω κώδικας προσομοιώνει πραγματικά μια σύνδεση κλικ και, στη συνέχεια, ζητά ότι η σύνδεση είναι επιτυχής. Ας ρίξουμε μια ματιά στο πραγματικό αποτέλεσμα λειτουργίας:

flow9.gif

Είδατε ότι μετά την εναλλαγή μεταξύ οριζόντιων και κατακόρυφων οθονών, εμφανίζεται ξανά η επιτυχής ειδοποίηση σύνδεσης. Δεν περάσαμε από τη διαδικασία επανασύνδεσης SharedFlow και δοκιμάστε το:

    private val _loginFlow = MutableSharedFlow<String>()

    val loginFlow = _loginFlow.asSharedFlow()
    fun startLogin() {
        // Handle login logic here.
        viewModelScope.launch {
            _loginFlow.emit("Login Success")
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Αλλάξαμε το StateFlow σε SharedFlow Μπορούμε να δούμε ότι το SharedFlow δεν απαιτεί αρχική τιμή Η μέθοδος εκπομπής προστίθεται στο σημείο σύνδεσης για την αποστολή δεδομένων και το μέρος όπου λαμβάνονται τα δεδομένα παραμένει αμετάβλητο.

flow10.gif

Εδώ μπορούμε να δούμε ότι η χρήση του SharedFlow δεν θα προκαλέσει αυτό το πρόβλημα κολλητότητας Στην πραγματικότητα, το SharedFlow έχει πολλές παραμέτρους που μπορούν να ρυθμιστούν:

    public fun <T> MutableSharedFlow(
        // 每个新的订阅者订阅时收到的回放的数目,默认0
        replay: Int = 0,

       // 除了replay数目之外,缓存的容量,默认0
        extraBufferCapacity: Int = 0,

      // 缓存区溢出时的策略,默认为挂起。只有当至少有一个订阅者时,onBufferOverflow才会生效。当无订阅者时,只有最近replay数目的值会保存,并且onBufferOverflow无效。
        onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
    )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Υπάρχουν περισσότερες χρήσεις του SharedFlow που περιμένουν να τις ανακαλύψουν όλοι, αλλά δεν θα υπεισέλθω σε λεπτομέρειες εδώ.

Άλλα κοινά σενάρια εφαρμογής

Προηγουμένως, παρουσιάσαμε τη βασική ροή ψυχρής ροής σε ζεστή ροή, καθώς και τα κοινά σενάρια χρήσης και εφαρμογής των StateFlow και SharedFlow Στη συνέχεια, θα επικεντρωθούμε σε πολλά πρακτικά παραδείγματα για να εξετάσουμε άλλα κοινά σενάρια ροής.

Χειριστείτε πολύπλοκη, χρονοβόρα λογική

Συνήθως κάνουμε κάποια πολύπλοκη χρονοβόρα λογική, την επεξεργαζόμαστε σε ένα δευτερεύον νήμα και, στη συνέχεια, μεταβαίνουμε στο κύριο νήμα για να εμφανίσουμε τη διεπαφή χρήστη και το Flow υποστηρίζει την εναλλαγή νημάτων και το flowOn μπορεί να βάλει προηγούμενες λειτουργίες στο αντίστοιχο υπονήμα για επεξεργασία. .
Εφαρμόζουμε ένα τοπικό ανάγνωσηςAssetsκάτω από τον κατάλογοperson.jsonαρχείο και αναλύστε το,jsonΠεριεχόμενα του αρχείου:

{
  "name": "ddup",
  "age": 101,
  "interest": "earn money..."
}
  • 1
  • 2
  • 3
  • 4
  • 5

Στη συνέχεια, αναλύστε το αρχείο:

fun getAssetJsonInfo(context: Context, fileName: String): String {
    val strBuilder = StringBuilder()
    var input: InputStream? = null
    var inputReader: InputStreamReader? = null
    var reader: BufferedReader? = null
    try {
        input = context.assets.open(fileName, AssetManager.ACCESS_BUFFER)
        inputReader = InputStreamReader(input, StandardCharsets.UTF_8)
        reader = BufferedReader(inputReader)
        var line: String?
        while ((reader.readLine().also { line = it }) != null) {
            strBuilder.append(line)
        }
    } catch (ex: Exception) {
        ex.printStackTrace()
    } finally {
        try {
            input?.close()
            inputReader?.close()
            reader?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return strBuilder.toString()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Η ροή διαβάζει αρχεία:

/**
 * 通过Flow方式,获取本地文件
 */
private fun getFileInfo() {
    lifecycleScope.launch {
        flow {
            //解析本地json文件,并生成对应字符串
            val configStr = getAssetJsonInfo(this@MainActivity2, "person.json")
            //最后将得到的实体类发送到下游
            emit(configStr)
        }
            .map { json ->
                Gson().fromJson(json, PersonModel::class.java) //通过Gson将字符串转为实体类
            }
            .flowOn(Dispatchers.IO) //在flowOn之上的所有操作都是在IO线程中进行的
            .onStart { Log.d("ddup", "onStart") }
            .filterNotNull()
            .onCompletion { Log.d("ddup", "onCompletion") }
            .catch { ex -> Log.d("ddup", "catch:${ex.message}") }
            .collect {
                Log.d("ddup", "collect parse result:$it")
            }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Τελικό αρχείο καταγραφής εκτύπωσης:

2024-07-09 22:00:34.006 12251-12251 ddup com.ddup.flowtest D onStart 2024-07-09 22:00:34.018 12251-12251 ddup com.ddup.flowtest D collect parse result:PersonModel(name=ddup, age=101, interest=earn money...) 2024-07-09 22:00:34.019 12251-12251 ddup com.ddup.flowtest D onCompletion

Αιτήματα διεπαφής με εξαρτήσεις

Συχνά συναντάμε αιτήματα διεπαφής που εξαρτώνται από τα αποτελέσματα ενός άλλου αιτήματος, τα οποία είναι τα λεγόμενα ένθετα αιτήματα, θα εμφανιστεί η κόλαση της επιστροφής κλήσης.

lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        //将两个flow串联起来 先搜索目的地,然后到达目的地
        viewModel.getTokenFlows()
            .flatMapConcat {
                //第二个flow依赖第一个的结果
                viewModel.getUserFlows(it)
            }.collect {
                tv.text = it ?: "error"
            }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Συνδυάστε δεδομένα από πολλαπλές διεπαφές

Ποιο είναι το σενάριο του συνδυασμού δεδομένων από πολλαπλές διεπαφές, για παράδειγμα, ζητάμε πολλαπλές διεπαφές και στη συνέχεια συνδυάζουμε τα αποτελέσματά τους για να τις εμφανίσουμε ομοιόμορφα ή να τις χρησιμοποιήσουμε ως παραμέτρους αιτήματος για μια άλλη διεπαφή;
Το πρώτο είναι να ζητήσετε ένα προς ένα και μετά να τα συγχωνεύσετε.
Ο δεύτερος τύπος είναι να κάνετε αίτημα ταυτόχρονα και, στη συνέχεια, να συγχωνεύσετε όλα τα αιτήματα.
Προφανώς, το δεύτερο αποτέλεσμα είναι πιο αποτελεσματικό.

//分别请求电费、水费、网费,Flow之间是并行关系
suspend fun requestElectricCost(): Flow<SpendModel> =
    flow {
        delay(500)
        emit(SpendModel("电费", 10f, 500))
    }.flowOn(Dispatchers.IO)

suspend fun requestWaterCost(): Flow<SpendModel> =
    flow {
        delay(1000)
        emit(SpendModel("水费", 20f, 1000))
    }.flowOn(Dispatchers.IO)

suspend fun requestInternetCost(): Flow<SpendModel> =
    flow {
        delay(2000)
        emit(SpendModel("网费", 30f, 2000))
    }.flowOn(Dispatchers.IO)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Αρχικά, προσομοιώσαμε και ορίσαμε πολλά αιτήματα δικτύου στο ViewModel και, στη συνέχεια, συγχωνεύσαμε τα αιτήματα:

lifecycleScope.launch {
    val electricFlow = viewModel.requestElectricCost()
    val waterFlow = viewModel.requestWaterCost()
    val internetFlow = viewModel.requestInternetCost()

    val builder = StringBuilder()
    var totalCost = 0f
    val startTime = System.currentTimeMillis()
    //NOTE:注意这里可以多个zip操作符来合并Flow,且多个Flow之间是并行关系
    electricFlow.zip(waterFlow) { electric, water ->
        totalCost = electric.cost + water.cost
        builder.append("${electric.info()},n").append("${water.info()},n")
    }.zip(internetFlow) { two, internet ->
        totalCost += internet.cost
        two.append(internet.info()).append(",nn总花费:$totalCost")
    }.collect {
        tv.text = it.append(",总耗时:${System.currentTimeMillis() - startTime} ms")
        Log.d(
            "ddup",
            "${it.append(",总耗时:${System.currentTimeMillis() - startTime} ms")}"
        )
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

αποτέλεσμα λειτουργίας:
flow11.png
Βλέπουμε ότι ο συνολικός χρόνος που δαπανήθηκε είναι ουσιαστικά ο ίδιος με τον μεγαλύτερο χρόνο αιτήματος.

Προφυλάξεις κατά τη χρήση του Flow

πολλαπλούςFlowδεν μπορεί να τοποθετηθεί σε έναlifecycleScope.launchΠήγαινε μέσαcollect{}, γιατί μπαίνονταςcollect{}Αντίστοιχα με έναν άπειρο βρόχο, η επόμενη γραμμή κώδικα δεν θα εκτελεστεί ποτέ εάν θέλετε να γράψετε alifecycleScope.launch{}Πηγαίνετε μέσα, μπορείτε να το ενεργοποιήσετε ξανά από μέσαlaunch{}Η υπο-κορουτίνα εκτελείται.
Παράδειγμα σφάλματος:

lifecycleScope.launch {
    flow1
        .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
        .collect {}

   flow2
        .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
        .collect {}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Σωστός τρόπος γραφής:

lifecycleScope.launch {
    launch {
       flow1
            .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
            .collect {}
    }

    launch {
      flow2
            .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
            .collect {}
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Συνοψίζω

Από τον κύκλο ζωής του Flow, εισαγάγαμε τη σωστή στάση χρήσης της ροής για την αποφυγή σπατάλης πόρων, στη μετατροπή της συνηθισμένης ψυχρής ροής σε θερμή ροή, στο StateFlow που αντικαθιστά το LiveData και το πρόβλημα κολλητότητάς του και στη συνέχεια λύσαμε το πρόβλημα κολλητικότητας μέσω Το SharedFlow, και στη συνέχεια στα κοινά σενάρια εφαρμογών, και, τέλος, οι προφυλάξεις για τη χρήση του Flow, βασικά καλύπτουν τις περισσότερες από τις δυνατότητες και τα σενάρια εφαρμογής του Flow Αυτό είναι επίσης το τελευταίο κεφάλαιο της εκμάθησης ροής.
Δεν είναι εύκολο να το δημιουργήσεις, αλλά είναι δύσκολο να σου αρέσειΚάντε like, συλλέξτε και σχολιάστε για να ενθαρρύνετε
Άρθρο αναφοράς
Reactive προγραμματισμός Kotlin Flow, StateFlow και SharedFlow

Kotlin |. Διάφορα σενάρια χρήσης της ροής δεδομένων