1. chibi929

    Posted

    chibi929
Changes in title
+ActivityとFragmentのライフサイクルと罠
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,218 @@
+ActivityとFragmentのライフサイクルを意識しなければならない状況で、
+思っていたライフサイクルとは異なるフローで呼ばれていたため、
+調査をしてみました。(主に破棄周り)
+
+http://developer.android.com/guide/components/fragments.html
+> The lifecycle of the activity in which the fragment lives directly affects the lifecycle of the fragment, such that each lifecycle callback for the activity results in a similar callback for each fragment. For example, when the activity receives onPause(), each fragment in the activity receives onPause().
+
+上記を読んで、
+Activityが `onPause()` を受け取ったときに Fragmentも `onPause()` を受け取る。となっていますが、
+ActivityよりもFragmentのほうが短命のはずなので、
+破棄時は `Fragment.onDestroy()` の後に `Activity.onDestroy()` となると思っていました。
+※逆に起動時は `Activity.onCreate()` の後に `Fragment.onCreate()` となると思っていました。
+
+# 前置き
+
+まずは、公式リファレンスのライフサイクルのおさらい。
+(画像を貼るだけ)
+
+## Activityのライフサイクル
+
+http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
+![activity_lifecycle.png](https://qiita-image-store.s3.amazonaws.com/0/53393/7264706d-ccd7-b57a-3eca-cdf4c6675a3d.png)
+
+## Fragmentのライフサイクル
+
+http://developer.android.com/guide/components/fragments.html#Creating
+![fragment_lifecycle.png](https://qiita-image-store.s3.amazonaws.com/0/53393/a23fa119-b41b-f7a2-b940-5ca1a79157e4.png)
+
+# ライフサイクル調査
+
+さて、ここからActivityとFragmentを使ったライフサイクルを調査していきます。
+冒頭に記載した通り、主に破棄周りについて。
+
+**環境**
+
+* Android Studio: 1.3
+* Mininum SDK: API 15 Android 4.0.3
+
+**予想**
+
+* `Fragment.onDestroyView()` -> `Fragment.onDestroy()` -> `Fragment.onDetach()` -> `Activity.onDestroy()`
+
+**調査方法**
+
+* Activityの起動から終了までのコールバックを追います。
+
+**調査対象は2種類**
+
+* `android.app.Activity` を継承したActivityに `android.app.Fragment` を追加する
+ * 便宜上 `AppActivity` , `AppFragment` とします。
+* `android.support.v4.app.FragmentActivity` を継承したActivityに `android.support.v4.app.Fragment` を追加する
+ * 便宜上 `V4AppActivity` , `V4AppFragment` とします。
+
+## ActivityとFragment
+
+```java:AppActivityjava
+public class AppActivity extends LifeCycleActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_app);
+
+ if (savedInstanceState == null) {
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.add(R.id.container, new AppFragment());
+ ft.commit();
+ }
+ }
+}
+```
+
+※基底クラスの `LifeCycleActivity` は `Activity` を継承して各種コールバックのログを出力しているクラスです。
+
+```java:AppFragment.java
+public class AppFragment extends LifeCycleFragment {
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ return inflater.inflate(R.layout.fragment_app, container, false);
+ }
+}
+```
+
+※基底クラスの `LifeCycleFragment` は `android.app.Fragment` を継承して各種コールバックのログを出力しているクラスです。
+
+```:logcat
+// ここから起動処理
+D/LifeCycleActivity( 3575): onCreate
+D/LifeCycleFragment( 3575): onAttach
+D/LifeCycleFragment( 3575): onCreate
+D/LifeCycleFragment( 3575): onCreateView
+D/LifeCycleFragment( 3575): onActivityCreated
+D/LifeCycleActivity( 3575): onStart
+D/LifeCycleFragment( 3575): onStart
+D/LifeCycleActivity( 3575): onResume
+D/LifeCycleFragment( 3575): onResume
+// ここから終了処理
+D/LifeCycleFragment( 3575): onPause
+D/LifeCycleActivity( 3575): onPause
+D/LifeCycleFragment( 3575): onStop
+D/LifeCycleActivity( 3575): onStop
+D/LifeCycleFragment( 3575): onDestroyView
+D/LifeCycleFragment( 3575): onDestroy
+D/LifeCycleFragment( 3575): onDetach
+D/LifeCycleActivity( 3575): onDestroy
+```
+
+※ログはActivityの起動から終了までです。
+
+予想通り、
+起動処理は `Activity` -> `Fragment` の順
+終了処理は `Fragment` -> `Activity` の順
+
+## FragmentActivityとFragment
+
+```java:V4AppActivity
+public class V4AppActivity extends LifeCycleFragmentActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_v4_app);
+
+ if (savedInstanceState == null) {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.add(R.id.container, new V4AppFragment());
+ ft.commit();
+ }
+ }
+}
+```
+
+* 前者との違い
+ * 継承元が異なる( `extends Activity` が `extends FragmentActivity` に変わっている )
+ * `getFragmentManager()` が `getSupportFragmentManager()` に変わっている
+
+※基底クラスの `LifeCycleFragmentActivity ` は `FragmentActivity` を継承して各種コールバックのログを出力しているクラスです。
+
+```java:V4AppFragment
+public class V4AppFragment extends LifeCycleV4Fragment {
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ return inflater.inflate(R.layout.fragment_v4_app, container, false);
+ }
+}
+```
+
+* 前者との違い
+ * 継承元が異なる( `extends android.app.Fragment` が `extends android.support.v4.app.Fragment` に変わっている )
+
+※基底クラスの `LifeCycleV4Fragment ` は `android.support.v4.app.Fragment` を継承して各種コールバックのログを出力しているクラスです。
+
+```:logcat
+// ここから起動処理
+D/LifeCycleFragmentActivity( 3575): onCreate
+D/LifeCycleFragmentActivity( 3575): onStart
+D/LifeCycleV4Fragment( 3575): onAttach
+D/LifeCycleV4Fragment( 3575): onCreate
+D/LifeCycleV4Fragment( 3575): onCreateView
+D/LifeCycleV4Fragment( 3575): onActivityCreated
+D/LifeCycleV4Fragment( 3575): onStart
+D/LifeCycleFragmentActivity( 3575): onResume
+D/LifeCycleV4Fragment( 3575): onResume
+// ここから終了処理
+D/LifeCycleFragmentActivity( 3575): onPause
+D/LifeCycleV4Fragment( 3575): onPause
+D/LifeCycleFragmentActivity( 3575): onStop
+D/LifeCycleV4Fragment( 3575): onStop
+D/LifeCycleFragmentActivity( 3575): onDestroy
+D/LifeCycleV4Fragment( 3575): onDestroyView
+D/LifeCycleV4Fragment( 3575): onDestroy
+D/LifeCycleV4Fragment( 3575): onDetach
+```
+
+※ログはActivityの起動から終了までです。
+
+ここが予想外でした!
+起動処理も終了処理も `Activity` -> `Fragment` の順
+
+# Activityの状態とFragmentのライフサイクル
+
+色々なサイトを巡ってライフサイクルを確認しましたが、
+下記に記載した参考サイトでもライフサイクルがマチマチだし、
+
+以下のデベロッパーサイトの図を見ても、
+Activityのステートが `Destroyed` の時に、Fragmentの `onDestroyView()` , `onDestroy` , `onDetach()` となっているので、
+Activityの `onDestroy()` が来るタイミングは「Fragmentより前!」とか「Fragmentより後!」とかは無いのかなぁ。
+
+今のところ Activity を使った場合のログの出力のされ方は毎回一定だし、
+FragmentActivity を使った場合のログの出力のされ方も毎回一定で、
+タイミングによって変化してるようには見えない。
+
+http://developer.android.com/guide/components/fragments.html#Lifecycle
+![activity_fragment_lifecycle.png](https://qiita-image-store.s3.amazonaws.com/0/53393/7a1ba6dd-13d2-8491-08bb-56da5d621235.png)
+
+今回は `API 15: Android 4.0.3 IceCreamSandwich` でプロジェクトを作成したからFragmentActivityが継承されたMainActivityが出来たのだと思われる。
+`API 22: Android 5.1 Lollipop` では、どうやらデフォルトでActivityが継承されるらしい。
+きっと `Android 6.0 Marshmallow(マシュマロ)` でもActivityが継承されたものがデフォルトで作成される。
+
+~~(実は `API 22(Lillipop)` や `API 23(Marshmallow)` では、今回調査対象した2種類両方とも同じコールバックだったりして...)~~
+調べてみると、 `API 22(Lillipop)` や `API 23(Marshmallow)` では、
+デフォルトでは `appcompat-v7` とか `support-v4` 使われていませんでした。
+但し、自分でライブラリを追加して使うことができるので、試してみたところ上記調査結果通りのログになりました。
+
+どちらにしても使い方に気を付けなければ!!
+
+# 最後に
+
+今回の調査で作ったサンプルアプリです。
+https://github.com/chibi929/AndroidLifeCycle
+
+# 参考サイト
+
+http://techbooster.org/android/environment/17001/
+http://qiita.com/suzukihr/items/90a93e79dc67c585de75
+http://yuki312.blogspot.jp/2012/02/androidfragmentactivity.html