はじめに
Fragmentにおける Activity と Context の意味や使い分けがわからなかったので、調べてみました。
結論
-
Contextで済む- nullを許容する →
this.context - nullを許容しない →
requireContext()
- nullを許容する →
-
Activityを渡す必要がある- nullを許容する →
this.activity - nullを許容しない →
requireActivity()
- nullを許容する →
環境
- OS:macOS Big Sur 11.1
- Android Studio:4.1.2
- Kotlin:1.4.20
- Gradle:6.8
- Gradle plugin:4.1.2
- Timber:4.7.1
調査
各処理の戻り値
Fragmentで以下を実行しました。
Timber.d("${this.activity}")
Timber.d("${requireActivity()}")
Timber.d("${this.context}")
Timber.d("${requireContext()}")
2021-03-06 19:07:40.162 24680-24680/com.theuhooi.uhooipicbook D/MonsterListFragment: com.theuhooi.uhooipicbook.MainActivity@95e20bb
2021-03-06 19:07:40.162 24680-24680/com.theuhooi.uhooipicbook D/MonsterListFragment: com.theuhooi.uhooipicbook.MainActivity@95e20bb
2021-03-06 19:07:40.163 24680-24680/com.theuhooi.uhooipicbook D/MonsterListFragment: dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper@e47f888
2021-03-06 19:07:40.166 24680-24680/com.theuhooi.uhooipicbook D/MonsterListFragment: dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper@e47f888
this.activity と requireActivity() 、 this.context と requireContext() の出力結果は同じです。
つまり同じインスタンスを返しています。
require○○() vs this.○○
requireActivity() と requireContext() の実装を見てみます。
/**
* Return the {@link FragmentActivity} this fragment is currently associated with.
*
* @throws IllegalStateException if not currently associated with an activity or if associated
* only with a context.
* @see #getActivity()
*/
@NonNull
public final FragmentActivity requireActivity() {
FragmentActivity activity = getActivity();
if (activity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to an activity.");
}
return activity;
}
/**
* Return the {@link Context} this fragment is currently associated with.
*
* @throws IllegalStateException if not currently associated with a context.
* @see #getContext()
*/
@NonNull
public final Context requireContext() {
Context context = getContext();
if (context == null) {
throw new IllegalStateException("Fragment " + this + " not attached to a context.");
}
return context;
}
見てわかる通り、 activity (context) が null だったら例外が発生し、 null でなかったらインスタンスを返す、というシンプルな実装になっています。
渡す先が null を許容する場合、 null のエラーハンドリングを任せられるため、 require○○() を呼ばずに this.○○ で渡すべきだと考えます。
require○○() を呼ぶと例外が発生し、他でハンドリングできなくなるためです。
逆に null でない context や activity が必要なのに require○○() を使わないのは冗長なので避けるべきです。
class MonsterListFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// ×冗長
this.context?.let {
MaterialAlertDialogBuilder(it)
}
// ○簡潔
MaterialAlertDialogBuilder(requireContext())
}
}
Activity vs Context
出力結果と require○○() メソッドの実装より、Fragmentが持つ activity の型は FragmentActivity 、 context の型は Context ということがわかりました。
FragmentActivity の継承を追っていくと、 Context に辿り着きます。
Context
↑
ContextWrapper
↑
ContextThemeWrapper
↑
Activity
↑
androidx.core.app.ComponentActivity
↑
ComponentActivity
↑
FragmentActivity
できる限り小さく済ませたいので、 Context で済むところに Activity を渡す必要はないと考えます。
Contextの種類
FragmentActivity の継承図より Activity が Context の子孫であることがわかりました。
同様に Application も Context の子孫であることがわかります。
Context
↑
ContextWrapper
↑
Application
Contextを区別するため、Applicationのことを「Application Context」、Activityのことを「Activity Context」と呼ぶことがあります。
Fragmentで取得できるContextは「Activity Context」であり、「Application Context」はActivityを介さないと取得できません(間違っていたらご指摘ください)。
Activity からは this.applicationContext でApplication Contextを取得できます。
Application Contextのほうが生存期間が長いので安全です。
しかし基本的にはActivityのライフサイクルに従ってActivity Contextを使うのがよさそうです。
未検証ですが、Fragmentの this.activity と this.context は同じインスタンス(今回は MainActivity )を参照していると思われます。
つまり 基本的にはどちらを使っても変わりません 。
this.activity と this.context の中身が異なったり、片方のみ null になるケースがあるかどうかが気になります。
おわりに
本記事の内容はあくまで私が出した結論です。
他にご意見やご指摘などありましたら、お気軽にコメントなどお願いします ![]()
参考リンク
- Androidの基礎知識 - mixi-inc/AndroidTraining
- Androidで使われているApplication ContextとActivity Contextの使い所まとめ。 - GA technologies GROUP Tech Blog
- https://twitter.com/the_uhooi/status/1368107727961026563?s=20
- https://twitter.com/yt8492/status/1368113367412473860?s=20
- https://twitter.com/the_uhooi/status/1368208335703449603?s=20