AndroidのApp Linksで複数ホストを設定する時の注意点

AndroidのApp Links対応にあたり、AndroidManifest.xmlに対応するスキーム/ホスト/パスなどを設定すると思います。１つのアプリで複数のホストに対応する場合にハマった点があったため、それについてまとめます。

### 🏁ゴール
アプリの`MainActivity`を次の2つのURLパターンから起動できるようにするのをゴールとします。それぞれのURLは異なるホストになっています。

- `https://foo.example.com/users/.*`
- `https://bar.example.jp/articles/.*`

### 🙅NGな例
こちらがNGな例です。どこがダメかわかりますでしょうか？

```xml
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:label="@string/app_name"
    android:theme="@style/Theme.MyApplication.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!-- App Links -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="https"
            android:host="foo.example.com"
            android:pathPattern="/users/.*" />
        <data
            android:scheme="http"
            android:host="bar.example.jp"
            android:pathPattern="/articles/.*" />
    </intent-filter>
</activity>
```

この場合、Androidは次の4つのURLパターンでアプリを起動しようとしてしまいます。

- `https://foo.example.com/users/.*`
- `https://foo.example.com/articles/.*`
- `https://bar.example.jp/users/.*`
- `https://bar.example.jp/articles/.*`

AndroidがAndroidManifest.xmlに定義されたものをどのように解釈するかは、Android StudioのTools にある**App Links Assistant**を使うとよくわかります。AndroidManifest.xmlが上記の状態でApp Links Assistantの**URL Mapping Manager**を起動すると次のような画面が表示されます。

![🙅NGな例 / URL Mapping Manager](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/a7eb99f4-041a-2aaa-1e1d-ff376000d432.png)

### 🙆OKな例
複数ホストを設定する場合、正しくは次のようにホストごとに`intent-filter`を分ける必要があります。

```xml
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:label="@string/app_name"
    android:theme="@style/Theme.MyApplication.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!-- App Links -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="https"
            android:host="foo.example.com"
            android:pathPattern="/users/.*" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="http"
            android:host="bar.example.jp"
            android:pathPattern="/articles/.*" />
    </intent-filter>
</activity>
```

この状態でURL Mapping Managerを起動すると次のような画面が表示されます。ちゃんと🏁ゴールの状態になっていますね！

![🙆OKな例 / URL Mapping Manager](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/804f35d9-bb16-6a6c-16c2-4806edbbed34.png)

ネット上の記事で🙅NGな例の方で書かれているものも散見されていますが、公式ドキュメントでも次のように明確に書かれています。

> 1 つのフィルタに複数の `<data>` 要素を組み込むことは可能ですが、一意の URL（`scheme` と `host` の特定の組み合わせなど）を宣言する場合は、それぞれ個別のフィルタを作成する必要があります。1 つのインテント フィルタ内に複数の `<data>` 要素があると、実際にはマージされ、あらゆる組み合わせの属性バリエーションがすべてサポート対象になります。

https://developer.android.com/training/app-links/deep-linking?hl=ja#adding-filters