はじめに
こんにちは!
Android開発で地味に頭を悩ませるのが、ActivityやFragmentの複雑なライフサイクルですよね...。onCreateやらonStartやらonDestroyViewやら...(正直今でも時々混乱します w)
最近Jetpack Composeを触っているのですが、このライフサイクルの考え方がガラッと変わって、最初は少し戸惑いました。
ですが、慣れてくるとComposeのライフサイクルは非常にシンプルで、**「誕生」「変化」「消滅」**の3つだけなんだな、ということが分かってきました。
今回は、僕自身の復習も兼ねて、@Composable関数(コンポーザブル)が画面に表示されてから消えるまでの「一生」を、この3つの状態で整理してみたいと思います。
状態1:Compositionへの参加(誕生の瞬間)
これは、コンポーザブルが初めて画面に描画されるタイミングです。
if文の条件が満たされたり、画面が最初に表示されたりした時に、僕たちが書いた@Composable関数が呼び出されます。
- ざっくり言うと: Composeの世界に「初めまして!」と登場する瞬間ですね。Composeは内部でUIの構成をツリー構造で管理しているのですが、そこに新しいノードとして追加されるイメージです。
-
昔のViewシステムで言うと:
FragmentのonCreateView()でレイアウトを読み込んで、UIを構築するプロセスにちょっと似てるかもしれません。
🔑 ここでのポイント
この「誕生」の処理は、UIの初期状態を作る大事な部分です。ただ、ここで注意したいのが、「この処理は最初の一回しか呼ばれないだろう」と思い込まないことです。なぜなら、次にお話しする「再コンポーズ」が、Composeの日常茶飯事だからです(笑)。
状態2:再コンポーズ(成長と変化)
さて、ここがComposeの肝であり、最初のうちは一番「???」となる部分、**再コンポーズ(Recomposition)**です。
簡単に言うと、状態(State)が変わったのをきっかけに、コンポーザブルがもう一度呼び出されて、画面の一部が更新されることです。
remember { mutableStateOf(...) }で定義した状態をコンポーザブルが使っていると、その値が変わった瞬間に、Composeが「お、データが変わったな!じゃあ見た目も変えなきゃ!」と、関係する部分だけを賢く再描画してくれます。
- ざっくり言うと: 監督が俳優に「もっと驚いた表情で!」と指示を出すような感じです。俳優は舞台から降りたりせず、その場で表情の演技だけをやり直します。
-
昔のViewシステムで言うと:
TextViewの文字を変えるためにtextView.setText("新しいテキスト")と手動で命令していたアレです。Composeでは、状態を変えるだけで、この面倒なUI更新を全自動でやってくれます。めちゃくちゃ楽です。
🔑 ここでのポイント
再コンポーズは、僕たちが思っている以上に頻繁に、そして高速に実行されます。なので、いくつか守らないとパフォーマンスが落ちる「お作法」があります。
-
重い処理は絶対ダメ!: 再コンポーズはアニメーション中なら1秒間に60回呼ばれることもあります。ここでDBアクセスやネットワーク通信なんて書いたら…考えるだけで恐ろしいですね(笑)。重い処理は全部
ViewModelに任せましょう。 -
副作用のない作りにする:
@Composable関数は、同じ状態を渡されたら、いつ呼ばれても同じUIを返すように作るのが理想です。 - Composeは意外とサボる: もし渡されるデータが変わっていなければ、Composeは「あ、これ前と同じだな」と判断して、再コンポーズを賢くスキップします。これがComposeのパフォーマンスが高い理由の一つです。
状態3:Compositionからの退出(消滅の瞬間)
最後に、コンポーザブルが画面から不要になって破棄されるタイミングです。
if文の条件が外れたり、別の画面に遷移したりして、UIが表示されなくなった時に発生します。UIツリーから、そのコンポーザブルは静かに消え去ります。
- ざっくり言うと: 映画の撮影が終わって、俳優がセットから帰っていくイメージです。「お疲れ様でしたー!」って感じですね。
-
昔のViewシステムで言うと:
FragmentのonDestroyView()が一番近いです。UIが見えなくなるので、後片付けをするタイミングです。
🔑 ここでのポイント
ほとんどの場合、コンポーザブルはただ消えるだけでOKです。でも、もし「誕生」の時に、外部のリソース(センサーの監視を開始した、など)を使い始めた場合は、「消滅」の時にそれを解放する後片付けが絶対に必要です。これを忘れると、メモリリークという名の恐ろしいバグの原因になります。
この「消滅」のタイミングを捕まえるために、ComposeはDisposableEffectという仕組みを用意してくれています。
DisposableEffect(Unit) {
// 誕生した時の処理
startSomeListener()
onDispose {
// 消滅する時の後片付け
stopSomeListener() // ← これを忘れると大変なことに…!
}
}
まとめ
というわけで、コンポーザブルの一生をざっと追いかけてみました。
| 状態 | 🎬 ざっくり言うと | 従来のViewシステムで言うと | 🔑 役割 |
|---|---|---|---|
| 参加 (誕生) | 俳優がセットに入る | onCreateView |
UIの初期描画 |
| 再コンポーズ (変化) | 監督の指示で再演技 | 手動でのUI更新 | 状態に応じたUIの差分更新 |
| 退出 (消滅) | 俳優がセットから出る | onDestroyView |
リソースの解放 |
この「状態が変われば、UIも勝手に変わる」という宣言的な考え方に慣れるのが、Composeを使いこなす第一歩なのかなと感じています。
ここまで読んでいただきありがとうございました!