3
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 アプリにおけるインスタンスの生存期間とスコープ

Last updated at Posted at 2025-03-05

本記事では、Android アプリ開発において登場する各種オブジェクト(ApplicationAndroidViewModel など)のインスタンス生存期間とスコープについて整理する。各オブジェクトがどのタイミング・スコープで生成され、破棄されるのかを理解することで、状態管理やリソース管理の最適化、メモリリークの防止などにつながる。

なお、記述内容は正確を期すよう努めているものの、誤りが含まれる可能性がある。もしそのような点にお気づきの場合は、指摘していただきたい。

前提

本記事では、androidx.navigation と Dagger Hilt の利用を想定して説明を進める。なお、スコープやインスタンスの生存期間の説明上、Dagger Hilt の文脈が登場するが、Dagger Hilt の依存性注入の解説は行わない。

取り扱うこと

  • 各オブジェクトの概要
  • 各オブジェクトの生存期間とスコープの概要

取り扱わないこと

  • 各オブジェクトの詳細なライフサイクルの内部処理
  • 実装方法や設計方針
  • 各オブジェクトの詳細な説明

各オブジェクトのスコープについて

本記事で主に扱うオブジェクトは以下の通りである。

  • Application
    android.app.Application のこと。
  • ActivityRetained@ActivityRetainedScoped):
    構成変更(画面回転など)の後も維持されるスコープのこと。Dagger Hilt におけるスコープの一種であり、実際のオブジェクトとして明示的に存在するのではなく、DI コンテナとして管理される。
  • Activity
    android.app.Activity のこと。
  • ScreenActivity 下の遷移の管理がされている画面のこと。Fragment や Compose の NavHostcomposable に当てはめてほしい。
  • ViewModel
    androidx.lifecycle.ViewModel のこと。
  • Service
    android.app.Service のこと。Activity(UI)とは独立してバックグランド処理に専念する。
  • Saved State の仕組み:
    onSaveInstanceStateSavedStateHandle などにより、一時的に状態を保存・復元する仕組み。

オブジェクト間の親子関係

各オブジェクトは、以下のような親子関係で整理できる。

{"Application":{"ActivityRetained":{"Activity":{"Screen":{}},"ViewModel":{}},"Service":{}},"Saved State":{}}

各オブジェクトの詳細

Application

Application はプロセス起動の起点となるオブジェクトである。Application の起動と破棄は OS が管理するため、通常はアプリ内から直接制御することない。多くの場合、ActivityService の起動要求に伴って Application が起動される。そして、ActivityService が存在しないときに OS のリソース管理の判断により Application が破棄される(プロセスが終了する)。

また、設定アプリなどから、通知や位置情報などのパーミッションを OFF に切り替えたときにも Application は破棄される。

Activity よりも生存期間が長いため、Activity の破棄・再生成後にも値を維持できる。

Application が持つする object1 は Activity が再生成された場合にも維持される。Activity が持っていた object2 は失う。

ActivityRetained@ActivityRetainedScoped

ActivityRetained は、画面回転などの構成変更が発生した際に Activity が再生成されてもそのまま保持されるスコープである。Dagger Hilt におけるスコープの一種であり、実際のオブジェクトとして明示的に存在するのではなく、DI コンテナとして管理される。

ただし、明示的に Activity を終了したとき(例:activity.finish())や OS のリソース管理によって Activity が破棄されるときには、ActivityRetained によるリソースも破棄される。

主に、ViewModel の管理や、構成変更時に状態を維持するために活用される。

構成変更により、Activity は再生成されるが、ActivityRetained と ViewModel は維持される。

明示的な、Activity の破棄では、ActivityRetained と ViewModel も破棄される。

Activity

Activity は画面を扱うオブジェクトである。画面回転などの構成変更や、バックグラウンド時の OS の判断などで破棄される。

Activity の破棄により UI に関する状態は失われるため、状態の保持・復元のためには、ApplicationViewModel、Saved State などを活用する必要がある。

Screen

ScreenActivity 下の遷移の管理がされている画面のこと。Fragment や Compose の NavHostcomposable に当てはめてほしい。画面遷移に連動して ViewModel の生存期間が管理される。

