Android App Linksを試してみた

 会社で資料を眺めていたら、今更ながらAndroid App Linksのことを知ったのでまとめてみます。
 みなさんも、バックエンドチームが作ったシステム構成図に知らないAndroidの機能が書いてあって震えることありますよね

Android App Links

 「外部のリンクを踏んだら特定のアプリを立ち上げたい」、なんてのはよくあることだと思います。入門書などでもActivity連携などで取り上げられてるテーマですし、そこで大体Intent(特に暗黙的Intent)の話につながっていく流れですよね。それを別の方向から実現するのが今回のAndroid App Linksです。
 Android6.0で登場した機能なので、全世界で見るとちょっとまだ導入には厳しそうですが……。
f:id:t-miliya612:20180418233137p:plain

何が出来るの

  • deeplinkよりも起動するアプリを指定できます
  • 立ち上げるアプリを事前に認証できます
  • これ全部をAndroidStudioのウィザードで片付けることができます

詳しく見ていきましょう

deeplinkよりも細かく起動するアプリを指定できます

 外部アプリを起動するとき、custom url schemeを使ってintent filterを設置するのがよくやる方法です。例えば、地図アプリを起動するときはgeo:など、Androidデフォルトのアプリが提供しているものもあります。
 Androidのユーザー側が自分で使用するアプリを決められるのがメリットですが、正直そんなに……という気持ちになります。それよりも、複数アプリが同じschemeを待ち受けている際に出てくるchooserのストレスの方が大きいような。
f:id:t-miliya612:20180418233025p:plain
 ここで「ALWAYS」を選んだばっかりに「アプリが立ち上がらない!」と問合せを受けるのは誰もが通る道だと思います。

f:id:t-miliya612:20180418233055p:plain
いらすとやにはなかった

 そこで他と被らない独自のcustom url schemeを作り始めるのですが、httpスキーマじゃないとリンクとして認識してくれないメールやSMSクライアントがあったりしてちょっとつらいのが現状です。仕方ないのでこれまではWebサーバーにリダイレクトを設置して頑張っていたのですが、どう見てもコレジャナイ感があります。

 一方、Android App Linksではhttpスキーマを使いつつ、パスまでしっかり使って起動するアプリを細かく指定できます。これで、chooserに悩まされるユーザーを減らすことができます。

立ち上げるアプリを事前に認証できます

 Android App Linksとして指定するURLは、個人で所有するドメインを使う必要があります。正確に言うと個人で契約していなくてもいいのですが、事前にドメインに紐付いているサーバーにアプリ情報を含むjsonを上げておき、認証するというプロセスが必須です。これによって、得体の知れないアプリがlinkを侵食してくる問題を予防ことができます。
 例えば、悪意あるアプリが決済系アプリと同じURLで待ち受けていた場合、ユーザーの選択によってはきれいに情報を引っこ抜かれてしまう恐れがあります。これを事前に防げるわけですね。

これ全部をAndroidStudioのウィザードで片付けることができます

 (実は一部出来ていないようなところがあるのですが…)App Linksを設定するには、AndroidStudioのウィザードに沿って作業を進めるだけでOKです。公式ページ動画にもやり方は載っているのですが、以下で簡単にまとめておきます。

どうやるの

 動画を参考に進めていきます。

 まずは適当にprojectを生成しましょう。注意点は、minSdkVersionを23以上にすることくらいです。今回はkotlinを選択しました。

f:id:t-miliya612:20180418233427p:plain
f:id:t-miliya612:20180418233446p:plain

 次に、AndroidStudioのTools->App Links Assistantを選択します。

f:id:t-miliya612:20180418234040p:plain

 すると、こんな感じのメニューが出てきます。

f:id:t-miliya612:20180418234215p:plain

 この4ステップでやっていきましょう。

①Add URL intent filters

f:id:t-miliya612:20180418234336p:plain

 Host, Path, 起動したいActivityを設定します。  Hostにはリンクに使用したいwebサイトのURLのhost部分のみを入力してください。今回はgithub.ioを使用します。理由は後述します。
 また、PATHのプルダウンメニューでは、今回とりあえずpathPrefixを選んでおきましょう。pathの末尾の値を取得するために必要です。

f:id:t-miliya612:20180418234355p:plain

 Check URL Mappingに試しにURLを入力してみましょう。うまく設定できていれば、先程指定したActivityが出てくるはずです。
 また、この時点で自動的にAndroidManifest.xmlにintent filterが設定されています。Previewを参考にしてください。

②Add logic to handle the intent

 次にIntentを受け取って処理するロジックを、選択したActivityに自動で記述してくれる……はずなのですが、

f:id:t-miliya612:20180418234409p:plain

 駄目でした。。。Javaだと通ったのですが、Kotlinはまだ未対応のようです。
 仕方ないので手で書いていきます。

MainActivity.kt

package tech.miliya.applinkssample

import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        handleIntent()
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        setIntent(intent)
        handleIntent()
    }

    private fun handleIntent() {
        val appLinkAction = intent.action
        val appLinkData = intent.data
        if (Intent.ACTION_VIEW == appLinkAction && appLinkData != null) {
            val comment = appLinkData.lastPathSegment
            tv_comment.text = comment
        }
    }

}

 ついでに、xmlも少し弄っておきます。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_comment" <!-- この行を追加 -->
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

 一旦ここで、アプリを起動できるかテストしておきましょう。④Test on device or emulatorを選択します。

f:id:t-miliya612:20180419000511p:plain

 先程入力した、{HOST}/{PATH}/適当なコメントを入力し、テストを実行します。

f:id:t-miliya612:20180418234416p:plain

 無事、準備したアプリが起動候補に出てきました。
 いよいよここからアプリをドメインと紐付けていきます。

③Associate website

f:id:t-miliya612:20180418234423p:plain

 Site Domain(リンクとしたいURLのホスト)とApplication ID(アプリのパッケージ)を入力し、Generate Digital Asset Links Fileを選択します。
 すると、Previewjsonファイルが表示されます。これを指定されたURLに配置していきましょう。

 ここだけはHTTPSが必須です。準備するのが面倒なので、ここは動画の通りにgithub.ioを使ってホストしていきます。

github.com

 Github{USERNAME}.github.ioの名前でリポジトリを作成し、指定された階層の通りに/.well-known/assetlinks.jsonを作成します。
 作業が完了したら、下の方にあるLink and Verifyをクリックしましょう。

note

 github.ioでは、そのままだと.well-known以下が見えないので、下記のファイルをリポジトリのルートに配置します。

  • .nojekyll(空ファイル)
  • _config.yml
include: [".well-known"]

④Test on device or emulator

 最終動作確認です。

f:id:t-miliya612:20180418234427p:plain

 無事設定できていれば、先程のようにchooserが出てくることなく、いきなりアプリが起動します。
 テキストが反映されていることも確認しておきましょう。お疲れ様でした。

まとめ

 AndroidStudioにあるアシスタントを使いながら、Android App Linksを使ってアプリの外部起動のフローを実装してみました。
 起動用URLの使い勝手の良さ、セキュリティ面ももちろんですが、chooserが出ないことをひたすら祈らなくてよくなるのは有り難いです。
 しかもアシスタントが優秀で素晴らしい……!
 Android6.0がもうちょっと広まれば、もっと積極的に使っていきたいです。

参考

developer.android.com developer.android.com www.youtube.com stackoverflow.com github.com