Android マルチウインドウのライフサイクル

# （追記）
2017/1/17 に次のケースを追加しました。

- マルチウインドウ ⇒ シングルウインドウ
- シングルウインドウ ⇒ マルチウインドウ

# この記事について
Android N のマルチウインドウでの Application、Activity、Fragment のライフサイクルについて調査しました。

コンポーネントの各コールバックでログを出力するサンプルアプリを作成し、マルチウインドウでの様々なケースでどのようなライフサイクルを持っているか、について書いています。

なお、マルチウインドウの基本的な事項については説明していません。マルチウインドウについて知りたい場合には次の記事を参照してください。
https://developer.android.com/guide/topics/ui/multi-window.html?hl=ja

# まとめ
調査によって分かったことは次の通りです。

- シングルウインドウからマルチウインドウ化した場合、
  - Application は影響を受けない
  - Activity と Fragment は再生成される
      - ただし、この挙動は android:configChanges で制御可能
- マルチウインドウで画面を回転した場合、
  - フォーカスが外れている方の Activity の再生成処理から始まる
  - フォーカスが外れている方の Activity は onResume() まで呼ばれてから onPause() が呼ばれる
  - その後、フォーカスを持っているウインドウの Activity が再生成処理が始まる
- 同じアプリがマルチウインドウで複数 Activity が起動する場合、
  - Application は１度しか起動しない
- マルチウインドウ ⇒ シングルウインドウにした場合、
  - フォーカスを持っている Activity は破棄され、再生成されます。
  - フォーカスが外れた Activity は破棄されません。onStop() まで呼ばれるだけです。

# 実験
## サンプルコード
サンプルコードは次のリポジトリにあります。

https://github.com/hshiozawa/MultiWindowLifecycleTester

## 構成

サンプルアプリは、次の Application、Activity、Fragment から構成されています。

- Application
  - _MyApplication_
- Activity
  - _MainActivity_
  - _SubActivity_
- Fragment
  - _MainFragment_
  - _SubFragment_

### MainActivity
MainActivity はランチャーからのアプリ起動によって起動する Activity です。
また、MainActivity 上にはフラグメントとして MainFragment が起動します。

### SubActivity
SubActivity は他のアプリから起動される Activity です。
また、SubActivity 上にはフラグメントとして SubFragment が起動します。

## 環境
今回の調査環境は次の通りです。

- Nexus 6P
- Android 7.1.1

# 結果
次のような状況で、各コンポーネントのライフサイクルについて調査しました。

1. ランチャーからの起動
+ マルチウインドウへの分割
+ 複数起動
  - フォーカスの移動
  - 画面の回転
+ SubActivity の終了
+ MainActivity の終了

## ランチャーからの起動
通常のランチャーからアプリを起動した場合です。
この状況では、サンプルアプリは次のような画面になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/aff0923a-e7d0-ef31-185f-3025381e98a3.png" width=300px/>

このケースでのライフサイクルは次のようになります。

```
MyApplication: onCreate()
MainActivity: onCreate()
MainFragment: onCreate()
MainFragment: onCreateView()
MainFragment: onStart()
MainActivity: onStart()
MainActivity: onResume()
MainFragment: onResume()
```

このケースに関して特に特筆するべき点はありません。
よく知られたライフサイクルです。

## マルチウインドウ化
次に、マルチウインドウ化した場合です。
サンプルアプリをマルチウインドウ化し、もう一つのウインドウには ES File Explorer を開きました。
この状況では、サンプルアプリは次のような画面になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/d6ce5a50-dbe0-68ee-70e3-92a29b8c7b30.png" width=300px/>

このケースでのライフサイクルは次のようになります。

```
// 破棄処理の開始
MainFragment: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w411dp h659dp 560dpi nrml port finger -keyb/v/h -nav/h s.12}
MainActivity: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w411dp h659dp 560dpi nrml port finger -keyb/v/h -nav/h s.12}
MainFragment: onPause()
MainActivity: onPause()
MainFragment: onStop()
MainActivity: onStop()
MainFragment: onDestroyView()
MainFragment: onDestroy()
MainActivity: onDestroy()
// 再生成処理の開始
MainFragment: onCreate()
MainActivity: onCreate()
MainFragment: onCreateView()
MainFragment: onCreate()
MainFragment: onCreateView()
MainFragment: onDestroyView()
MainFragment: onDestroy()
MainFragment: onStart()
MainActivity: onStart()
MainActivity: onResume()
MainFragment: onResume()
MainFragment: onPause()
MainActivity: onPause()
```

