48
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FragmentにおけるActivityとContextの使い分け

Last updated at Posted at 2021-03-06

はじめに

Fragmentにおける ActivityContext の意味や使い分けがわからなかったので、調べてみました。

結論

  • Context で済む
    • nullを許容する → this.context
    • nullを許容しない → requireContext()
  • Activity を渡す必要がある
    • nullを許容する → this.activity
    • nullを許容しない → requireActivity()

環境

  • 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で以下を実行しました。

MonsterListFragment.kt
Timber.d("${this.activity}")
Timber.d("${requireActivity()}")
Timber.d("${this.context}")
Timber.d("${requireContext()}")
Logcatの出力結果
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.activityrequireActivity()this.contextrequireContext() の出力結果は同じです。
つまり同じインスタンスを返しています。

require○○() vs this.○○

requireActivity()requireContext() の実装を見てみます。

Fragment.java
/**
 * 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;
}
Fragment.java
/**
 * 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 でない contextactivity が必要なのに require○○() を使わないのは冗長なので避けるべきです。

MonsterListFragment.kt
class MonsterListFragment : Fragment() {
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // ×冗長
        this.context?.let {
            MaterialAlertDialogBuilder(it)
        }

        // ○簡潔
        MaterialAlertDialogBuilder(requireContext())
    }
}

Activity vs Context

出力結果と require○○() メソッドの実装より、Fragmentが持つ activity の型は FragmentActivitycontext の型は Context ということがわかりました。

FragmentActivity の継承を追っていくと、 Context に辿り着きます。

Context
↑
ContextWrapper
↑
ContextThemeWrapper
↑
Activity
↑
androidx.core.app.ComponentActivity
↑
ComponentActivity
↑
FragmentActivity

できる限り小さく済ませたいので、 Context で済むところに Activity を渡す必要はないと考えます。

Contextの種類

FragmentActivity の継承図より ActivityContext の子孫であることがわかりました。

同様に ApplicationContext の子孫であることがわかります。

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.activitythis.context は同じインスタンス(今回は MainActivity )を参照していると思われます。

つまり 基本的にはどちらを使っても変わりません
this.activitythis.context の中身が異なったり、片方のみ null になるケースがあるかどうかが気になります。

おわりに

本記事の内容はあくまで私が出した結論です。
他にご意見やご指摘などありましたら、お気軽にコメントなどお願いします :pray:

参考リンク

48
31
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?