UIを持たないFragment その2

前回はUIを持たないFragmentであるNfcFeliCaFragmentを作ったが、今回はそのFragmentを使うActivity側の処理を考えてみる。

Fragmentは自身とActivityを含めた画面の遷移とそのバックスタックを制御する為に"トランザクション"※という概念を導入しており、ActivityにFragmentを追加するためにはトランザクションのコンテキストで実行する必要がある。

  • FragmentをActivityに追加する(NFCTagReader.java)
public class NFCTagReader extends FragmentActivity 
    : 
    :
    FragmentManager fm = this.getSupportFragmentManager();
    NfcFeliCaTagFragment fragment =new NfcFeliCaTagFragment();
    FragmentTransaction  trans = this.getSupportFragmentManager().beginTransaction();
    {
        trans.add(fragment, NfcFeliCaTagFragment.TAG);
        fragment.addNfcTagListener(this);
        trans.commit();
    }

FragmentTransactionを生成した後のブレースはトランザクションの範囲を分かりやすくためだけに書いている。

本来であれば一つのタスクの遷移とバックスタックのまとまりとしてFragmentを追加する(5つのFragmentをcommit前に追加すると、一度のバックで5つのFragmentが全て戻る)が、今回はUIとしては使わないので追加するまとまりは重要ではない。むしろ、追加したFragmentをどのように取得できるかだ。なお、UIを持たないFragmentの場合、トランザクションにはFragmentを一意に識別するための名前をキーとして使う。キーにはNfcFeliCaTagFragmentクラスで定義したTAGフィールド(同クラス名と同じ)を用いている。

トランザクションに追加されたFragmentは、その後FragmentManagerクラスのファインダメソッドを取得することができるが、UIを持たないFragmentの場合は追加した時同様にキーである文字列を用いる。

  • 登録した際のキー(名前)からFragmentを取得する
    FragmentManager fm = this.getSupportFragmentManager();
    NfcFeliCaTagFragment fragment =  (NfcFeliCaTagFragment)fm.findFragmentByTag(NfcFeliCaTagFragment.TAG);

ならばトランザクションに追加した後であれば、いつでも上記のコードでFragmentを取得できるのだろうか? 否。

実際に組んでみれば分かるが、以下のようにActivityが生成されるタイミングでFragmentを生成、トランザクションに追加した直後は、findFragmentByTagメソッドの戻り値はnullになる。

  • 追加直後では参照できないFragment
public class NFCTagReader extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //使用するタグフラグメントを名前で取得
        NfcTagFragment fragment = this.obtainNfcFeliCaFragment(NfcFeliCaTagFragment.TAG);
        Parcelable nfcTag = fragment.getNfcTag(); // !!! NullPointerException !!! 
        :

    }
    private NfcFeliCaTagFragment obtainNfcFeliCaFragment(String tag) {
        FragmentManager fm = this.getSupportFragmentManager();
        NfcFeliCaTagFragment fragment =  (NfcFeliCaTagFragment)fm.findFragmentByTag(tag);
        if ( fragment == null ) {
            fragment = new NfcFeliCaTagFragment();
            FragmentTransaction  trans = 
                    this.getSupportFragmentManager().beginTransaction();
            {
                trans.add(fragment, TAG);
                fragment.addNfcTagListener(this);
            }
            trans.commit();
        }
        return fragment;
    }

トランザクションはコミットしているのにどうしてFragmentはnullのままなのだろう。次回はこの辺を分析して回避する方法を探ってみよう。


トランザクションと言えば有名なのはデータベース等のACID:原子性(Atomicity)、一貫性(Consistency)、独立性(Isolation)、永続性(Durability)を実現するための概念だが、ここではActivityへ追加するFragmentをひとまとまりとして管理するために使用している。なのでコミット(commit)はあるがロールバックは無い。