Fragmentのライフサイクルって何？

# ご覧いただきありがとうございます🙇‍♂️
今年の10月からWebアプリエンジニアからAndroidアプリエンジニアに転職しAndroid開発を学習中の者です。
実務の中でFragmentを使用した際にライフサイクルが理解できていないために、苦労したので今回はFragmentが持つライフサイクルについて学習したものをまとめてみました。

# そもそもFragmentとは何か
Fragmentとはアクティビティの内部で動作するUIと動作の再利用可能モジュールのこと。アクティビティがアプリ全体の画面を構成するのに対し、UIのコンポーネントとして働き柔軟に分割・再利用が可能になる。
タブレットなどの大画面デバイスなどでは、画面全体をまとめて画面遷移させるのは合理的ではないため、画面を分割して左に一覧表示をして右側に詳細画面を表示するといった分割レイアウトを作成する際などに役立つ。

# Fragmentのライフサイクル
Fragmentのライフサイクルは、Activityと密接に連携をしている。大まかに分けて3つの段階に分かれる
1. 生成
1. 表示（Viewの管理）
1. 停止・破棄

## 生成に関わるライフサイクル
### 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にアタッチされる時の流れ
1. onAttach()
1. onCreate()
1. onCreateView
1. onViewCreated()
1. onStart()
1. onResume()

### Fragmentが非表示または破棄される時の流れ
1. onPause()
1. onStop()
1. onDestroyView
1. onDestory
1. onDetach()

# Claudeにサンプルコードを作成してもらう
ここまででライフサイクルのメソッドを理解できたので、実際にコーディングをする際のサンプルコードをClaudeに生成してもらいました

``` kotlin
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関連のバグを減らすことができそうだと感じた。