1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Android】Jetpack Compose:DisposableEffect 完全ガイド

1
Posted at

Jetpack Compose:DisposableEffect 完全ガイド

ライフサイクルと紐づいた副作用処理を安全に行うための仕組み

Jetpack Compose の副作用 API には、

  • LaunchedEffect
  • SideEffect
  • DisposableEffect
  • rememberUpdatedState
    など、さまざまな種類がある。

その中で DisposableEffect は、
「リソースの登録」や「リスナーの追加」などの 開始 と、
画面やキーが変わった時の クリーンアップ を扱うための仕組み。

つまり “登録と解除をセットで扱うエフェクト”。

実務で最も重要な効果を持つ API のひとつだ。

この記事では DisposableEffect の使い方・実例・注意点・ベストプラクティスを
ひとつずつわかりやすく解説する。


1. DisposableEffect とは?

ひとことで言うと:

Compose のライフサイクルにあわせて “登録” と “解除” を確実に実行する副作用ブロック

できること

  • 画面が表示されたらリスナーを登録
  • 画面が消えたらリスナーを解除
  • Key が変われば旧リソースを破棄し、新しいリソースに登録
  • API の購読開始/終了を安全に扱う

2. 基本構文

DisposableEffect(key1, key2, ...) {

    // 登録処理
    onDispose {
        // クリーンアップ処理
    }
}

実行タイミングは以下のとおり:

  • 初回:登録処理が実行される
  • key が変化:いったん onDispose が実行されてから再登録
  • 呼び出し元 composable が破棄 → onDispose が呼ばれる

3. 実例で理解する DisposableEffect

3.1 BroadcastReceiver を登録する(Android)

DisposableEffect(Unit) {
    val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            // handle broadcast
        }
    }

    context.registerReceiver(receiver, IntentFilter("MY_ACTION"))

    onDispose {
        context.unregisterReceiver(receiver)
    }
}

register と unregister のセットを安全に扱える典型例。


3.2 Callback/Listener の登録・解除

DisposableEffect(Unit) {
    val listener = object : Player.Listener {
        override fun onStateChanged(state: Int) {
            // …
        }
    }

    player.addListener(listener)

    onDispose {
        player.removeListener(listener)
    }
}

CustomView の listener でも同じパターンで使える。


3.3 SensorManager / LocationManager の登録・解除

DisposableEffect(Unit) {
    sensorManager.registerListener(sensorListener, accel, SensorManager.SENSOR_DELAY_NORMAL)

    onDispose {
        sensorManager.unregisterListener(sensorListener)
    }
}

Compose でセンサーを扱う時は必須。


3.4 ViewModel では持たないが UI Lifecyle に紐づく処理

DisposableEffect(lifecycleOwner) {
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME) {
            // resume 時のみ処理
        }
    }

    lifecycleOwner.lifecycle.addObserver(observer)

    onDispose {
        lifecycleOwner.lifecycle.removeObserver(observer)
    }
}

画面の範囲で生きるリソースに最適。


3-5. Key 切り替えとクリーンアップ

DisposableEffect(selectedId) {
    // 新しい ID で購読
    notifier.subscribe(selectedId)

    onDispose {
        // 古い ID の購読を解除
        notifier.unsubscribe(selectedId)
    }
}

Key が変わった瞬間、旧リソースが必ず片付く。


4. LaunchedEffect との違い

一番わかりやすい差分表

項目 DisposableEffect LaunchedEffect
主目的 登録+解除(リソース管理) suspend の副作用
実行タイミング 初回+key変更+破棄時 初回+key変更時
解除処理 ✔ onDispose が呼ばれる ❌ 自前で書かないと不可
suspend OK? ❌ NG ✔ OK

DisposableEffect は ライフサイクル管理のための API
LaunchedEffect は コルーチン処理のための API

用途ははっきり分かれている。


5. SideEffect との違い

項目 SideEffect DisposableEffect
実行タイミング 毎回 recompose 後 初回+key変更+破棄時
目的 外部状態の更新 リソースの管理
クリーンアップ ❌ できない ✔ できる

SideEffect は「軽い通知」
DisposableEffect は「重い登録と解除」


6. よくあるミスと注意点

❌ 6-1. suspend 関数を使おうとする

DisposableEffect {
    delay(1000) // ❌ エラー
}

DisposableEffect は 同期処理専用。


❌ 6-2. Compose state を書き換える

DisposableEffect(Unit) {
    count++ // ❌ 無限ループの危険
}

State 更新は再compose → 再Dispose → 再登録を引き起こし、
リソースが暴走する。


❌ 6-3. onDispose を忘れてリークする

リスナー・BroadcastReceiver・Observer などを入れたら、
必ず onDispose で解除することが前提。


7. ベストプラクティス(実務向け)


7.1 外部リソースは “必ずセットで管理”

DisposableEffect(Unit) {
    register()
    onDispose { unregister() }
}

7.2 Key でコントロールする

ID が変わる UI(詳細画面など)では必須:

DisposableEffect(userId) {
    repo.subscribe(userId)
    onDispose { repo.unsubscribe(userId) }
}

7.3 カスタム View のリスナーはここで扱う

AndroidView(...) { view ->
    DisposableEffect(view) {
        val listener = …
        view.addListener(listener)
        onDispose { view.removeListener(listener) }
    }
}

7.4 LifecycleObserver の登録と解除

DisposableEffect(lifecycleOwner) {
    val observer = …
    lifecycleOwner.lifecycle.addObserver(observer)
    onDispose {
        lifecycleOwner.lifecycle.removeObserver(observer)
    }
}

まとめ:DisposableEffect は「開始と終了」を扱うプロのツール

  • リスナー追加 → 解除
  • センサー登録 → 解除
  • BroadcastReceiver 登録 → 解除
  • Lifecycle Observer 追加 → 解除

こういった “ライフサイクルと紐づく処理” を 絶対に安全に行いたい場面のための API。

覚えておきたいポイント:

  • suspend は使えない
  • onDispose は必ず書く
  • Key が変わると古いリソースが破棄される
  • LaunchedEffect / SideEffect と役割が全く違う

DisposableEffect を正しく使えば、
Compose の “見えないライフサイクル” に振り回されず
安定した UI と安全なリソース管理ができるようになる。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?