ネストされたフラグメントと画面回転

Androidアプリで support library v4 を使用している場合、ライブラリの revision によっては、ネストされている子フラグメントが画面回転等の際に失われてしまう問題があるようです。

画面回転等で Activity が再生成される場合でもフラグメントのインスタンスを保持しておくよう、setRetainInstance(true) の指定を行うことがありますが、そのようなフラグメントがネストされた子フラグメントを持つ場合、Activity 再生成のタイミングで子フラグメントが失われてしまう場合があるようです。

Issue 74222 – android – Nested fragments not being retained in support library v4 rev. 20 –
Android Open Source Project – Issue Tracker – Google Project Hosting

この問題は support library v4 の rev.20 から発生しているようで、Fragment クラスで保持している ChildFragmentManager 自体が失われてしまうといったことが起きているようです。 ( 現時点の最新版である rev.22.1.1 でも解消されていない模様。 )

Issue Tracker でこの件についてサンプル対処コード添えてコメントされている方が、自身のブログでもこの問題について言及されていました。

Tech Footprint: Nested Fragment with ChildFragmentManager lost state in rev20/rev21 of Android support library

以下、リンク先からの引用コードです。

public class NestingFragment extends Fragment{
    //As we setRetainInstanceState(true), this field will hold 
    //the reference of the old ChildFragmentManager
    private FragmentManager mRetainedChildFragmentManager;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        if (mRetainedChildFragmentManager != null) {
            //restore the last retained child fragment manager to the new 
            //created fragment
            try {
                Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
                childFMField.setAccessible(true);
                childFMField.set(this, mRetainedChildFragmentManager);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            mRetainedChildFragmentManager = getChildFragmentManager();
        }
    }
}

フラグメントがアクティビティに attach された際に ChildFragmentManager の参照を保持しておき、再生成の際にその参照をリフレクションで直接フィールドに書き戻しているようです。

本来ならアクセスできないフィールドにアクセスするコードであるため、環境によって動作しない可能性や今後フィールド名の変更等により動作しなくなる恐れもありますが、とりあえず今回はこのコードを参考にさせて頂き、自身で作成している基底フラグメントにこのようなコードを実装することでこの問題を回避することにしました。

最近になってようやく Android アプリの本格的な開発に携わるようになったのですが、経験が浅いためちょっと凝ったことをしようとすると色々と難しい問題に直面しますね。

カテゴリー: アプリケーション開発 | タグ: , | コメントをどうぞ

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です