このケースでは、MainFragment と MainActivity が破棄され、MainFragment と MainActivity が再生成されていることが分かります。
この時、MainApplication は再生成されていません。マルチウインドウ化の際は Application は再生成されず、Activity のみ再生成されます。

最後に MainActivity と MainFragment は onResume() が呼ばれたあとに、onPause() が呼ばれている点には注意が必要です。
マルチウインドウ化すると元のウインドウからフォーカスが外れます。
そのため、一度 onResume() が呼ばれてから onPause() が呼び出されるような挙動になっています。


### android:configChanges 指定
マルチウインドウ化時に Activity を再生成しない方法もあります。
AndroidManifest.xml の該当 Activity に次のように指定すると、Activity の再生成は行われません。

```
android:configChanges="screenLayout|orientation"
```

これを指定するとActivity は破棄されず、次のようなライフサイクルになります。

```
MainFragment: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w411dp h659dp 560dpi nrml port finger -keyb/v/h -nav/h s.4}
MainActivity: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w411dp h659dp 560dpi nrml port finger -keyb/v/h -nav/h s.4}
MainFragment: onPause()
MainActivity: onPause()
MainFragment: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw360dp w411dp h324dp 560dpi smll land finger -keyb/v/h -nav/h s.4}
MainActivity: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw360dp w411dp h324dp 560dpi smll land finger -keyb/v/h -nav/h s.4}
```


## 複数起動
次に、同一のアプリをマルチウインドウで複数起動した場合です。
これは他のアプリから連携起動が可能なアプリなどで起こりうるケースです。

片方のウインドウに MainActivity が起動している状態で、他のアプリからサンプルアプリの SubActivity を起動します。
この状況では、サンプルアプリは次のような画面になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/3dd03dcd-d4da-8f8c-1e9b-68ddaca59f0a.png" width=300px/>

この状況でのライフサイクルは次のようになります。

```
SubActivity: onCreate()
SubFragment: onCreate()
SubFragment: onCreateView()
SubFragment: onStart()
SubActivity: onStart()
SubActivity: onResume()
SubFragment: onResume()
```

SubActivity の起動時には MainApplication は起動しません。

また、もともと SubActivity 側のウインドウにフォーカスがあありました。
そのため MainActivity 側では onPause() が呼ばれていません。

### フォーカスの移動
フォーカスを移動すると次のようなライフサイクルになります。

```
// Main から Sub へフォーカスを移動
MainFragment: onPause()
MainActivity: onPause()
SubActivity: onResume()
SubFragment: onResume()
// Sub から  Main へフォーカスを移動
SubFragment: onPause()
SubActivity: onPause()
MainActivity: onResume()
MainFragment: onResume()
```

フォーカスが移動されると、フォーカスがあった画面の Activity は onPause() が呼び出されます。
フォーカスが移動した画面の Activity は onResume() が呼び出されます。

### 画面を回転
MainActivity にフォーカスがある状態で画面を回転してみます。
画面を回転すると次のような画面になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/1c04b085-cac5-fd6c-05e7-26bbc70a0641.png" width=400">

この時のライフサイクルは次のようになります。

