前人未踏の領域へ Androidアプリ開発編

Androidアプリ開発に関する調査メモ置き場。古い記事にはアプリ以外も含まれます。

Android: HiltでFragmentをInjectionするとcontextの中身が変わる件

内容

Android開発時にFragmentにHiltの @AndroidEntryPoint アノテーションを付けてインジェクションを行った際、requireContextを呼んだ場合など context の中身が ViewComponentManager.FragmentContextWrapper になる。

これの何が問題かというと、これまで contextの中身はActivityと相場が決まっていたので if (context is Activity) やら context as Activity などが随所に書かれているが、これがfalseになったりクラッシュしたりする。

対応(新)

HiltはActivityのContextを取得する方法を提供してくれている。

@AndroidEntryPoint
class SampleFragment : Fragment() {

    @Inject
    @ActivityContext
    lateinit var activityContext: Context

対応(旧)

こちらは対応(新)を知らなかったときの古い記述です。残しておきますがご参考まで。

FragmentContextWrapper は baseContextという形でcontextを保持している。なのでbaseContextがActivityかどうかをチェックするようにしてあげる必要がある。

なのでContextクラスに hiltBaseContextプロパティを拡張してみた。

val Context.unwrapppedContext: Context
    get() {
        return if (this is ViewComponentManager.FragmentContextWrapper) {
            this.baseContext
        } else {
            this
        }
    }


fun hoge(context: Context) {
    if(context.unwrapppedContext is Activity) {
        // do something  
    }  
}

Context.baseContextという風にプロパティ名をそのまま使うと実装をミスった場合に ContextWrapper.getBaseContext()が直接呼ばれてしまう場合があるので unwrapppedContext としてみたが 長くていまいちしっくりこない。

あとcontextがactivityかどうかのチェック時に常にbaseContextを意識しないといけないのが難点。