6/22に開催されたKotlin Fest 2024へ参加してきたので、拝聴したセッションについてのメモ書きまとめです。
※聞き間違い、誤解などがあるかもしれません。
今こそ始めたい!Compose Mulitplatform
- Compose Multiplatformを始める際は、公式のプロジェクト作成ウィザードであるKotlin Multiplatform Wizardを使おう
- 環境構築はKdoctorでちゃんとできているか確認しよう
- Android Studioでは共通部分のUI実装をするCommonMainでプレビューの確認ができない
- AndroidMain側で確認するか、FleetIDEを使おう
- 画面遷移はサードパーティのライブラリが基本
- 一応Jetbrains公式からnavigation-compose的なライブラリがExperimentalで出たらしい。実装方法ほぼ同じようにできるということ
- Android以外だとBack Handler・deeplinkをサポートしていないらしい(近々サポート予定とのこと)
- UIStateは問題なく持てるので状態管理はできる
- Dialog実装でそのまま実装するとマテリアルデザインになってしまってiOSだと違和感が出てしまう
- 各OSで実装しよう。commonMainでexpect funにして作成して、それぞれのMainディレクトリにactual funで作成する。OSごとのバージョン番号など、プラットフォームごとの表示分けは同じように実装可能
- キャッチアップはJetBrainsのドキュメント、KMP-awssome、Kotlin Multiplatform OverViewなどから
パフォーマンスと可読性を両立:KotlinのCollection関数をマスター
Kotlinのコレクション関数をコード例を出しつつ、わかりやすく種類ごとに列挙してくださった面白いセッション。
正直使ったことのない、初めて聞いたものもありました。
特にいつ使えば良いんだろう…と思うものに対して具体的な使用例も紹介してくださったので非常にタメになるセッションでした。
- パフォーマンス比較すると、Sequenceはリストサイズによる違いはほぼなく、リストサイズが大きくなければCollectionの方が良い。最速はfor文
Kotlin Coroutinesで共有リソースに正しくアクセスする
- 競合状態が発生すると期待する結果が得られないこともある
- mutexを使ってロックをする。ブロック時はコルーチンが中断するのでコルーチンではこちらを使う
- 途中で介入されないように、Intの場合はスレッド安全なAtomicIntを使おう(Intでないなど扱いたいデータに対応したものがなければ使えない)
- synchronizedを使う。アノテーションでも使える。mutexとの違いはブロック時にスレッドを占有し続けてしまうこと。suspendでは使えないのでそれ以外で使う
- 単一スレッドの場合は読み込みと書き込みの間にsuspendが入る場合は、mutexでブロックするなど注意が必要
- MutableStateFlow.updateはスレッド安全なのでおすすめ。ただし書き込みされていたらもう一度実行する可能性があるので注意
- 非同期処理はできるだけ1スレッドで
- 複数コルーチンからアクセスされる共有リソースはできるだけ減らすこと
withContextってスレッド切り替え以外にも使えるって知ってた?
- withContextはスレッド切り替え用というわけではなく、CoroutineContextを切り替える役割
- CoroutineContextはKeyとElementを持つ辞書型に近いもの
- withContextは引数で与えられたCoroutineContextを現在のCoroutineContextに加えてブロックを実行する
- CoroutineContextはwithContextに渡すことでコンテキスト変数になる
ここからちょっとサーバーサイドの話が多くなりあまりわからなくなりました。。。
- コンテキスト変数として扱うことで引数のバケツリレーを防げる
Jetpack Compose: 効果的なComposable関数のAPI設計
- 使い回さないならシンプルに作る、使い回すなら柔軟なAPIになるようにする
- State Hoistingすべきタイミング:親のコンポーネントの状態を制御したい時か、他のコンポーネント状態を共有する場合
- 親の状態管理が複雑になってしまう場合があるので、子のコンポーネントで状態が完結するならその中で管理する
- ただし閉じすぎるとプレビューやテストが書きづらくなる。
- オーバーロードで対処する。ステートフルなAPIを公開しつつ、ステートレスなAPIをprivateにする
- Composableラムダを引数にすると柔軟なAPIになる(Slot API)
- 後から用途が増えそうなもの、増えてきたものを置換する
- ただし記述が冗長になる可能性があるので注意
- デフォルト引数を使うことで記述を簡単に。またComponentDefaultsといって引数が多ければそれらをまとめたオブジェクトを作ろう
- デメリットとして、値に気づかない可能性あり→値の意味をちゃんと考える必要があるときは使わない方が良い
- データクラスで引数をまとめるべき?
- 可読性を担保するため基本的にフラットに書いた方が良い
- パフォーマンスの問題もある。フラットに書いていればその部分が変わった時だけ再コンポーズするが、データクラスだと無意味に再コンポーズされる可能性
- Property Drilling:引数のバケツリレー
- 基本的にフラットに書くのでなってしまっても構わない
- ただしクラスを作ることで可読性が上がったりするならその方が良い場合もある。ケースバイケース
あらゆるアプリをCompose Multiplatformで書きたい! -ネイティブアプリの「あの機能」を私たちはどう作るか-
- OAuth認証で特定のページをブラウザで開く
- Android: StartActivity / iOS: openURL
- リダイレクトを受け取る
- リダイレクトのリクエストをアプリで受け取れるように設定する→ロジック実装→それぞれUri / URL型のインスタンスから欲しい値取得
- 個別の処理を並べてまとめてみることが大事。よく見るとそれぞれのOSで似たことをやっている
- ローカルストレージ
- Android: DataStore / iOS: UserDefaults
- DataStoreがv1.1.0~マルチプラットフォームに対応したのでそちらを使う。DataStoreのインスタンスをそれぞれのOS用に用意
- 画像表示対応
- Imageコンポーザブルを使う。bitmap/vector/painterに変換する必要があるのでライブラリを使って変換(kamel, Compose ImageLoader, Coil)
- 動画表示対応
- 自作する
- AndroidはMediaPlayerかExoPlayerでAndroidView上で再生
- iOSはAVPlayerVideoControllerかVideoPlayerをUIKitView上で再生。URLだけ渡せば読み込んでくれる
- Kotlinで使いたいならAVPlayerVideoControllerだが、VideoPlayerの方がカスタマイズが効く
- Android開発者は限界までKotlinで頑張る、iOS開発者ならSwiftUIで書いて組み込むなどで対応
- フレームワークのアプデによりビルドが通らなくなるのがきつい
- テストを書くこと。v1.6.0からKotlinで書いた単一のテストコードで両方のプラットフォームに対応できるようになった
感想
全体的にAndroid関連ではKMP・Compose Multiplatformの話が増えている印象を受けたので、そろそろ本腰入れて勉強しないといけないなと引き締まる思いでした。
また私自身はAndroid専門のためあまり状況を知りませんでしたが、サーバーサイドの話題も多かったのが意外でした。
こうした技術カンファイベントにオフラインで参加するのは初めてでしたが、現地の盛り上がりを肌で感じることができた上にノベルティなどもたくさんいただけてとても楽しかったです。
これからもKotlinを愛でていこうという思いが強まりました。