ご覧いただきありがとうございます🙇♂️
今年の10月からWebアプリエンジニアからAndroidアプリエンジニアに転職しAndroid開発を学習中の者です。
実務の中でFragmentを使用した際にライフサイクルが理解できていないために、苦労したので今回はFragmentが持つライフサイクルについて学習したものをまとめてみました。
そもそもFragmentとは何か
Fragmentとはアクティビティの内部で動作するUIと動作の再利用可能モジュールのこと。アクティビティがアプリ全体の画面を構成するのに対し、UIのコンポーネントとして働き柔軟に分割・再利用が可能になる。
タブレットなどの大画面デバイスなどでは、画面全体をまとめて画面遷移させるのは合理的ではないため、画面を分割して左に一覧表示をして右側に詳細画面を表示するといった分割レイアウトを作成する際などに役立つ。
Fragmentのライフサイクル
Fragmentのライフサイクルは、Activityと密接に連携をしている。大まかに分けて3つの段階に分かれる
- 生成
- 表示(Viewの管理)
- 停止・破棄
生成に関わるライフサイクル
onAttach()
- FragmentがActivityに最初に関連づけられる時に呼ばれる
- Contextにアクセスできるようになる
onCreate()
- Fragmentが生成された時に呼ばれる。この時点ではUIがまだ生成されていない
- ViewModelの初期化や非表示データの準備などFragmentに必要な初期設定を行う
onCreateView()
- FragmentのUIを初期化するために呼ばれる
-
LayoutInflater
を使ってXMLからViewの作成を行う
onViewCreated()
onCreateView()の後、Viewが完全に作成された時に呼ばれる
RecyclerView
の初期化やリスナーの設定などを行う
表示に関わるライフサイクル
onStart()
- Fragmentがユーザーに見えるようになった時に呼ばれる
- UIの更新やリスナーの準備、センサーを登録する
onResume()
- Fragmentが完全に表示され、操作可能になった時に呼ばれる
- アニメーションの再開やユーザー入力の監視などフォアグラウンドの状態で動作を行う
停止・破棄に関するライフサイクル
onPause()
- Fragmentがフォアグラウンドを離れるときに呼ばれる
- アニメーションやセンサーの一時停止
onStop()
- Fragmentがユーザーに見えなくなったときに呼ばれる
- リスナーの解除やバックグラウンド処理の停止などリソースの解放
onDestroyView()
- FragmentのViewが破棄される直前に呼ばれる
-
Binding
オブジェクトの解放
onDestory()
- Fragmentが完全に破棄される時に呼ばれる
- 永久データの保存やオブジェクトの解放などクリーンアップを行う
onDetach()
- FragmentがActivityとの関連付けを解除される時に呼ばれる
-
onAttach()
で取得したContextの解放
Fragmentのライフサイクルの全体的な流れ
FragmentがActivityにアタッチされる時の流れ
- onAttach()
- onCreate()
- onCreateView
- onViewCreated()
- onStart()
- onResume()
Fragmentが非表示または破棄される時の流れ
- onPause()
- onStop()
- onDestroyView
- onDestory
- onDetach()
Claudeにサンプルコードを作成してもらう
ここまででライフサイクルのメソッドを理解できたので、実際にコーディングをする際のサンプルコードをClaudeに生成してもらいました
class ExampleFragment : Fragment() {
// FragmentがActivityに関連づけられた時
override fun onAttach(context: Context) {
super.onAttach(context)
Log.d("FragmentLifecycle", "onAttach() called")
}
// Fragmentが最初に生成された時
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("FragmentLifecycle", "onCreate() called")
// ViewModelの初期化
val viewModel: ExampleViewModel by viewModels()
}
// FragmentのUIを初期化
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("FragmentLifecycle", "onCreateView() called")
// XMLレイアウトからViewを生成
return inflater.inflate(R.layout.fragment_example, container, false)
}
// Viewが完全に作成された後
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("FragmentLifecycle", "onViewCreated() called")
// RecyclerViewの初期化
val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)
recyclerView.adapter = ExampleAdapter()
recyclerView.layoutManager = LinearLayoutManager(context)
// ボタンのリスナー設定
view.findViewById<Button>(R.id.exampleButton).setOnClickListener {
// ボタンクリック時の処理
}
}
// Fragmentがユーザーに見えるようになった時
override fun onStart() {
super.onStart()
Log.d("FragmentLifecycle", "onStart() called")
// センサーの登録や初期化
sensorManager?.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}
// Fragmentが完全に表示され、操作可能になった時
override fun onResume() {
super.onResume()
Log.d("FragmentLifecycle", "onResume() called")
// アニメーションの再開
animationView?.resume()
}
// Fragmentがフォアグラウンドを離れる時
override fun onPause() {
super.onPause()
Log.d("FragmentLifecycle", "onPause() called")
// アニメーションの一時停止
animationView?.pause()
}
// Fragmentがユーザーに見えなくなった時
override fun onStop() {
super.onStop()
Log.d("FragmentLifecycle", "onStop() called")
// センサーの登録解除
sensorManager?.unregisterListener(sensorListener)
}
// FragmentのViewが破棄される直前
override fun onDestroyView() {
super.onDestroyView()
Log.d("FragmentLifecycle", "onDestroyView() called")
// ビューバインディングの解放
_binding = null
}
// Fragmentが完全に破棄される時
override fun onDestroy() {
super.onDestroy()
Log.d("FragmentLifecycle", "onDestroy() called")
// リソースの解放
job?.cancel()
}
// FragmentがActivityとの関連付けを解除される時
override fun onDetach() {
super.onDetach()
Log.d("FragmentLifecycle", "onDetach() called")
}
}
まとめ
これまではライフサイクルを特に意識せず、実装を進めたことでViewの表示がうまくいかないことが多々あったため、ライフサイクルの流れを意識して、実装することでFragment関連のバグを減らすことができそうだと感じた。