#注意
こちらはnavigationのpreview版時点での話です。リリース版では挙動が異なる可能性があるので信じないでください。
#はじめに
GoogleIO2018で発表されたNavigation
FragmentTransactionなどの煩わしい処理を無くし、GUIで直感的に画面遷移を実現できるAAC(Android Architecture Components)の新しいパーツです。
画面遷移はもちろんのこと、BottomNavigationやNavigationViewなども簡略化のサポートを行っている優れもの。
今回はその中でも、DeepLinkの挙動が気になったので軽く調べてみました。
#DeepLinkのライフサイクル
DeepLinkを実装する際に、よく考えるのは、着地した画面からback keyを押したときの挙動を考えることが多いと思います。
詳細画面に着地したあと、back key押下でアプリホーム画面に戻らなきゃいけないとかいう、あれです。
これを実現する為に、わざわざホーム画面でschemeを受け取り、URLパターンに応じて詳細画面に遷移する・・・というような実装をする必要があり(あくまで一例です。1Activity×N個Fragmentとかだと今回考える必要ない)、面倒です。
こんな煩わしさを解消してくれるツールが今回のNavigationで、直接遷移したいFragment(Activity)に 対象のURLを登録するだけで、もちろんその画面に遷移することはもちろん、それまでのバックスタックもすべて積み上がった状態で遷移を実現してくれます。
画面スタックを自作する実装が不要となるのでとても便利です。
しかし、そこで感じたのは、「表示されないスタックされた画面って、ライフサイクルどうなるの??」
実際の利用シーンで、それぞれの画面のonResumeなどにGAのScreenTrackを行っている方はたくさんいらっしゃると思います。
もし仮にすべてのスタックされた画面のライフサイクルが呼ばれてしまうと、**詳細画面に遷移しただけなのに、なぜかホーム画面のScreen数も計測されてる?**みたいなことが起きてしまいます。
今回はそんな事故が起きないように実際に以下のようなサンプルを作って検証してみました。
#検証
まずはサンプルを作ります。Navigationは理解するのが割とかんたんだったので、詳しい内容知りたい方は先にNavigation Codelabを1度やってみるのをおすすめします。
今回作ったサンプルのnavigationはこんな感じです。
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/fragment_main">
<fragment
android:id="@+id/fragment_main"
android:name="com.pag.n_satou.navigationsample.MainFragment"
tools:layout="@layout/fragment_main"
android:label="MainFragment" >
<action
android:id="@+id/action_fragment_main_to_secondFragment"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.pag.n_satou.navigationsample.SecondFragment"
android:label="SecondFragment"
tools:layout="@layout/fragment_second">
</fragment>
</navigation>
また、fragment_mainのボタンをタップしたらsecondFragmentへ遷移するようにnavigationを設定しておきます。
view.findViewById<Button>(R.id.button).setOnClickListener {
findNavController(it).navigate(R.id.action_fragment_main_to_secondFragment)
}
これで、SecondFragmentはMainFragmentをスタックに持ったNavigationになりました。
また、DeepLinkをつける為にSecondFragmentにdeeplink tagをつけます
<fragment
android:id="@+id/secondFragment"
android:name="com.pag.n_satou.navigationsample.SecondFragment"
android:label="SecondFragment"
tools:layout="@layout/fragment_second">
<deepLink app:uri="www.navigationsample.com/foo"/> // ココ
</fragment>
AndroidManifestも編集します。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/main_navigation"/> // ココ
</activity>
これでSecondFragmentに直接deeplink遷移するnavigationのできあがりです。
#ライフサイクルを見てみる
まずは起動→ボタンタップ→画面遷移の順でライフサイクルを確認します
通常時
// アプリ起動
D/com.pag.n_satou.navigationsample.MainActivity #onCreate
D/com.pag.n_satou.navigationsample.MainFragment #onCreateView
D/com.pag.n_satou.navigationsample.MainFragment #onViewCreated
D/com.pag.n_satou.navigationsample.MainFragment #onStart
D/com.pag.n_satou.navigationsample.MainActivity #onResume
D/com.pag.n_satou.navigationsample.MainFragment #onResume
// ボタンタップ→画面遷移
D/com.pag.n_satou.navigationsample.SecondFragment #onCreateView
D/com.pag.n_satou.navigationsample.SecondFragment #onViewCreated
D/com.pag.n_satou.navigationsample.SecondFragment #onStart
D/com.pag.n_satou.navigationsample.SecondFragment #onResume
いつもどおりのライフサイクルです。では、deeplink時のライフサイクルを見てみます。
DeepLink時
// DeepLinkで起動
D/com.pag.n_satou.navigationsample.MainActivity #onCreate
D/com.pag.n_satou.navigationsample.MainActivity #onCreate
D/com.pag.n_satou.navigationsample.SecondFragment #onCreateView
D/com.pag.n_satou.navigationsample.SecondFragment #onViewCreated
D/com.pag.n_satou.navigationsample.SecondFragment #onStart
D/com.pag.n_satou.navigationsample.MainActivity #onResume
D/com.pag.n_satou.navigationsample.SecondFragment #onResume
// バックキーでMainFragmentへもどる
D/com.pag.n_satou.navigationsample.MainFragment #onCreateView
D/com.pag.n_satou.navigationsample.MainFragment #onViewCreated
D/com.pag.n_satou.navigationsample.MainFragment #onStart
D/com.pag.n_satou.navigationsample.MainFragment #onResume
親のActivity/対象のFragmentだけが初期化され、初めてスタックされた画面に遷移したときに画面が初期化されるようです。ヨカッター。
#まとめ
対象の画面にたどり着いたときだけライフサイクル・初期化が行われます。
まだ正式版ではないのでリリース版で挙動が変更されるかもしれませんが、Android開発においてライフサイクルは心臓部ぐらいの重要度だと思うので、これからも気をつけてみていきたいと思います。