DroidKaigi2023見逃してたセッション

Androidアプリの良いユニットテストを考える

www.youtube.com
基本方針は以下で良さそう

  • Androidフレームワークに依存したコードと依存していないアプリのロジックを切り離してPure Local Test(Robolectricでない単純なテスト)で実行できるようにする
  • Androidフレームワークに依存したコードはRobolectricで実行する
  • 上記で不足している箇所をInstrumentation Testで実行する
mockito等のスタブについて

使いすぎるとテストコードと実装が密接に結びついてしまいテストコードのメンテナンスにコストがかかりやすくなるので必要のない箇所では使わない方がいい。
スタブを使うべき箇所として以下があげられていた

  • 現在時刻や乱数に依存する箇所
  • APIや外部サービス等アプリの外部から入力を受け取る箇所
  • BuildConfigなどappモジュールに実態があるものを他モジュールでも参照している箇所
  • 発生させるのが難しいエラーをエミュレートしたいとき
  • 依存オブジェクトからの非同期処理に対して遅延している状態をエミュレートしたいとき

スパイを使うべき箇所

  • APIや外部サービスなどアプリの外部への出力をする箇所
  • 依存オブジェクトのメソッドを呼び出す順序や呼び出す回数が重要な箇所

参考資料として挙げられてたテストの書き方について
GitHub - DeNA/android-modern-architecture-test-handson

テスト周りは公式ドキュメントも含めて色々覚える必要がある

DroidKaigi2023 Day2オンライン参加のメモ

リッチUIについて色々な事例紹介

speakerdeck.com
文字列や画像のグラデーションや背景追加や矩形追加等
かなりのパターンのアニメーションについても紹介されている

音量制御についてのセッション

docs.google.com
音量制御はあまり触れてこなかったので参考になった
音量設定は端末差異があるので数値で指定すると意図しないものになる可能性がある
SoundPool, MediaPlayer, AudioTrackを使い分ける

パフォーマンス監視ツールとJetpackComposeの導入

speakerdeck.com
パフォーマンス指標を図ることでJetpackComposeの導入のメリットを明確にすることにより採択されるようにしていった
各監視ツールの特徴がまとめられている

Jetpack ComposeでAndroid/iOSアプリを作る

AndroidiOS向けアプリを両方作る
UIはComposeで作ってビジネスロジック部分はKolin Multiplatformで作る話
Compose for iOSはα版なのでまだまだ実用には遠そう

Jetpack ComposeのSide-Effectを使いこなす

LaunchedEffect

LaunchedEffect(key)の形なのでkeyが変わったら動く(初回は必ず実行される)
内部はCoroutineで動いてるらしい
keyにUnitとか固定値を入れると最初の1回だけ動くようにできる
keyを複数指定も可能で指定したどれか1つが変わったら動く

LaunchedEffectのCoroutines

キャンセルするタイミングは
・keyが変わってLaunchedEffectが再起動するとき
・CompositionからLeaveするとき

rememberCoroutineScope
val scope = rememberCoroutineScope()

ボタンとかのイベント処理で使いそう
キャンセルのタイミングはCompositionからLeaveするとき

DisposableEffect

最後に何か処理をしたい場合によく使う
これはLaunchedEffectと違ってCoroutineでは起動してない
例ではライフサイクルの監視処理を起動させて最後に破棄する例だった
初回起動時はonDisposeが実行されず、次回起動時に前回の値でonDispose起動→それ以外の処理が今回の値で起動する

rememberUpdatedState

ComposeのStateを返す
内部としてはrecompositionのたびにvalueを書き換えてる
どういうときに必要か

  1. Composeの外で変化する状態を参照している
  2. LauchedEffect, DisposableEffectのkeyではない状態を参照
  3. 時間が経過して使用される状態への参照
  4. コールバック関数
SideEffect

Compositionが成功したときに実行される
Composable内でエラーが起きた場合には実行されないので正常時だけ動くような制御ができる
ログの抑止にも使えるかも

derivedStateOf
val listState = rememberLazyListState()    // スクロールの度に状態が変わる
LazyColumn(state = listState) { /*  */ }
val showButton = listState.firstVisibleItemIndex > 0    // スクロールの度にrecompositionされてしまう

if (showButton) {
    Button() {
        Text("ScrollTop")
    }
}

本来はshowButtonが変わった時だけ動かしたい

val showButton by remember {
    derivedStateOf {
        listState.firstVisibleItemIndex > 0
    }
}

こうするとshowButtonが変わった時だけrecompositionされる
LazyList系が一番使われそう

produceState

Composeの外の処理をComposeに変換してくれる

snapshotState

ComposeのStateをComposeの外でFLowとして監視したい


ログ出力にTimber使ってるので調べてみる

DroidKaigi2023 Day1オンライン参加のメモ

オンラインで参加して気になったセッションのメモ

