---
# System prepended metadata

title: flow 實際使用的一些場合
tags: [flow, Android, kotlin]

---

# flow 實際使用的一些場合


#### 程式碼範例使用 [之前寫的side Project](https://github.com/JCChiou/bookManagerDB)
1. **Retrofit連線**
    * 下面是在viewModel發送網路請求的方法
    * flow寫法易讀性稍高
    
    使用coroutine寫法
    ```kotlin=
    
    
    private fun getBookApiResponse() {
        val count = getApiCount.value
        viewModelScope.launch(Dispatchers.Main) {
            try {
                val data = count?.let {
                    async(Dispatchers.IO) {
                        BookApi.retrofitService.getAPIData(it)
                    }
                }
                _response.value = data?.await()
                _dataFlag.value = true //送出Api request flag 準備寫入ＤＢ
                Timber.d("try block execute")
            } catch (e: Exception) {
                Timber.d("錯誤訊息=  ${e}")
            }
        }
    
    }
    ```
    
    使用flow寫法
    ```kotlin=
     private fun getBookApiResponse() {
        val count = getApiCount.value
        var data: Mydata? = null
        viewModelScope.launch {
            flow {
                data =
                    count?.let { BookApi.retrofitService.getAPIData(it) }
                    emit(data)
                Timber.d("flow emit")
            }.flowOn(Dispatchers.IO)
                .onStart {
                    Timber.d("flow start")
    //                    throw IllegalThreadStateException("flow error")
                }
                .onCompletion {
                    Timber.d("flow complete")
                    _dataFlag.value = true
    //                    throw IllegalThreadStateException("flow error")
                }
                .catch { ex ->
                    Timber.d("an error ${ex.message}")
                    _dataFlag.value = false
                }
                .collect {
                    _response.value = data
                    Timber.d("flow collect")
                }
        }
    }
    ```
2. **搭配Room使用**
    
    使用coroutine寫法
    ```kotlin=
        private suspend fun getBookListFromDatabase() {
        viewModelScope.launch {
            _myBookList.value = database.getBookList()
            _actionFinsihed.value = false

        }
        selectLock = false
    }
    
    //DAO
    @Query("SELECT * FROM my_bookstore_table")
    fun suspend getBookList() : List<BookStore>?
    
    ```
    
    使用flow寫法(這邊舉例三種寫法)
    A. 
    * **將collect的值賦予liveData，用observer的方式一樣**
    * ***要注意DAO的function不能有suspend聲明***
    [這篇有解答](https://stackoverflow.com/questions/59170415/coroutine-flow-not-sure-how-to-convert-a-cursor-to-this-methods-return-type)
    ```kotlin=
    //初始化->獲取資料庫所有資料
    private fun getBookListFromDatabase() {
        viewModelScope.launch {
            database.getBookList().flowOn(Dispatchers.IO)
                .onStart {
                    Timber.d("getBook list flow start")
                }
                .catch {
                    Timber.d("getBook list flow catch error")
                }
                .onCompletion {
                    Timber.d("getBook list flow completion")
                    selectLock = false
                }
                .collectLatest {
                    _myBookList.postValue(it)
                }
        }
    }
    
    //DAO
    @Query("SELECT * FROM my_bookstore_table")
    fun getBookList() : Flow<List<BookStore>?>
    ```
    B. 
    * **透過liveData Builder建立**
    * **需導入implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0-alpha01"**
    * **直接在UI觀察該函數，因為函數會emit值到liveData**
    ```kotlin=
    fun getBookListFromDatabase2() =  liveData {
        database.getBookList()
            .onStart {
                Timber.d("getBook list flow start")
            }
            .catch {
                Timber.d("getBook list flow catch error")
            }
            .onCompletion {
                Timber.d("getBook list flow completion")
                selectLock = false
            }
            .collectLatest {
                emit(it)
            }
    }
    
    //在UI layer觀察
    bookStoreViewModel.getBookListFromDatabase2().observe(this, Observer { newList ->
            newList?.let {
                adapter.data = it
                //要判斷是否為新增資料 在執行下載圖片，否則直接由快取顯示
                apiBookViewModel.getImgFromUrl(it)
            }
        })
        
    ```
    C.
    * **轉換成asLiveData**
    * 方法跟liveData builder一樣，只是把liveData builder封裝成asLiveData
    ```kotlin=
     fun getBookListFromDatabase() =
            database.getBookList().flowOn(Dispatchers.IO)
                .onStart {
                    Timber.d("getBook list flow start")
                }
                .catch {
                    Timber.d("getBook list flow catch error")
                }
                .onCompletion {
                    Timber.d("getBook list flow completion")
                    selectLock = false
                }.asLiveData()
                
    //UI layer觀察
       bookStoreViewModel.getBookListFromDatabase().observe(this, Observer { newList ->
            newList?.let {
            adapter.data = it
                //要判斷是否為新增資料 在執行下載圖片，否則直接由快取顯示
                apiBookViewModel.getImgFromUrl(it)
            }
        })
    ```
[參考資料]    
[知乎](http://zhuanlan.zhihu.com/p/312468040)
[知乎](https://zhuanlan.zhihu.com/p/347785851)
[gitconnected](https://levelup.gitconnected.com/learn-by-doing-live-data-builder-bridging-coroutines-and-live-data-in-android-6ea9aa597b4)
###### tags: `flow` `kotlin` `Android`