会について
Kotlinについてワイワイする会に参加してきました。気になっていたのですが今回が初参加。
もくもく会に参加したいので予定あけておかないと。
会場を提供してくださった、アプリボットさんのブログにあるように、談義ののちに懇談会となりました。
談義
昨年に転職して以来、Kotlinでワイワイプロダクト作っている中で考えていることを話してきました。 資料はこちら。
Kotlinを既存プロダクトに導入・導入を推進する中で、結構重要だった感じがしたことを列挙しつつ補足しつつ、な談義をしました。 が、会場では喋りすぎたり喋り足りなかったりするところもあったので、簡単に文章で補足しておきたいなと思います。
1. Kotlin std lib
Kotlinのstdライブラリの選定について、Androidの場合には(一応) minSDK
を見ておきたい。
2019年、多くのアプリでは minSDK
が 21
以上だと思うので std-jdk7
で良さそう。ただ、そうでない場合に std
か std-jdk7
かは、Android DeveloperのAPIを見ながら考えた方が良さそうという話でした。
もちろん、ライブラリを作っている時には minSDK
に合わせた適切なバージョンを選ぶ必要があります。
2. Rx-Streamの置き換え
既存プロダクトでは、Stream APIの利用やHTTTP GETの非同期処理にRx-Streamを使うことが多いと思います。 Kotlin導入の手順としてはまず、Kotlin標準のList操作へRxの処理を置き換える。HTTPレスポンスをKotlin Coroutinesへ置き換えてる、といったあたりから始めるのがよいのでは、という提案です。
この置き換えを行っていくと"Rx-Streamでなければ対応できない"処理が見つかりやすくなるので、プロダクトのどこにRxが必要なのかがわかりやすくなります。 RxでなければならないところをRxで、それ以外をKotlinの標準APIで実装することで、コードの見通しがグッとよくなります。
3. Enum + Kotlin Extension
Androidアプリを書いていると、時たま以下のようなEnumを作成するかと思います。
public enum UserType { FREE("free", R.string.plan_free), PREMIUM("premium", R.string.plan_premium); public final String code; public final int stringResId; UserType(String code, int stringResId) { this.code = code; this.stringResId = stringResId; } }
ただ、こういったenumでは下記のような問題が生じやすいかと思います。
- 要素の追加/削除がしにくい
- enumによるリソース/設定値の追加/削除がしにくい
- 「とりあえずnull」を入れるとNullableとして他の全てのケースを考慮しなければならくなる
R.string.hoge
の文字列が長いことによる改行が発生する
そこで、Kotlinならば下記のように2つに分離して書けば良いのでは、と考えています。
enum UserType(val code: String) { FREE(“free”), PREMIUM(“premium”) } fun UserType.stringResId() = when(this) { UserType.FREE -> R.string.plan_free USerType.PREMIUM -> R.string.plan_premium else -> R.string.other }
このケースでは、enumが"列挙"に終始しています。このケースでは何か別の要素を追加する際に、UserTypeの中では特に問題が発生しません。
また stringResId
ではない別の要素 (ButtonColor
など)をたす場合に、 stringResId
のことを考慮する必要はありません。
この対応は、既存のプロダクトにあるコードをリファクタリングするだけなので、簡単に取り組むことができると思われます。
4. 2つのKotlin Data Class
主にアプリの内部で利用するロジック用のData Classと、サーバーAPIのレスポンスをパースするためのData Classを分離してみると便利では、という話です。
というのもServer API Response
と JSON Parsed Class
、In-App Data
とそれぞれを呼称すると次のような関係があると思います。
Server API Response | JSON Parsed Class | In-App Data | |
---|---|---|---|
目的 | サーバー側ロジックのアウトプット | APIレスポンスをJVM上で扱いやすくする | アプリ内ロジックに適した形でデータを表現する |
特徴 | サーバー側ロジックの変化による、追加/削除に対応しやすい | APIレスポンスの変更に対応しやすい | アプリロジックに合わせて変更しやすい |
KotlinのData Classは作成しやすく、作成すればequalやhasCodeと言った比較に必要なメソッドを自動で生成してくれます。 この特徴を生かし、少々コードは増えますが2種類のData Classを使い分けてみると、ロジックがすっきりしてくるのでは、という話です。
5. Coroutines + Result
昨年末に書いた下の記事の内容です。
Coroutinesの結果をResult
でハンドリングし、同時にLiveData
で通信状態をActivity/Fragmentへ伝えていくと処理が書きやすいです。Kotlin CoroutinesによってIOスレッドとUIスレッドの接続はできますが、操作するUIパーツがAndroidのライフサイクル的に安全かどうかは保証できません。LiveData
を使うことでこの問題はカバーでき、難解なスレッド操作処理を手で書かなくてよくなります。
Result
のerror
がThrowable
をハンドリングすることに合わせ、自ViewModel
からThrowable
をActivity/Fragmentへ伝播させることが最近多くなってきました。賛否があるかもしれませんが、Throwable
の型によって起きたエラーが ネットワーク起因
サーバー状態起因
アプリロジック起因
なのか判定しなければ、ユーザーに次の操作を促せない状態に複数遭遇しているのがきっかけです。
このThrowable
については、もう少し考えをまとめておきたいなと思います。
おわりに
談義は10分ほどでしたが、その後には多くの方とKotlinの話をすることができました。とても楽しかったです。 まだまだKotlinの勉強するぞー。