12777


自作のアプリを実機へインストールしています。  
端末の電源ボタンを押し休止モードから復帰したところ、例外が発生し落ちたのでこの問題を修正したいのですが、原因がわかりません。

以下のようなコードで、 `onDestroy` 中の `myfield` が `null` であるという例外が発生していました。  
(crashlyticsが拾ったスタックトレースを最後に添付しています。)

[code]

    
    public class MyFragment extends Fragment {
    
        private MyClass myfield;
    
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
    ...
            myfield = new MyClass();
    ...
            return view;
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            myfield.onDestroyView();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            myfield.onDestroy();
        }
    }
    
    
[/code]

`myfield`への代入はこの1箇所のみであり、`onCreateView`が実行されたのであれば`null`になることはないはずだと考えております。  
また、コードを変更し`onCreateView`内で例外が発生するようにしてみたところ、`onDestroy`は呼ばれないことも確認しました。  
(追記)加えて、`onDestroyView`内でも`myfield`のメソッドを呼ぶ実装をしておりますが、ここでは例外が発生していません(そもそも呼ばれていない？)。

休止から復帰したタイミング、というところになにか通常には無いフローがあるような気もするのですが、 `onCreateView` が呼ばれずに
`onDestroy` が呼ばれるような条件はあるのでしょうか。  
また、ありえる場合、どのような記述で対応するのが一般的なのでしょうか。

[code]

    java.lang.RuntimeException: Unable to destroy activity {my.app/my.app.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void my.app.MyClass.onDestroy()' on a null object reference
           at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3706)
           at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3724)
           at android.app.ActivityThread.access$1400(ActivityThread.java:151)
           at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357)
           at android.os.Handler.dispatchMessage(Handler.java:102)
           at android.os.Looper.loop(Looper.java:135)
           at android.app.ActivityThread.main(ActivityThread.java:5254)
           at java.lang.reflect.Method.invoke(Method.java)
           at java.lang.reflect.Method.invoke(Method.java:372)
           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void my.app.MyClass.onDestroy()' on a null object reference
           at my.app.MyFragment.onDestroy(MyFragment.java:83)
           at android.support.v4.app.Fragment.performDestroy(Fragment.java:2006)
           at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1076)
           at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
           at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
           at android.support.v4.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:1954)
           at android.support.v4.app.FragmentActivity.onDestroy(FragmentActivity.java:313)
           at android.support.v7.app.ActionBarActivity.onDestroy(ActionBarActivity.java:169)
           at my.app.MainActivity.onDestroy(MainActivity.java:97)
           at android.app.Activity.performDestroy(Activity.java:6169)
           at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1141)
           at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3693)
           at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3724)
           at android.app.ActivityThread.access$1400(ActivityThread.java:151)
           at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357)
           at android.os.Handler.dispatchMessage(Handler.java:102)
           at android.os.Looper.loop(Looper.java:135)
           at android.app.ActivityThread.main(ActivityThread.java:5254)
           at java.lang.reflect.Method.invoke(Method.java)
           at java.lang.reflect.Method.invoke(Method.java:372)
           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
    
[/code]


CC BY-SA 3.0
2015-07-28T11:05:04.047
12774
2015-07-28T13:56:38.643
2015-07-28T11:22:46.363
2808
2808
question
android android-fragments android-lifecycle
Android Fragment で onCreateView が呼ばれずに onDestroy が呼ばれることはありますか


`Fragment`は必ずUIを持つ要素ではありません。`Activity`のView階層と紐付かない`Fragment`を作ることもでき、この場合は`onCreateView()`は呼ばれません。

以下、公式ドキュメントの、[Adding a fragment without a
UI](http://developer.android.com/intl/ja/guide/components/fragments.html#Adding)の内容です。

> To add a fragment without a UI, add the fragment from the activity using
> add(Fragment, String) (supplying a unique string "tag" for the fragment,
> rather than a view ID). This adds the fragment, but, because it's not
> associated with a view in the activity layout, **it does not receive a call
> to onCreateView()**. So you don't need to implement that method.

`onCreateView()`で保持したフィールドは、対応する`onDestroyView()`で解放するのがベターかと思います
~~が、`onDestroyView()`も呼び出されないとのことですので、`Activity`と`Fragment`を紐付けるロジックに問題があるように思います。

  * `add (int containerViewId, Fragment fragment, String tag)`
  * `replace (int containerViewId, Fragment fragment, String tag)`

を使わず、

  * `add (Fragment fragment, String tag)`

を用いつつ、`getView()`で取得した`View`を自前で`addSubview()`していないでしょうか。~~

**UPDATE:**
@fkmさんの回答より、`FragmentPagerAdapter`のcache実装に由来する不都合と思われるので、後半を取り消します。


CC BY-SA 3.0
2015-07-28T11:42:05.650
12775
2015-07-28T13:56:38.643
2015-07-28T13:56:38.643
5337
5337
12774
answer


再現しました。

## 前提

`FragmentPagerAdapter`は次のように、インデックスを伝えて各ページを生成してるとします。

[code]

    public class MyPagerAdapter extends FragmentPagerAdapter {
        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
        }
    
        @Override
        public Fragment getItem(int i) {
            return ItemFragment.newInstance(i);
        }
    
        @Override
        public int getCount() {
            return 10;
        }
    }
    
[/code]

この状態で、１ページだけスワイプで切り替えた状態で、ホームボタンで中断します。当然ですがActivityは保持しない設定にしておきます。

LogCatを貼って挙動を説明します。

[code]

    07-28 22:22:24.977    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=0
    07-28 22:22:24.977    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=1
    07-28 22:22:24.978    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=2
    07-28 22:22:24.978    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=3
    07-28 22:22:31.172    8859-8859/com.mokelab.pager V/Item﹕ onCreateView index=1
    07-28 22:22:31.173    8859-8859/com.mokelab.pager V/Item﹕ onCreateView index=2
    07-28 22:22:31.174    8859-8859/com.mokelab.pager V/Item﹕ onCreateView index=3
    07-28 22:22:35.162    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=0
    07-28 22:22:35.162    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=1
    07-28 22:22:35.162    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=2
    07-28 22:22:35.163    8859-8859/com.mokelab.pager V/Item﹕ onDestroy index=3
    
[/code]

まず、一度でも`attach`された`Fragment`の`onDestroy()`が呼ばれているのがわかります。なので、`index=0`も`onDestroy()`が呼ばれています。

そしてアプリスイッチで再開すると、`index=0`の`Fragment`の`View`は生成する必要のない位置にいるので、1〜3の`onCreateView()`が呼ばれていますね。

そしてこの状態で再度ホームボタンで中断すると、やはり`index=0`の`Fragment`は復元されているので、`onDestroy()`が呼ばれています。


CC BY-SA 3.0
2015-07-28T13:28:49.893
12777
2015-07-28T13:28:49.893
845
12774
answer