AndroidのNavigationのnavArgsが便利そう。

Android + kotlin

最近、仕事で使っている Navigation Architecture Componentのバージョンを 1.0.0-alpha07から一気に2.0.0まで上げました。 そのときに、リリースノートを見ていたときに by navArgs() というものを知ったのでメモ

developer.android.com

これによって、例えば以下のように書いていたものが

val args = HogeFragmentArgs.fromBundle(arguments!!)

のようにfromBundleを噛ませて書いていたのが、Fragmentのクラスのメンバ変数として

private val args: HogeFragment by navArgs()

のようにかけることを知りました。

これを使うためには1.0.0-alpha10以上の-ktxのnavigation architecture componentが必要になります。

これによってメンバ変数として定義し、初期化を使用時に遅らせることができます。

従来でもメンバ変数として定義し、初期化を遅らせることはlateinitを使えばできたのですが、それがより簡単にできるようになりました。

蛇足

さてさて、navArgs()とはなんじゃろなとなったので、Android Studioで実装に飛ぶと

  • FragmentNavArgsLazy.kt
@MainThread
inline fun <reified Args : NavArgs> Fragment.navArgs() = NavArgsLazy(Args::class) {
    arguments ?: throw IllegalStateException("Fragment $this has null arguments")
}

のような実装がされています。ここはkotlinの拡張関数を使って、NavArgsLazyを呼び出しているので、更に実装を見てみると

  • NavArgsLazy.kt
class NavArgsLazy<Args : NavArgs>(
    private val navArgsClass: KClass<Args>,
    private val argumentProducer: () -> Bundle
) : Lazy<Args> {
    private var cached: Args? = null

    override val value: Args
        get() {
            var args = cached
            if (args == null) {
                val arguments = argumentProducer()
                val method: Method = methodMap[navArgsClass]
                    ?: navArgsClass.java.getMethod("fromBundle", *methodSignature).also { method ->
                        // Save a reference to the method
                        methodMap[navArgsClass] = method
                    }

                @Suppress("UNCHECKED_CAST")
                args = method.invoke(null, arguments) as Args
                cached = args
            }
            return args
        }

    override fun isInitialized() = cached != null
}

のような記述が見えることになります。

このクラスが評価されるとget()が呼び出され、キャッシュを持っている場合はキャッシュを返し、 初回呼び出し時には、argumentProducerを使ってFragmentのargumentsが渡されているので、argumentsを型変数Argsに変換して返されることになるようです。

#

  • リリースノート定期的に見よう。
  • これ調べてたらbyが何やってるのか気になったので動作を調べてみたい
  • バージョンを上げたらargumentsがNullableを返すようになっていてfromBundleがNonNullを受け取るので変更が面倒だった。。
  • 上のソースコードのリンクのURLを貼りたいがどこかよくわかっておらず。。(汗)