DroidKaigi2018公式アプリのソースコードを読む

先日2018/2/8～2/9に開催された[DroidKaigi2018](https://droidkaigi.jp/2018/)の公式アプリのソースコードが[githubで公開されています。](https://github.com/DroidKaigi/conference-app-2018)
例年、参加者達でDroidKaigi公式アプリを開発するのが恒例行事になっていて、今年もAndroidの最新技術を集めた感じで開発されました。
日本のAndroid最先端の人達が共同で開発したアプリなので、コードを読むだけで最先端技術のキャッチアップができます:muscle:
ぜひ一度クローンして読んだり試してみるのをオススメします。

とはいえ、かなり最先端技術ばかり集まっているので、読み進めるのが辛い場面もあるかと思います。(私です)
そこで今回はDroidKiagi2018の公式アプリを読み進めるときに私が詰まってしまった部分を解説したいと思います。

合わせて読みたい記事
[DroidKaigi 2018 App Architecture by takahirom](https://speakerdeck.com/takahirom/droidkaigi-2018-app-architecture)
[DroidKaigi アプリの内部を見る by tatsuhama50 ](https://www.slideshare.net/kenichitatsuhama/droidkaigi-88921455)
[Contributeしてない俺がDroidKaigiアプリについてゴニョゴニョ話す会](https://github.com/operando/Notes/blob/master/Souzoh_Android_Talk_2/Contribute%E3%81%97%E3%81%A6%E3%81%AA%E3%81%84%E4%BF%BA%E3%81%8CDroidKaigi%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%82%B4%E3%83%8B%E3%83%A7%E3%82%B4%E3%83%8B%E3%83%A7%E8%A9%B1%E3%81%99%E4%BC%9A.md)


# DroidKaigiアプリのソースコードでかい

いきなりなんですが、公式アプリのgithubのコードはけっこうでかいです。
アプリ自体かなり多機能なので、試しに読んでみるにはかなり大きい感じに仕上がってます。
これをいきなり読み進めていくのは辛いのですが・・:confounded:

# simplifyブランチ

しかし、なんと！そういう人のために簡略化したリポジトリが作成されています！
さすが世界の@takahiromさん:heart_eyes:
しかもFlux版とかcoroutine版も作成されていて、ほんとすごい！
とりあえず、このありがたい簡略版の[simplifyブランチ](https://github.com/DroidKaigi/conference-app-2018/tree/simplify)をクローンしてきましょう。

# build.gradleどこ？

さてさっそくの詰みポイントですが、DroidKaigi2018の公式アプリのbuild.gradleはKotlin Scriptで書かれています。
なのでbuild.gradleはありません。build.gradle.ktsがそれの代わりです。
最新のgradleを使っていれば自動的に読み込まれるはずですが、もし読み込まれなかった場合はbuildファイル名を指定するためsettings.gradleに1行追加してやるとよいです。

```settings.gradle
rootProject.buildFileName = 'build.gradle.kts'
```

(参考:[Kotlin を Gradle でビルドする minimal サンプル](http://etc9.hatenablog.com/entry/2017/08/03/190506))

# build.gradle.ktsを読む

まあ初見でもファイル名もそのまんまだし、中身はKotlinなので、なんとなく雰囲気で読んでみたら「あ～、なるほどなるほど」ってなると思います:sweat_smile:
ちなみに雰囲気で読んでいた私はdependenciesで詰まりました:innocent:

`implementation(Depends.Kotlin.stdlib)`
あれ～、なんかよく知らない書き方だ～。どうなってるの～？:rolling_eyes:

# buildSrc

`implementation(Depends.Kotlin.stdlib)`の答えはbuildSrcにあります。
リポジトリをよく見るとルートにbuildSrcってフォルダがあると思います。
実はgradleはビルド時にbuildSrcフォルダ内も走査して、クラスパスに追加してくれます。
[Build sources in the buildSrc project](https://docs.gradle.org/current/userguide/organizing_build_logic.html#sec:build_sources)

DroidKaigi2018のブランチではこれを使ってマルチモジュールの依存関係をスッキリさせようってことをやっているのです。
[Kotlin + buildSrc for Better Gradle Dependency Management](https://handstandsam.com/2018/02/11/kotlin-buildsrc-for-better-gradle-dependency-management/)

# Depends.kt ＆ Versions.kt

というわけでbuild.gradle.ktsに出てくるDependsとかはbuildSrc配下のここに定義されているやつです。
https://github.com/DroidKaigi/conference-app-2018/blob/master/buildSrc/src/main/kotlin/Depends.kt

同じようにVersionsも定義しているクラスがあるので、両方を見るとよいと思います。
https://github.com/DroidKaigi/conference-app-2018/blob/master/buildSrc/src/main/kotlin/Versions.kt

正直、この二つを読むだけでも最近のイケてるライブラリ達はこれなのか～って雑な発見があるし、お！こんなのあるのか！って発見もあって楽しいので、ぜひこれだけでも読んでみてください。
知らないライブラリ出てきたらそのままググってみることをオススメします。
それだけでもかなり勉強になります。

また、Kotlin Scriptでbuild.gradleを書いたことがある人はけっこう少ないと思いますが、もしKotlin Scriptでbuild.gradleを書きたい！ってなったら、すごくよい教科書だと思うのでこのbuild.gradle.ktsを隅々まで読むといいと思います！

# 読んでみていいなと思ったところ

ここまでで、AndroidStudioでプロジェクトを読み込めたのでだいたいの雰囲気で読めるようになると思います。
AndroidStudioは頭がいいので、読み手の知識不足も全部補ってくれます。:blush:
すみずみまで読み込んだわけではないのですが、読んでみていいなと思ったところを少し紹介したいと思います。

## Kotshiを使っている

[Kotshi](https://github.com/ansman/kotshi)はKotlinでJsonを扱うためのライブラリです。
AndroidではGsonが使われることが多かったという印象ですが、KotlinとGsonはあまり相性が良くなかったです。
参考:[Kotlinと相性が良いMoshiのkotlin extensionを使う](https://qiita.com/egugue/items/f1f35c250f7a25768751)

そこでKotlinとも相性が良くて、しかもretrofitと相性のよいMoshiがあったのですが、リフレクションを使っていて処理が重いという問題もありました。
~~それらの問題を全部解決するライブラリをjake神が作りました。さすがjake神~~
↑間違えてました。jake神がこういうのあったらいいな→実はもうあるぜって紹介しただけで、Kotshiの作成者は[ansman](https://github.com/ansman)さんでした:pray:(勘違いばっかりだな)
Kotshiについては↓の記事が詳しいです。
パフォーマンスの違いも実験されているので、ぜひ読んでみてください。
[kotlinでjsonパースはkotshiがいいかも](http://blog.kaelae.la/entry/2018/01/29/203000)

ちなみにKotshiの正体はMoshiのJsonAdapterなので、[READMEにも書かれているように](https://github.com/ansman/kotshi#usage)MoshiにKotshiのadapterを入れてやらないとただのMoshiになってしまいます。(この辺を勘違いしてて、Kotshi使ってるつもりでMoshi使ってたマンが私です:innocent:)

## APIレスポンスをMapperで変換してる

DroidKaigiアプリではサーバーレスポンスをクライアントで使いやすいように加工してからRoomのDBに保存しています。

* API定義:[DroidKaigiApi.kt](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/api/DroidKaigiApi.kt)
* レスポンスモデル:[Response.kt](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/api/response/Response.kt)
* Roomに保存する:[SessionDataRepository.kt](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/repository/SessionDataRepository.kt#L67)
* RoomのDB:[SessionRoomDatabase](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/db/SessionRoomDatabase.kt)

こんな感じで、RoomのDBにサーバーレスポンスを入れるときに[クライアントで使いやすいように変換](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/db/SessionRoomDatabase.kt#L37)してから保存しています。
この変換処理はKotlinの[拡張関数](https://kotlinlang.org/docs/reference/extensions.html#extension-functions)で書かれていて、[SessionDataMapperExt.kt](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/api/response/mapper/SessionDataMapperExt.kt)にそれぞれのデータをうまいこと加工する処理が書かれています。

クライアント側のデータ構造って画面仕様が変わるとガラっと変わるし、前は必要だったものが不必要になったりするしで、けっこう変わります。
一方、サーバー側のデータ構造ってDB構造と関係しているのもあって、そんなにコロコロ変えられないです。
そこで、簡単な変更ならクライアント側でMapper使えばサクッと対応できちゃいますね。
Mapperの外から見るとAPI仕様がクライアントに合わせて変わったように見えるのでコードもスッキリします。
もちろんクライアントの要望に合わせてサーバーAPIも変えていくって場面もあると思いますし、いろんなところでクライアント側で吸収するようになったらAPIを見直す時期なのかもしれないですね:wink:

## 時刻周りのサーバーレスポンス変換をJsonAdapterで解決してる

MoshiではAPIレスポンスをJsonに変換するときのJsonAdapterをカスタムして追加できます。
それを利用して[InstantAdapter](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/data/api/response/mapper/InstantAdapter.kt)というJsonAdapterでAPIレスポンスの時刻系のデータをクライアントで使いやすいように変換しています。
このInstantAdapterを適用させるには、[こんな感じ](https://github.com/DroidKaigi/conference-app-2018/blob/simplify/app/src/main/java/io/github/droidkaigi/confsched2018/di/NetworkModule.kt#L43)でRetrofitに追加したらいいです。

# まとめ

* DroidKaigi2018の公式アプリには最新技術がいっぱい
* [simplifyブランチ](https://github.com/DroidKaigi/conference-app-2018/tree/simplify)あるぞ！
* [Depends.kt](https://github.com/DroidKaigi/conference-app-2018/blob/master/buildSrc/src/main/kotlin/Depends.kt)を読むだけでもいろんな発見があるのでぜひ！