ViewModel

ViewModel は、ActivityRetained のスコープに配置されることで、構成変更による再生成があっても、状態を維持する。

Activity や画面遷移のライフサイクルに連動した生存期間となる。具体的には、ActivityScreen が完全に破棄された(画面遷移のバックスタックから無くなった)ときに、ViewModel も破棄される。

注意すべきは、Screen が画面遷移のバックスタックにあり、表に出ていない場合にも ViewModel はアクティブである。

Screen1 → Screen2 に遷移した状態では、Screen1 は非アクティブであるが、ViewModel1 と ViewModel2 がアクティブである。Screen2 を pop すると、Screen1 がアクティブになり、ViewModel2 は破棄される。

Service

Service はバックグランドでの処理に用いら、UI とは独立して動作する。そのため Application は参照できるが、ActivityViewModel は参照できない。

Saved State の仕組み1

onSaveInstanceStateSavedStateHandle などにより、一時的に状態を保存・復元する仕組みのこと。保存先の領域は Application のスコープ外にある。

ActivityViewModel の状態を保存・復元されるために利用される。また、画面遷移などの複数の Screen 等をまたいだあたいのやり取りにも利用される。

Activity の再生成後にも Saved State は保持される。

注意すべきこと

生存期間や役割の違いから、いくつか特に注意すべきポイントを以下に示す。

activity.finish() では Application は破棄されない

例えば、アプリの退会時に状態を初期化し、アプリを終了したいとする。このとき activity.finish() が最も単純な方法に思えるであろう。しかし、これでは Activity が破棄されるだけで、Application は破棄されるとは限らない。Application のスコープ(Dagger Hilt での Singleton)で保持している状態は継続して生存する。

activity.finish() では Activity を終了させることはできるが、Application を終了させることができないことに注意する必要がある。

Activity の終了により、Activity が保持する object2 は破棄されるが、Application が保持する object1 は破棄されない。

ViewModelActivity の生存期間の違いによるメモリリーク

Activity は構成変更により再生成される一方、ViewModel はそのスコープにより、長い生存期間を持つ。もし ViewModelActivitycontext などを渡してしまうと、ViewModel が参照し続けて、メモリが解放されない(メモリリークする)。

Activity だけでなく、FragmentComposable においても同様にメモリリークが発生する。そのため、ViewModel 内では ApplicationContext のみを参照するなどの対策が必要である。

ViewModel が Activity から context を受け取る。Activity の再生後も context を保持し続けるため、メモリが解放されない。

Saved State と Application の不整合

例えば、入力画面でユーザーが値を入力し、その値を Application の領域に保存した場合を考える。入力後に確認画面に遷移し、その画面で値を参照する。このとき設定アプリを開き何らかのパーミッションを OFF にすると、Application が破棄され、入力した値も破棄される。アプリを再度開くと、Saved State により確認画面を開いた状態に復元されるが、Application の値は復元されない。そのため、確認画面にいるが、入力値が存在しないという状態が生まれてしまう。

Application より Saved State の生存期間が長いことにより、アプリ全体の状態に不整合が起きる危険性がある。それぞれに保存する値に注意する必要がある。

パーミッションを OFF にすることで、Application の値が破棄されるが、Saved State は破棄されない。両方が保持されていることを前提としている場合、不整合が生じる。

まとめ

本記事では、Android アプリにおける各種オブジェクトの生存期間とスコープについて整理した。

各オブジェクトのスコープやライフサイクルの理解は、Android アプリケーションの設計・実装において非常に重要である。リソースの無駄な消費や状態の不整合を回避するためには、スコープやライフサイクルを理解する必要がある。

参考

  1. The activity lifecycle
  2. Request runtime permissions
  3. Save UI states
  4. App startup time
  5. Dependency injection with Hilt
  6. Use Hilt with other Jetpack libraries
  1. Saved State の仕組みは、保存するための領域が Application の外側にあるというだけの認識で説明を進める。system_serverBundle などの技術的な解決策や仕組みの説明は割愛する。

3
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
3
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?