はじめに
本記事は主に初心者向けの記事になります。
-> Androidで非同期処理するにはコルーチンを使えばいいみたい。
-> 最近はflow, channelが熱いとか。
-> 基本的な使い方はわかったけど実際どんな感じで使えばいい?
って感じの方向けです。(自分の備忘録も兼ねてます)
#今回やること
以下の動作をする機能を実装します。
- APIリクエストを投げる
- レスポンスが返ってくるまでローディングを表示
- APIリクエストエラーハンドリング
#実装
成功、失敗、ローディングなどの状態を管理するために、リクエストのresultなどをラッピングするクラスを作って定義します。
sealed class State<T> {
class Loading<T> : State<T>() // ローディング中
data class Success<T>(val data: T) : State<T>() // 成功時
data class Failure<T>(val e: Throwable) : State<T>() // 失敗時
}
このState
クラスを使ってflowで流れてくるデータを判別して各々に処理を書きます。
今回はダミーのweather flow
データを取得するweatherAPIをコールします。
val repository = WeatherRepository()
repository.getCurrentWeather()
.flowOn(Dispatchers.Default) // これより上のflowの実行コンテキストの指定
.onEach { result ->
when (result) {
is State.Loading -> {
showLoadingProgress() // loadingDialogとかの表示
}
is State.Success -> {
// do something
Log.d("### flow result ###", result.data.toString())
hideLoadingProgress() // loadingDialogとかを消す
}
is State.Failure -> {
// do something
Log.e("### flow result ###", result.e.stackTraceToString())
hideLoadingProgress() // loadingDialogとかを消す
}
}
}
.launchIn(lifecycleScope) // 動作するコルーチンスコープの指定
result
からAPIのレスポンスを取得できます。
onEach
やlaunchIn
は書き方が少し違うだけで以下と同じです。こっちの方がネストがちょっと浅くなって個人的に好き。
CoroutineScope.launch{
flow.collect { result ->
...
}
}
内容的にはflowで流れてきたデータをラッピングクラスのインスタンスで分けて処理をしていくだけです。
repository
に関してはこんな感じになってます。
なお、requestWeatherApi
はsuspend
関数です。
class WeatherRepository(
val weatherDataSource: WeatherDataSource = WeatherDataSource()
) {
fun getCurrentWeather(): Flow<State<Weather>> = flow {
val result = try {
val weather = weatherDataSource.requestWeatherApi()
State.Success(weather)
} catch (e: Throwable) {
State.Failure(e)
}
emit(result)
}.onStart {
// collectされた時にローディング状態を最初に流す
emit(State.Loading())
}
}
try-catch
でエラーをキャッチし、flowのオペレータの一つであるonStart
でローディング状態を流します。
onStart
を使えばflowに最初に流すデータを指定することができます。
flowに流れるデータは
-
State.Loading()
(collectされると同時にemit) -
State.Success(weather)
(リクエストが完了したらemit)
になります。
WeatherDataSource
はこんな感じの3秒待ってデータを吐き出すようなものです。今回はこれをAPIに見立てて実装しています。
class WeatherDataSource {
suspend fun requestWeatherApi(): Weather {
delay(3000)
return Weather("sunny", Date())
}
}
data class Weather(
val weather: String,
val date: Date
)
#おわり
短いですがこれにて終わりになります。
Utillクラスとかを作るともっとボイラープレート減ってよくなりそう。。。
rx java使いにとってはflowはとってもなじみやすくなりそうですね。自分はまだまだですが。
オペレータとかも一部previewとかexperimentalとかありますがstableになってきたりしているので今年から本格的に導入されていく気がする。