Android - Activity / Fragmentの再生成パターンと解決策

# 概要

Androiderの方には当たり前のことかもしれないですが、、
AndroidでActivityの再生成について調べた際の知見をまとめます。
「Activity 再生成」などで検索するとたくさん有益な情報が出て来るので、合わせて調べていただけるといいと思います。
またFragmentについても少し言及したいと思っています。

# 結論

基本的に `Activity/Fragment`は破棄されるもの。破棄されたとしても、適切に復帰できるような設計、コードを書いておくことが大事

→ __個人的にはAndroid Architecture Componentをおすすめしたい__

という記事です。

# Activity再生成のパターン

1. メモリ不足の際のOSトリガーによる再起動
2. アクティビティで処理する設定の変更 (画面回転含む)

基本的には、大きく分けて上記2つなのかなと思っています。以下の2つのリンクを参考にしています。

* [アクティビティの再作成 | Android Developers](https://developer.android.com/training/basics/activity-lifecycle/recreating.html?hl=ja)
* [activity | Android Developers](https://developer.android.com/guide/topics/manifest/activity-element.html?hl=ja#config)

# 解決策 (リスト)

### アクティビティで処理する設定の変更

> アクティビティで処理する設定変更のリストを指定します。実行時に設定の変更が発生すると、デフォルトではアクティビティはシャットダウンおよび再起動されますが、この属性で設定を宣言しておくと、アクティビティの再起動を防止できます。 代わりに、アクティビティは実行中のままになり、アクティビティの onConfigurationChanged() メソッドが呼び出されます。

これは、Activityの属性に以下を設定することで回避することが可能です。

```xml
android:configChanges="orientation|...|..."
```

設定リストは[android:configChanges](https://developer.android.com/guide/topics/manifest/activity-element.html?hl=ja#config)を参考にしてください。

### Activity/Fragmentの状態復帰

簡単なデータであればBundleに入れて保存しておくのがよいと思われます。方法は2つあって、

1. __Intent__ / __Arguments__ に含める
2. `onSaveInstanceState()`, `onRestoreInstanceState()`で復帰する 


#### Activityの起動時にIntentにデータを含める

以下のようにして、起動時にActivityに対してデータを送信しておくと再生成時にもデータは保持されます。

```kotlin
companion object {
  fun createIntent(context: Context): Intent {
    val intent = Intent(context, MyActivity::class.java)
    intent.putExtra(...)
    // ...
  }
}
```

#### Fragmentの起動時にArgumentsにデータを含める

こちらは、IntentのFragment版という感じで、同じようにBundleを含めます。

```kotlin
companion object {
  fun newInstance(context: Context): MyFragment {
    val f = MyFragment()
    val args = Bundle().apply {
      putInt("num", 1)
      // ...
    }
    f.setArguments(args)
    return f
  }
}
```

#### onSaveInstanceState, onSaveInstanceState

__Activity, Fragmentどちらも使えます!!__

```kotlin
override fun onSaveInstanceState(outState: Bundle?) {
  super.onSaveInstanceState(outState)
  // 保存しておきたい値を保存する...
}

  override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    super.onRestoreInstanceState(savedInstanceState)
    // 保存しておいた値から値を取得...
  }
```

#### Fragment#setRetainInstanceを使う

Fragment限定です。いくつか問題があるみたいなので、注意が必要です。詳しくはこちら → [Activity/Fragmentの再生成と画面回転に関するまとめ - Qiita](https://qiita.com/anolivetree/items/cf0f615b4d8b1b4be495)

また、Activityが再生成された際はActivityがnullになるので `getActivity()` などに依存している場合は注意が必要です。その場合はListenerやCallbackなどを定義するといいと思います。(今回は再生成にはあまり関係ないので割愛します)

[Fragment#setRetainInstance](https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean))

#### Android Architecture Componentsを使う

[Android Architecture Components | Android Developers](https://developer.android.com/topic/libraries/architecture/index.html) 
→ __Now 1.0 stable__

Googleがこれらの問題を解決するフレームワークを発表しましたね。自分も使ってみましたが、非常に使いやすいものでした。 こちらは、Activity, Fragmentのライフサイクルとは別に生きているため上にあげたものを気にする必要がなくなりました！
Android Architecture Componentsに関しては、たくさん記事があるのでそちらに譲らせていただきます。

## 参考

* [アクティビティの再作成 | Android Developers](https://developer.android.com/training/basics/activity-lifecycle/recreating.html?hl=ja)
* [実行時の変更の処理 | Android Developers
](https://developer.android.com/guide/topics/resources/runtime-changes.html)
* [activity | Android Developers](https://developer.android.com/guide/topics/manifest/activity-element.html?hl=ja#config)
* [Activity#onConfigurationChanged](https://developer.android.com/reference/android/app/Activity.html?hl=ja#onConfigurationChanged(android.content.res.Configuration))
* [Fragment#setArguments](https://developer.android.com/reference/android/app/Fragment.html#setArguments(android.os.Bundle))
* [Fragment#setRetainInstance](https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean))
* [onSaveInstanceStateについてちゃんと知る - Qiita](https://qiita.com/ntsk/items/6eafe7bd7edbab61341e)
* [Activity/Fragmentの再生成と画面回転に関するまとめ - Qiita](https://qiita.com/anolivetree/items/cf0f615b4d8b1b4be495)