AndroidAppLinksでアプリを開く


概要

業務でAndroidAppLinks(以下AAL)を使ったのでまとめてみます。
主な内容は以下の通り
- DeepLinkとの違い
- assetlinks.json の置き場所
- 署名毎の対応
- 反応して欲しいURLの正規表現の書き方とその限界について
- 他のアプリも同じURLで反応する場合の挙動

DeepLinkとの違い

https://developer.android.com/training/app-links?hl=ja
DeepLinkで複数のアプリが反応した場合、どのアプリで開くか尋ねられる。

AALではどのアプリで開くか尋ねられることなくアプリを直接起動させることができる。

assetlinks.json の置き場所

自分の持つドメインの直下に .well-known/assetlinks.json を設置する
https://test.com が自分のドメインであれば https://test.com/.well-known/assetlinks.json に json ファイルを設置する。

弊社ではテスト環境用の stg.test.com dev.test.com と本番環境用の test.com が存在したため、それぞれのホストの直下に assetlinks.json を置く必要があった。

署名毎の対応

弊社では本番環境に加え、stg,dev環境の計三つの環境(ドメイン)が存在した。
また、それぞれAndroidのプロジェクト内でフレーバー、署名が分かれていたため
ドメイン毎に別のassetlinks.jsonを用意する必要があった。

assetlinks.json の注意点

注意点
①フレーバーがあっているか
②署名は正しいか(debugのままになっていないか)

フレーバー毎に反応するURLを変える

URLのホストが3つあるが、本番ビルドでは stg.test.com,dev.test.comに反応して欲しくない。
そこでURL Mappingで以下のように設定した。

hostName の箇所を変数にして、build.gradle で代入する方法を取った。

AndroidManifest.xml
<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="${hostName}"
        android:path="/android_app_links" />
</intent-filter>
build.gradle
productFlavors {
    dev {
        manifestPlaceholders = [hostName:"dev.test.com"]
    }
    stg {
        manifestPlaceholders = [hostName:"stg.test.com"]
    }
    pro {
        manifestPlaceholders = [hostName:"test.com"]
    }
}

こうすることで、Devのフレーバーで起動したときには dev.test.com のホストのIntentFilterにしか反応しなくなるのでAALの動作もそれに従います。

反応して欲しいURLの正規表現の書き方とその限界について

弊社の仕様の要望は以下の通りだった

反応して欲しいURL

https://test.com/10文字の英数字
https://test.com/10文字の英数字_桁数不明の数字

- 例
https://test.com/Ki9s7kPoiu
https://test.com/Ki9s7kPoiu_299281

反応してほしくないURL

https://test.com/10文字の英数字/*
https://test.com/10文字の英数字_桁数不明の数字/*

- 例
https://test.com/Ki9s7kPoiu/image
https://test.com/Ki9s7kPoiu_299281/login

普通に正規表現が使えれば Path に対し以下のような条件が付けられる

/[a-zA-z0-9]{10}$
/[a-zA-z0-9]{10}_[0-9]+$

早速この正規表現をIntentFilterのPathPatternに記入してみるが動作しない。

なんならURL MappingのPathPatternのプレースホルダーにある /[a-z]+ すら動作しない始末
いろいろ調べたが、そもそもこのPathPatternは正式な正規表現ではない。
そのため限られた条件しか付けられないことがわかった。

IntentFilterで使えるPathPatternの文字は .* だけだった。
. は任意の一文字、* は直前の文字が0個以上続くという意味。
つまり私がやりたかった /10文字の英数字 という条件は書けなかったので、妥協して10文字であるという条件でのみ絞ることにした。
しかし繰り返しを表す {10} もつかえないのでPathPatternは以下のようになった。

/..........
(.が10個)

また、/10文字の英数字_桁数不明の数字

/.........._.*

となった。
もし . * これ以外に使える文字があれば教えて欲しい。

他のアプリも同じURLで反応する場合の挙動

弊社にはアプリが2つあり(以下AとBとする)、それぞれのアプリが同じURLに対してAALで反応する。
もしユーザーの端末にAとBどちらのアプリもインストールされている場合はAを優先して起動したいという要望があった。

一瞬話が逸れるが、iOSではAALと同じような仕組みであるUniversal Links(以下UL)というのがある。
https://qiita.com/mono0926/items/2bf651246714f20df626
これはサーバー側に置くjsonファイル apple-app-site-association にどのようなURLにアプリが反応するかを記述する。
その際に、先に反応して欲しいアプリを apple-app-site-association の先頭に記述することで優先度付けを行うことができた(公式なやり方ではないが)

iOSでできるのであればAndroidでもできるだろうと思い調べるが、優先度についてどこにも書かれていない。
また、 assetlinks.json の記述順を変えても優先度をつけることはできなかった。
あとIntentFilterのorderも試したが、当然意味がなかった。
結果としてはA、Bどちらのアプリを優先的に起動するかを設定することはできなかった。

ちなみにA,Bどちらのアプリもインストールした状態でAALが動作すると毎回ランダムに起動するわけではなく、後にインストールした方のアプリが優先的に起動する。

最後に

いろいろ妥協する場面があったが、反応したいURLの正規表現なんかはURLをAALありきで設計すれば回避できるし、ほとんどの使い方で問題になることはなさそう。
署名やフレーバーが細かく分かれていると、どこに何を置くのかが分からなくなるくらいで、それに気をつければつまずくこともないかと思います。
同じURLで起動するアプリの優先度を設定できない問題は回避する策が今のところ私は知らないので、もし知っている人がいたらコメントください。