```
I/MyApplication: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w683dp h387dp 560dpi nrml land finger -keyb/v/h -nav/h s.15}
// SubActivity の再生成処理
SubFragment: onStop()
SubActivity: onStop()
SubFragment: onDestroyView()
SubFragment: onDestroy()
SubActivity: onDestroy()
SubFragment: onCreate()
SubActivity: onCreate()
SubFragment: onCreateView()
SubFragment: onCreate()
SubFragment: onCreateView()
SubFragment: onDestroyView()
SubFragment: onDestroy()
SubFragment: onStart()
SubActivity: onStart()
SubActivity: onResume()
SubFragment: onResume()
SubFragment: onPause()
SubActivity: onPause()
// MainActivity の再生成処理
MainFragment: onPause()
MainActivity: onPause()
MainFragment: onStop()
MainActivity: onStop()
MainFragment: onDestroyView()
MainFragment: onDestroy()
MainActivity: onDestroy()
MainFragment: onCreate()
MainActivity: onCreate()
MainFragment: onCreateView()
MainFragment: onCreate()
MainFragment: onCreateView()
MainFragment: onDestroyView()
MainFragment: onDestroy()
MainFragment: onStart()
MainActivity: onStart()
MainActivity: onResume()
MainFragment: onResume()
```

まず、SubActivity の再生成処理が行われていることが分かります。
SubActivity 側は onResume() が呼び出された後に onPause() が呼びされています。

その後、MainActivty の再生成処理が行われています。
すなわち、フォーカスがない方のウインドウの Activity の再生成処理が最初に行われることが分かります。


## SubActivity の終了
SubActivity 側を終了します。
終了すると次のような画面になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/d6ce5a50-dbe0-68ee-70e3-92a29b8c7b30.png" width=300px/>

この時のライフサイクルは次のようになります。

```
I/SubFragment: onPause()
I/SubActivity: onPause()
I/SubFragment: onStop()
I/SubActivity: onStop()
I/SubFragment: onDestroyView()
I/SubFragment: onDestroy()
I/SubActivity: onDestroy()
```

MainActivity や MainApplication は影響を受けず、SubActivity だけが破棄されることが分かります。

## MainActivity の終了
最後に MainFragment を終了します。
この時のライフサイクルは次のようになります。

```
I/MainFragment: onPause()
I/MainActivity: onPause()
I/MainFragment: onStop()
I/MainActivity: onStop()
I/MainFragment: onDestroyView()
I/MainFragment: onDestroy()
I/MainActivity: onDestroy()
```


## マルチウインドウ ⇒ シングルウインドウ
マルチウインドウ状態からシングルウインドウ状態に戻した場合のライフサイクルです。

まず、下記のような状況にして、

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/3dd03dcd-d4da-8f8c-1e9b-68ddaca59f0a.png" width=300px/>

真ん中のバーをひっぱって、下記のような状態にします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/41604/aff0923a-e7d0-ef31-185f-3025381e98a3.png" width=300px/>

この場合のライフサイクルは次のようになります。

```
MainFragment: onPause()
MainActivity: onPause()
MainFragment: onStop()
MainActivity: onStop()
MainFragment: onDestroyView()
MainFragment: onDestroy()
MainActivity: onDestroy()
MainFragment: onCreate()
MainActivity: onCreate()
MainFragment: onCreateView()
MainFragment: onCreate()
MainFragment: onCreateView()
MainFragment: onDestroyView()
MainFragment: onDestroy()
MainFragment: onStart()
MainActivity: onStart()
MainActivity: onResume()
MainFragment: onResume()
MainFragment: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w411dp h659dp 560dpi nrml port finger -keyb/v/h -nav/h s.22}
MainActivity: onConfigurationChanged(): {1.0 ?mcc?mnc [ja_JP] ldltr sw411dp w411dp h659dp 560dpi nrml port finger -keyb/v/h -nav/h s.22}
SubFragment: onStop()
SubActivity: onStop()
```

まず、シングルウインドウになるため MainActivity が破棄されたあとに再生成されます。

この時、SubActivity 側は Activity の再生成処理は行われていません。単に onStop() が呼ばれただけです。

この状態からタスクスイッチで SubActivity を選択すると次のように SubActivity は再生成されます。

```
SubFragment: onDestroyView()
SubFragment: onDestroy()
SubActivity: onDestroy()
SubFragment: onCreate()
SubActivity: onCreate()
SubFragment: onCreateView()
SubFragment: onCreate()
SubFragment: onCreateView()
SubFragment: onDestroyView()
SubFragment: onDestroy()
SubFragment: onStart()
SubActivity: onStart()
SubActivity: onResume()
SubFragment: onResume()
```