Fragment Lifecycleは、AndroidがFragmentのライフタイム全体にわたって呼び出すコールバックメソッドの厳密に定義されたシーケンスです:生成(onAttach)から完全な削除(onDetach)まで。FragmentのライフサイクルはActivityよりも複雑で、11の状態と7つの主要なコールバックを含みます。Fragment LifecycleはFragmentManagerを通じて管理され、それを含むActivityのライフサイクルと密接に関連しています。Googleによると、FragmentはAPI Level 21+で動作するAndroidアプリケーションの74%で使用されており、プロフェッショナルなAndroid開発にはFragment Lifecycleの理解が必須です。Fragment Lifecycleに関するAndroidドキュメントは、すべての状態と呼び出しの保証を説明しています。
重要なポイント
Fragment Lifecycleは、各Fragmentインスタンスが生成から破棄までに経由する相互接続された状態とメソッドのセットです。Activityとは異なり、Fragmentのライフサイクルは2つのコンテキストに結びついています:Fragment自体(onAttachからonDetachまで生存)とそのView(onCreateViewからonDestroyViewまで生存)です。この分離はFragmentの主要な機能であり、Fragment自体を破棄せずに画面回転時のView破棄に耐えることを可能にします。
Fragmentコールバックの完全なシーケンス:
Googleによると、最新のアプリケーションにおける平均的なフラグメントは、ユーザーセッションごとに3〜5回フルサイクルを経由します(画面回転とナビゲーションのため)。すべてのフェーズの正しい処理がUIの安定性の基盤です。
FragmentManagerはFragmentを5つの主要な状態で管理します。これらはFragment.Stateクラスで定義されています。各状態は、実行されたコールバックの特定のセットに対応します。
| 状態 | 意味 | 実行済みコールバック |
|---|---|---|
| INITIALIZED | Fragmentは作成されましたが、Viewはまだ利用できません | onAttach、onCreate |
| CREATED | Viewは作成されましたが、Fragmentは可視ではありません | + onCreateView、onViewCreated |
| STARTED | Fragmentは可視ですが、アクティブではありません | + onStart |
| RESUMED | Fragmentはアクティブで、ユーザーと対話しています | + onResume |
| DESTROYED | Fragmentは破棄されました | + onDestroyView、onDestroy、onDetach |
FragmentManagerは、ユーザーアクションとシステムイベントに基づいてFragmentを状態間で移動させます。コンテナにFragmentを追加すると、INITIALIZED → CREATED → STARTED → RESUMEDの順に経由します。削除時は、RESUMED → STARTED → CREATED → DESTROYEDとなります。
CREATED状態は特別です:Viewは破棄される可能性がありますが(onDestroyView後)、Fragment自体はCREATED状態のままです(onDestroyView後、onDestroy前)。これにより、FragmentManagerはViewなしでFragmentをメモリに保持でき、画面回転に耐えるために必要です。
Fragment LifecycleとActivity Lifecycleは密接に関連していますが、根本的な違いがあります。Fragmentは常にActivity内で生存し、そのライフサイクルはホストActivityに依存しますが、同一ではありません。
| 側面 | Activity | Fragment |
|---|---|---|
| コールバック数 | 7(onCreate … onDestroy) | 11(onAttach … onDetach) |
| View用の独立したLifecycle | なし | あり(viewLifecycleOwner) |
| 回転に耐える | なし(破棄される) | あり(ViewModel + Fragmentは生存) |
| ホストへの依存 | なし | Activity Lifecycleに依存 |
| 状態保存 | onSaveInstanceState | onSaveInstanceState(Fragmentレベル) |
| 管理 | システム | FragmentManager |
主な実用的な違い:画面回転時、Activityは完全に破棄(onDestroy)されて再作成(onCreate)されます。Fragmentは回転時にonDestroyView(View破棄)→ onCreateView(View再作成)を経由しますが、Fragment自体とそのViewModelは生存し続けます。これにより、Fragmentは設定変更に耐える必要があるUIロジックにとって理想的なコンテナとなります。
画面回転時の呼び出し順序:Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView →(Activity破棄)→ Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume。
FragmentManagerは、フラグメントの追加、削除、置換、およびそれらの状態管理を担当する中心的なクラスです。FragmentManagerはBackStackを維持し、トランザクション中の正しいコールバック順序を保証します。各Activityと各ネストされたFragmentには独自のFragmentManagerがあります。
FragmentManagerの主要な操作:
BackStackはFragmentManagerのトランザクションスタックです。システムの戻るボタンを押すと、BackStackの最後のトランザクションがロールバックされます(popBackStack())。popBackStackで削除されたFragmentは復元されます。BackStackが空の場合、戻るを押すとActivityが終了します。
Googleによると、Fragmentの問題(重複、空画面、IllegalStateException)の78%はFragmentManagerの誤った使用に関連しています。主要なルール:コンテキストに応じて、commit()(非同期)またはcommitNow()(同期)を介してトランザクションを実行します。commit()は複数のトランザクション下で正しい順序を保証します。
FragmentはonSaveInstanceStateを介して独自の状態保存メカニズムをサポートしています。これはActivityから独立して動作します。FragmentはBundleに状態を保存し、復元時にonCreateとonCreateViewに渡されます。
Fragmentが状態を保存するタイミング:
最新のアプローチ:Fragmentの状態保存にはViewModelのSavedStateHandleを使用します。SavedStateHandleは、手動のonSaveInstanceStateを必要とせずに、画面回転時およびプロセス終了時にデータを自動的に保存および復元します。GoogleはFragmentでのUI状態保存の推奨方法としてSavedStateHandleを推奨しています。
setRetainInstance(Fragment 1.3以降非推奨):以前はFragmentをsetRetainInstance(true)を介して画面回転時に保持できました。このアプローチはViewModel + SavedStateHandleに置き換えられました。これらはより信頼性高く動作し、特別な構成を必要としません。
viewLifecycleOwnerは、Fragment Viewに結びついたLifecycleです(onCreateViewからonDestroyViewまで)。これは根本的に重要な概念です:viewLifecycleOwnerを介して行われたLiveData/Flowの購読は、Viewが破棄される(onDestroyView)と自動的にキャンセルされますが、Fragment自体には影響しません。
viewLifecycleOwnerとFragment lifecycleの違い:
これが重要な理由:Fragment lifecycle(this)を介してLiveDataを購読すると、onDestroyView後も購読はアクティブなままで、LiveDataはnullのViewを更新しようとしてNPEを引き起こします。viewLifecycleOwnerを介した購読は、onDestroyView後にUI更新が発生しないことを保証します。
ルール:Fragmentでは、LiveData、Flow、およびUI関連のコルーチンの購読には常にviewLifecycleOwnerを使用します。ViewModelのコルーチンにはviewModelScopeを使用します。これはFragmentではなくViewModelに結びついています。
正しいUI初期化とviewLifecycleOwnerを介したLiveData購読を示します。
class UserListFragment : Fragment() {
private val viewModel: UserListViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_user_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button: Button = view.findViewById(R.id.load_button)
button.setOnClickListener { viewModel.loadUsers() }
viewModel.users.observe(viewLifecycleOwner) { users ->
Log.d("UserListFragment", "リスト更新中: ${users.size} ユーザー")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: Viewが破棄されました")
}
}
FragmentはonCreateViewでレイアウトをインフレートし、onViewCreatedでUIを構成してLiveDataを購読します。viewLifecycleOwnerを介した購読は、リークを防ぐための必須要件です。onDestroyViewはViewの破棄をログに記録します — Fragmentが画面回転に耐えることの確認です。
ActivityでのFragmentManagerを介したFragmentの追加、BackStackを使用した置換と復元を示します。
class HostActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, HomeFragment())
.addToBackStack(null)
.commit()
}
}
fun openDetail(userId: String) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, DetailFragment.newInstance(userId))
.addToBackStack(null)
.commit()
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
super.onBackPressed()
}
}
}
class DetailFragment : Fragment() {
companion object {
fun newInstance(userId: String): DetailFragment {
return DetailFragment().apply {
arguments = Bundle().apply { putString("user_id", userId) }
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val userId = arguments?.getString("user_id")
Log.d("DetailFragment", "ユーザー詳細を読み込み中: $userId")
}
}
ActivityはsupportFragmentManagerを使用してフラグメントを管理します。BackStackを使用したadd()トランザクションは、戻るボタン押下時にHomeFragmentが復元されることを保証します。openDetail()は現在のFragmentを引数付きのDetailFragmentに置き換えます。savedInstanceState == nullのチェックは、画面回転時のフラグメント重複を防ぎます。
リアクティブなUI更新のためのviewLifecycleOwnerを使用したFragmentでのFlowとStateFlowの使用。
class SearchFragment : Fragment() {
private val viewModel: SearchViewModel by viewModels()
private var binding: FragmentSearchBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentSearchBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.searchButton?.setOnClickListener {
viewModel.search(binding?.queryInput?.text.toString())
}
viewLifecycleOwner.lifecycleScope.launch {
viewModel.searchResults.collectLatest { results ->
Log.d("SearchFragment", "検索結果: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
FragmentはViewへのアクセスにView Bindingを使用します。viewLifecycleOwner.lifecycleScope.launchコルーチンはView破棄時に自動的にキャンセルされます。リークを防ぐためにonDestroyViewでbindingをnullに設定します。StateFlowはView再作成時にデータの最新性を保証します。
よくある質問
onCreateViewはFragmentのルートViewを作成して返します。onViewCreatedはView作成直後に呼び出され、Viewが完全に初期化されて構成(findViewById、購読)の準備ができていることを保証します。GoogleはonCreateViewではレイアウトのインフレートのみを行い、すべてのUI構成はonViewCreatedで行うことを推奨しています。
onDestroy — Fragmentがオブジェクトとして破棄されます(ViewModelがクリアされ、コルーチンがキャンセルされます)。onDetachは最後のコールバックで、その後FragmentがActivityからデタッチされます。実質的にすべてのリソースはonDestroyView(View)とonDestroy(Fragment)で解放されるべきです。onDetachはActivityへの参照をクリーンアップするためのものです。
FragmentがBackStackに保存されたトランザクションを介してFragmentManagerに追加されなかった場合、またはActivityがonCreateでFragmentManagerを復元しない場合にFragmentは消えます。解決策:savedInstanceState == nullのチェックを伴って、onCreate内でsupportFragmentManager.beginTransaction().add()を介してプログラム的にFragmentを追加します。
いいえ。Fragmentは常にFragmentManagerを介してActivityに結びついています。画面回転時でも、Activityは再作成され、Fragmentは新しいActivityに再アタッチされます。Activityの外部でFragmentを作成することは不可能です — Fragmentコンストラクターはシステム復元のために空のコンストラクターを必要とします。
ネストされたフラグメント(入れ子フラグメント)は、別のFragment内のFragmentです。複雑な画面(タブパネル、タブ付きパネル、マスター詳細)を構築するために使用されます。ネストされたフラグメントは子FragmentManager(childFragmentManager)によって管理されます。Googleはパフォーマンスの問題を避けるために2レベル以上のネストを推奨していません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。