kdnakt blog

hello there.

第59回Kotlin dojoを開催した

今週は第59回を開催しました!

引き続きKotlin Hands-onをすすめています。

 

前回の様子はコチラ↓

kdnakt.hatenablog.com

 

 

[第59回の様子]

2021/4/8に第59回を開催した。

水曜日は予定があったので(結局なくなったけど)、木曜日の開催。

 

参加メンバーは自分をいれて4名。いつもと違う曜日だからか、2021年初参加のメンバーも!

今回も自分はナビゲータ役で参加した。

 

勉強会本編の内容としては、ひき続きIntroduction to Coroutines and Channelsハンズオンを進めた。第7章 Showing progressのタスクの実装をおえた。動作確認はできなかったので来週……。

 

[学んだことや疑問点]

  • Introduction to Coroutines and Channels: 7. Showing progress
    • これまでの実装では、一部のリクエストについてレスポンスが返ってきているにもかかわらずUIの更新処理は最後にまとめて1回だけ行われていた
    • これを途中経過を表示するように修正していく
    • 途中経過を更新するための更新ロジックをコールバックとして以下のように渡す
// 呼び出し側
launch(Dispatchers.Default) {
    loadContributorsProgress(service, req) { users, completed ->
        withContext(Dispatchers.Main) {
            updateResults(users, startTime, completed)
        }
    }
}

// 実装する関数
suspend fun loadContributorsProgress(
    service: GitHubService,
    req: RequestData,
    suspend updateResults: (List<User>, completed: Boolean) -> Unit
) {
    // loading the data
    // calling `updateResults` on intermediate states 
}
    • updateResults関数はUIを更新するためにメインスレッド上で実行される必要があるため、withContext()Dispatchers.Mainを指定している
    • 本章のタスクとしてはこのloadContributorsProgress関数を実装するというもの
    • まずは指示通り第4章Using suspend functionsの実装をコピーする。
suspend fun loadContributorsProgress(
    service: GitHubService,
    req: RequestData,
    suspend updateResults: (List<User>, completed: Boolean) -> Unit
) {
    val repos = service
        .getOrgRepos(req.org)
        .also { logRepos(req, it) }
        .body() ?: listOf()

    return repos.flatMap { repo ->
        service
            .getRepoContributors(req.org, repo.name)
            .also { logUsers(repo, it) }
            .bodyList()
    }.aggregate()
}
    • この状態だと、戻り値がUnitJavaでいうvoid)でいいのにreturnしてしまっているので、ここを修正する必要がある
    • コールバックのupdateResults関数に渡すcompletedフラグと似たような処理を3章のUsing callbacksで実装したのを思い出し、そこから処理をコピペすることに。
suspend fun loadContributorsProgress(
    service: GitHubService,
    req: RequestData,
    suspend updateResults: (List<User>, completed: Boolean) -> Unit
) {
    val repos = service
        .getOrgRepos(req.org)
        .also { logRepos(req, it) }
        .body() ?: listOf()

    // Request3Callbacks.ktからほぼコピペ
    val allUsers = mutableListOf<User>()
    val counter = AtomicInteger(0)
    for (repo in repos) {
        val users = service.getRepoContributors(req.org, repo.name)
            .also { logUsers(repo, it) }
            .body() ?: listOf()
        allUsers += users
        if (counter.incrementAndGet() == repos.size) {
            updateResults(allUsers.aggregate())
        }
    }
}
    • この状態だとupdateResults()が一度しか呼ばれず、completedフラグも渡せていないので、if文の条件の部分をcompletedフラグとして利用して以下のように修正する
suspend fun loadContributorsProgress(
    service: GitHubService,
    req: RequestData,
    suspend updateResults: (List<User>, completed: Boolean) -> Unit
) {
    val repos = service
        .getOrgRepos(req.org)
        .also { logRepos(req, it) }
        .body() ?: listOf()

    val allUsers = mutableListOf<User>()
    val counter = AtomicInteger(0)
    for (repo in repos) {
        val users = service.getRepoContributors(req.org, repo.name)
            .also { logUsers(repo, it) }
            .body() ?: listOf()
        allUsers += users
        updateResults(allUsers.aggregate(), counter.incrementAndGet() == repos.size)
    }
}
    • と、ここまでコードを書いたところで時間切れ。動作確認ができなかったので来週はこれを動かすところから。

 

[まとめ]

モブプログラミング・スタイルで、Introduction to Coroutines and Channelsハンズオンを進めた。

前回までにやった内容のおかげで、withContext()とかでやってることの意味が少しわかるようになったかも。

 

今週のプルリクエストはこちら。

github.com