Continuity is The Father of Success

Androidアプリとかゲームとか。毎日続けてるものについて。

Kotlin Fest Reject Conference 2019 [非公式]でLTしてきました

だいぶ遅くなってしまった。。。

いつもの感じでLTしてきました。 台風の日だったので参加できるかちょっと不安でしたが、なんとかなったのでよかった!

dena.connpass.com

参加の経緯

Kotlin Fest 2019の懇親会にて、Kotlin Fest Reject Conf 2019の紹介があったので参加を決めました。 Kotlin Fest 2019には一つ出してダメだったネタがあったので、ちょうど良いやと思っていたので軽い感じで参加。

その後はちょこちょこ資料を作りながら当日に備えていたのですが、会の当日がまさかの台風15号の直撃後になってしまいました。 会は開かれるのか、というか自分はそもそも会場に迎えるのか……などと考えながらリモートワークをしていたのを覚えています。

17時半ごろから電車移動を開始したところ、案外問題なく会場までたどり着けたのでよかったです。

会の雰囲気

Reject Confということで、肩の力が抜けたLTが多く気軽にしかし深い内容を聞けたように思います。 個人的に面白いなーと思ったのは、タケハタさんの「サーバサイドKotlinでgRPCをやってみよう」。

speakerdeck.com

LT内でも触れられていますが、Kotlin Fest 2019でいくつかあった”サーバーサイドKotlin + GraphQL”に対する”サーバーサイドKotlin + gRPC”! 普段はAndroidアプリを作成しているのでこれらの技術選定に関わることはないのですが、もしgRPCを使うことになったらアプリは何がよくなるのかなーと考えながら聞いてました。

paniniさんがgRPCのKotlin codeを生成するライブラリを書いているので、導入すればアプリサイドはフルKotlinで実装できる気も👀

github.com

LT内容

業務アプリを色々とKotlinに書き換え、途中で詰まったところについて話しました。 ささっと進めばいいかと思ったけれど、5分では詰め込みすぎたかもと反省。

speakerdeck.com

スライドでは足りなかった箇所を補足します。

2つのインスタンス化メソッドとNonNull

例えば、下記のようなクラスがあったとします。

ケース1

public class HogeFragment extends Fragment {

    public static HogeFragment newInstance(Fuga fuga) {}

    public static HogeFragment newInstance(Fuga fuga, Piyo piyo) {}

}

この時、

ケース2

public class HogeFragment extends Fragment {

    public static HogeFragment newInstance(@NonNull Fuga fuga) {}

    public static HogeFragment newInstance(Fuga fuga, Piyo piyo) {}

}

ケース3

public class HogeFragment extends Fragment {

    public static HogeFragment newInstance(@Nullable Fuga fuga) {}

    public static HogeFragment newInstance(@NonNull Fuga fuga, @NonNull Piyo piyo) {}

}

であることがあります。 (ありました)

ケース1では piyo は引数から明らかに nullable です。 ですが fuga はどうでしょうか? アノテーションがついていないJavaクラスからのコンバートでは、処理を一通り追わないと non-null とすることはできません。

ケース2が一番辛いケースとなります。 サンプルコードでは引数が2つなので違和感に気付きやすいのですが、実際のコードでは引数が3~4あることがあるためです。 この場合、かなり網羅的なテストを行わないとミスに気づくことが難しくなってしまいます。

対応は、複数のインスタンス化メソッドを持つクラスにおいては、インスタンス化時に引数とした値の利用箇所を全て確認する方針とするしかありません。

共通データクラス(神データクラス)

特にJSONのパース処理がつらいので、seald classを使ってちゃんとパースしたいクラスの形にしましょう、という話。 Swaggerなどが整備されているならば、Swaggerの通りにnon-nullとnullableを表現したデータクラスを0ベースで作った方が、早いかもしれません。

使い回されるデータクラス

「便利だから」との理由で似たインタフェースを持つAPIへのリクエストや、外部SDKからのコールバックをラップしたクラスが生成されていることがあります。 このため、複数のクラスから(特に理由もなく)利用されているクラスはnullの扱いを慎重にする必要があります。 とりわけSDKからのコールバックをラップしている場合、特定のSDKを利用した時のみクラッシュするといった、検証しにくい不具合を引き当てることがあります。

onActivityResult

Activityクラスの onActivityResult には @NonNull@Nullable アノテーションがついていません。 このため、Convert to KotlinするとNonNullの引数として定義されてしまいます。 (また、親クラスにアノテーションがついていないためAndroidStudioのlint機能で対応することもできません。)

Cross Reference: /frameworks/base/core/java/android/app/Activity.java

リネーム機能を利用してあらかじめアノテーションを付与したり、コンバート後にリネームする対応が必要となります。 特に、第3引数の Intent がnullとなってクラッシュするケースが多いように思います。

R8

OkHttp4系やKotlin Coroutines 1.3系のために、R8対応しましょう。 Gsonからの移行がつらいです。

終わりに

息の長いプロジェクトからの知見のため、あんまり響く人はいないかもなーと思っています。 が、どなたかの助けになればいいかと思っています。おわり。