NavigationのDeepLinkのライフサイクルを調べる

#**注意**

>こちらはnavigationの**preview版**時点での話です。リリース版では挙動が異なる可能性があるので信じないでください。

#はじめに

GoogleIO2018で発表された[**Navigation**](https://developer.android.com/topic/libraries/architecture/navigation/)

FragmentTransactionなどの煩わしい処理を無くし、GUIで直感的に画面遷移を実現できる[AAC](https://developer.android.com/topic/libraries/architecture/)(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](https://codelabs.developers.google.com/codelabs/android-navigation/#0)を１度やってみるのをおすすめします。

今回作ったサンプルのnavigationはこんな感じです。

```xml:main_navigation.xml
<?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>
```
<img width="1440" alt="スクリーンショット 2018-06-03 12.28.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/63740/b3ebbb78-e671-33ff-3b5c-d99db71c16ec.png">

また、fragment_mainのボタンをタップしたらsecondFragmentへ遷移するようにnavigationを設定しておきます。

```kotlin:MainFragment.kt
view.findViewById<Button>(R.id.button).setOnClickListener {
    findNavController(it).navigate(R.id.action_fragment_main_to_secondFragment)
}
```

これで、**SecondFragmentはMainFragmentをスタックに持ったNavigation**になりました。

また、DeepLinkをつける為にSecondFragmentにdeeplink tagをつけます

```xml:main_navigation.xml
<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も編集します。

```xml:AndroidManifest.xml
<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開発においてライフサイクルは心臓部ぐらいの重要度だと思うので、これからも気をつけてみていきたいと思います。