1
2

More than 3 years have passed since last update.

【kotlin】flowで非同期処理やローディングをいい感じにやりたい

Last updated at Posted at 2021-01-28

はじめに

本記事は主に初心者向けの記事になります。

-> 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のレスポンスを取得できます。

onEachlaunchInは書き方が少し違うだけで以下と同じです。こっちの方がネストがちょっと浅くなって個人的に好き。

CoroutineScope.launch{
    flow.collect { result ->
        ...
    }
}

内容的にはflowで流れてきたデータをラッピングクラスのインスタンスで分けて処理をしていくだけです。

repositoryに関してはこんな感じになってます。
なお、requestWeatherApisuspend関数です。

WeatherRepository.kt
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に流れるデータは
1. State.Loading() (collectされると同時にemit)
2. State.Success(weather) (リクエストが完了したらemit)
になります。

WeatherDataSourceはこんな感じの3秒待ってデータを吐き出すようなものです。今回はこれをAPIに見立てて実装しています。

WeatherDataSource.kt
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になってきたりしているので今年から本格的に導入されていく気がする。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2