よくあるUIのサンプル

github.com
結構使えそうな事例があるので参考になる。
1. ローディング
2. ボトムナビゲーション
3. ボトムシート
4. アコーディオン
5. カルーセル
6. クレジットカード番号入力
7. 固定ヘッダー
8. 特定位置へのスクロール
9. もっと見る

NestedScrollについて

scrollStateを渡すことでスクロール可能なコンポーネントにできる。
JetpackComposeとAndroidViewをつないで動かす事例とかも紹介されてた。

val scrollState = rememberScrollState()
Column(modifier = Modifier.verticalScroll(scrollState)) { ... }

AndroidStudioの環境いろいろ

speakerdeck.com

Firebase TestLab with Gradle Managed Devices

テスト自動化 使えそうなので調べる

Android SDK Upgrade Assitant

OSバージョンアップ対応時とかに使えそう

Gradle Build Script

build.gradle→build.gradle.ktsでKotinになる。
Google的には移行推奨らしい。

Network Inspector

条件を指定して特定のAPIリクエストに対して送信前に内容を変えたり受信後にアプリに渡す前に書き換えたりできるらしい

App Quality Insightsを使った品質検査

Firebase Crashlyticsレポートを確認できる

Composeのトレース

Compose UI 1.3.0-beta以上
Compose Compilier 1.3.0以上
何が契機でrecomposeされたか追えるようになる

JetpackComposeで上下左右の位置関係を反転できるレイアウトを作る

qiita.com
これの「子を右から左に配置したい」のところを参考にして上下も変更可能にカスタムした。

@Composable
fun InversionLayoutExample(
    layoutDirection: LayoutDirection = LayoutDirection.Ltr,
    idStart: Boolean = true) {
    Column(modifier = Modifier.width(200.dp)) {
        ProvideLayoutDirection(layoutDirection = layoutDirection) {
            InversionLayout(idStart)
        }
    }
}

@Composable
private fun ProvideLayoutDirection(
    layoutDirection: LayoutDirection,
    content: @Composable () -> Unit,
) {
    CompositionLocalProvider(
        LocalLayoutDirection provides layoutDirection,
        content = content,
    )
}

@Composable
private fun InversionLayout(isStart: Boolean = true) {
    Row(
        modifier = Modifier
            .padding(16.dp)
            .width(200.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
    ) {
        Box(
            modifier = Modifier
                .width(100.dp)
                .height(200.dp)
                .background(Color.Red),
            contentAlignment = if (isStart) {
                Alignment.TopStart
            } else {
                Alignment.BottomStart
            }
        ) {
            Text(
                text = "AAA",
                textAlign = TextAlign.End
            )
        }
        Box(
            modifier = Modifier
            .width(100.dp)
            .height(200.dp)
            .background(Color.Blue)
        )
    }
}

パラメータのパターンによる違いは以下の通り
InversionLayoutExample(LayoutDirection.Ltr, true)

InversionLayoutExample(LayoutDirection.Ltr, false)

InversionLayoutExample(LayoutDirection.Rtl, true)

InversionLayoutExample(LayoutDirection.Rtl, false)

中心点から対象に配置したいとかそういうときに使えるかも

9/7の学習内容

Kotlin Coroutines Flowについて

qiita.com
これまでstreamを扱っていた箇所をこれで実装できるかも

ViewModelとRetrofitでの通信実装例

satoshun.github.io
かなり近い形で使えそう
エラーハンドリングこれまでtry-catchでやってたけどrunCatchingの方が見た目もスッキリ感ある

XML→JetpackComposeの導入事例

techblog.zozo.com

今日の学習内容

Jetpack ComposeでPermissionを取得したい - 縁側プログラミング

  • LazyXXXのかくつき

こっちはまさにLazyVerticalGrid使ってて気になってたとこだった
LazyColumnのスクロールがカクカクするときの確認ポイント(Stableアノテーション) - 縁側プログラミング

  • Jetpack Compose内でのコルーチン

ボタンのonClickで時間かかる処理をしたいときはrememberCoroutineScope()を使う
コンポーザブルでコルーチンを使いたい場合は、LaunchedEffectを使う
コルーチンのキャンセルとかの例もあり参考になる
Jetpack ComposeのonClickでコルーチンを使いたい - 縁側プログラミング

  • マテリアルアイコンの使い方

Icons.Default.XXXで使える
全てのアイコンを使えるようにするにはmaterial-icons-extendedをbuild.gradleに追加する必要あり
Jetpack Composeでマテリアルアイコンを表示したい - 縁側プログラミング

  • Navitionで遷移を実装している場合のonPause, onResume処理

Navigationを使ってActivityやFragment内の遷移を実装していて
onPause, onResumeのタイミングで処理したい場合、Lifecycleイベントを使って実現する
Jetpack ComposeでonResumeやonPauseの処理を書きたい - 縁側プログラミング