参考:Advocating Against Android Fragments
Fragment の基本
Fragment といえば、Honeycomb から導入された、多様なデバイスへ対応するための仕組みで、Activity のライフサイクルに合わせて動作するモジュールのようなものです。
Fragment として細かく分けたモジュールを組み合わせることで、多様なデバイスへ対応しやすくする、という寸法です。
ライフサイクル
Activity に付随して動くので、Fragment も勿論ライフサイクルを持つのですが、Activity よりもはるかに複雑なものとなっています。
Square のエンジニアブログに曰く、WTFs/min = 2^fragment count
であると。
トランザクションとインスタンスの管理
Fragment Transactions によって、Fragment の操作をまとめ、DB のトランザクション処理のように扱うことができます。
最終的に一連の操作は MainThread 上で実行されるため、commit()
操作は Handler のキューに操作のまとまりを積むことになります。
ということは、Handler のキューの状態次第で、操作が完了するまでにラグが生じる可能性が出てきます。そのラグの間、Fragment の状態は不定です。不定であるということは、そこにデバッグを困難にする要素があるということです。
レイアウトに直接組み込む以外に、普通にコンストラクタを使ってインスタンスが生成できます。この時、デフォルトコンストラクタがリフレクションによってアクセス可能でなければ、インスタンスの状態の復帰に失敗します。つまり、匿名インナークラスや、引数のあるコンストラクタを Fragment に持たせる事はできませんし、setter によって依存の注入をする方法も良い方法ではないといえます。
いよいよ、Fragment のテストがしづらいことが見えてきたことでしょう。
最後に、Fragment は Honeycomb からの仕組みですので、2.x 系で使いたい場合は support library が必要であることを付け加えておきます。
View
一方で、自分で好きな View を作ることもできます。これは初期の Android Framework から標準でそのように作られている仕組みです。
Fragment をレイアウトに組み込んで使うという考え方で見ると、独自 View でもほぼ同じことができます。Fragment 内で受け取ったデータを TextView に流しこむのと、View 内で受け取ったデータを TextView に流しこむのとに、差異はそれほどないはずです。むしろ、プレゼンテーションの管理をするだけであれば、View がその役割を一手に担うほうが見通しがよいでしょう。
ライフサイクル
View もライフサイクルを持ちます。少し独特な面もありますが、状態の保存と復帰など、Fragment にあるものは View にもあります。
リソースの Qualifier
Fragment を使うにしても View を使うにしても、レイアウトリソースを画面ごとに作り分けることは必要です。つまり、リソースさえ綺麗に分離できれば、Fragment だろうと View だろうと、ほぼ変わらない事ができるということです。
もはや Fragment だけが多様なデバイスへ対応するための方法ではないということが見えてきたと思います。
実は最初から十分に対応可能だった
Fragment だけでなく、Custom View でも多様なデバイスへ対応するための設計は十分に可能です。むしろ、複雑な Fragment の仕組みを使うより、既存の View の仕組みを使うほうが、いくらか幸せになれそうな気さえします。新しい API は必要ありませんし。
待った!
それでも Fragment が役に立つ場面があります。Dialog がその代表でしょう。
AlertDialogs
プロンプトを表示して判断を促す場合に AlertDialog を使うならば、DialogFragment は必須でしょう。FragmentManager がよくその面倒を見てくれるはずです。ただし、どの選択肢を選んだかをコールバックで対応する場合はよくよく注意して実装する必要があります。
ProgressDialogs
プログレスはどうでしょう。今や、ProgressDialog はその存在自体を黒歴史のように扱われています。ProgressBar を表示して、UI の状態を変更するだけでも十分に意味を持ち、わざわざダイアログとして見せる必要性は薄れています(そしてこれらのことをするためのライブラリもいくつか公開されています)。
非同期処理
Loader を使っていい感じに非同期処理をしたいのならば、Fragment が役に立つでしょう。しかし、Activity でも同じことができます。最終的に非同期処理の結果を View にバインドする仕事をどこに持たせるかが重要になり、それは多くの場合 View のしごとになるはずですから、あえて Fragment を挟む必要はないかもしれません。
さいごに
Fragment にせよ、View にせよ、いずれにしてもビジネスロジックを分離したり、View のロジックを分離したりと言ったことは必要不可欠です。Dagger や Mortar など、これを支援する仕組みは沢山あるので、是非活用していきたいところです。