kdnakt blog

hello there.

第52回Kotlin dojoを開催した

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

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

 

前回の様子はコチラ↓

kdnakt.hatenablog.com

 

 

[第52回の様子]

2021/2/17に第52回を開催した。

 

参加メンバーは自分をいれて5名。1ヶ月に一度くらいふらりと参加してコメントをくれるメンバーもいるので、やはり毎週継続していて良かったなと思う。

 

勉強会本編の内容としては、前回に続いてIntroduction to Coroutines and Channelsハンズオンを進めた。第3章の2つめの演習問題のタスクを実施して第3章を終わった。

 

[学んだことや疑問点]

  • Introduction to Coroutines and Channels: 3. Using callbacks
    • 前回はコールバックを利用して、ローディング処理を別スレッドに移し、UI処理を止めない修正をした。
    • 今回はさらに一歩進んで、Retrofitを利用してローディング処理を並列で実行するように修正していく。 

square.github.io

    • Call.enqueue()を利用すると並列でHTTPリクエストを実行できるらしい。
    • 以下が今回のタスクのコード。リクエストが非同期で処理されているのに、同期的にupdateResults()を呼び出しているため、結果表示がうまくいかなくなっている
fun loadContributorsCallbacks(service: GitHubService, req: RequestData, 
                              updateResults: (List<User>) -> Unit) {
    service.getOrgReposCall(req.org).onResponse { responseRepos ->
        logRepos(req, responseRepos)
        val repos = responseRepos.bodyList()
        
        val allUsers = mutableListOf<User>()
        for (repo in repos) {
            service.getRepoContributorsCall(req.org, repo.name).onResponse { responseUsers ->
                logUsers(repo, responseUsers)
                val users = responseUsers.bodyList()
                allUsers += users
            }
        }
        // TODO: Why this code doesn't work? How to fix that?
        updateResults(allUsers.aggregate())
    }
}
    • Callの中に、JavaThread.join()のような待機用メソッドがあれば、と思って確認してみたが、それらしいものは見当たらなかった。
    • ローディング処理が終わったリポジトリ数を数えて、事前に取得しているリポジトリ数と一致したら結果を更新する、という方針で実装するとよさそう
    • とそこまで考えたところで、参加者の1人が、タスクのコードが書かれているsrc/tasks/Request3Callbacks.ktファイルに、java.util.concurrent.atomic.AtomicIntegerがインポートされているのを発見した
    • これが使えそう、ということで実装したところ以下のようになった
fun loadContributorsCallbacks(service: GitHubService, req: RequestData, 
                              updateResults: (List<User>) -> Unit) {
    service.getOrgReposCall(req.org).onResponse { responseRepos ->
        logRepos(req, responseRepos)
        val repos = responseRepos.bodyList()
        val allUsers = mutableListOf<User>()
        val counter = AtomicInteger(0) // 追加した行
        for (repo in repos) {
            service.getRepoContributorsCall(req.org, repo.name).onResponse { responseUsers ->
                logUsers(repo, responseUsers)
                val users = responseUsers.bodyList()
                allUsers += users
                // 追加したif文
                if (counter.incrementAndGet() == repos.size) {
                    updateResults(allUsers.aggregate())
                }
            }
        }
    }
}
    • 模範解答を確認すると、なぜか2つ模範解答がある
    • 1つ目のコードは、あきらかにHTTPリクエストの実行完了順を意識していないコードで、参加者全員で首をひねりながら読み進めると、「However, this code is also incorrect. 」の文章が。そりゃそうですよね……。
    • 2つ目のコードは、我々の解答と一致していたので、一安心。
    • 参考として、GitHubにはRxJavaを利用した解答もあるらしい。こちらは時間が足りず未確認。

 

[まとめ]

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

AtomicIntegerとかひさしぶりに使った……。

次回はいよいよsuspend関数のお出まし。楽しみ!

 

今週の進捗は以下のプルリクエストにまとまっている。

github.com