yotiky Tech Blog

とあるエンジニアの備忘録

[Android]Deeplink(Custom URL Scheme)を使ってアプリからアプリを起動する

アプリから別アプリを起動するケースがあまり多くないのかまとまった情報はそれほど多くありません。
作業の覚書としてここに書き記しておきます。

目次

アプリからアプリを起動する

Deeplink(Custom URL Scheme)を使ってアプリAからアプリBを起動するには、

  1. アプリB(起動される側)にカスタムURLスキームを定義する
  2. アプリA(呼び出し側)に呼び出し処理を実装する

が必要となります。

起動される側(アプリB)

まずURLスキームを定義します。 Plugins\Android\AndroidManifest.xml を開いて、<action android:name="android.intent.action.MAIN" />が定義されている <activity>の直下に<intent-filter>を追記します。

    <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:screenOrientation="fullSensor" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
      </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="myScheme" android:host="myHost" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>

これで、myScheme://myHostで起動する準備ができました。 1

呼び出し側(アプリA)

ネイティブ実装の場合

まず、ネイティブでの呼び出し方法は次の通りです。

Uri uri = Uri.parse("myScheme://myHost");
Intent i = new Intent(Intent.ACTION_VIEW, uri);
// アプリが見つからなければ、ActivityNotFoundException
startActivity(i);

以上。

Unityで実装の場合

Unityで実装するには、AndroidJavaClassとAndroidJavaObjectを使ってネイティブと同じ処理を呼び出します。
AndroidJavaClassなどはマーシャリングとかリフレクションみたいな感じのやつです。

        var url = "myScheme://myHost";
        var packageName = "myApplication";

        using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
        using (AndroidJavaClass intentStaticClass = new AndroidJavaClass("android.content.Intent"))
        using (AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri"))
        using (AndroidJavaObject uriObject = uriClass.CallStatic<AndroidJavaObject>("parse", url))
        {
            var actionView = intentStaticClass.GetStatic<string>("ACTION_VIEW");

            using (AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", actionView, uriObject))
            {
                try
                {
                    currentActivity.Call("startActivity", intent);
                }
                catch (Exception e)
                {
                    // 未インストールならストアを開くなど
                    Application.OpenURL("https://play.google.com/store/apps/details?id=" + packageName);
                    
                    // ストアアプなら
                    var urlStore = "market://details?id=" + packageName;
                    using (AndroidJavaClass uriClassStore = new AndroidJavaClass("android.net.Uri"))
                    using (AndroidJavaObject uriObjectStore = uriClassStore.CallStatic<AndroidJavaObject>("parse", urlStore))
                    using (AndroidJavaObject intentStore = new AndroidJavaObject("android.content.Intent", actionView, uriObjectStore))
                    {
                        currentActivity.Call("startActivity", intentStore);
                    }
                }
            }
        }

アプリがインストール済みかどうかは、startActivityやPackageManagerを使ってエラーハンドリングするのが一般的?なようです。

パラメータを渡す場合

Deeplinkで起動する際にパラメータを渡したい場合は、上記に更に対応が必要になります。

  1. 起動時のActivityをフックして値をUnity側へ橋渡しするプラグインを作成する
  2. 起動されるアプリケーションにカスタムURLスキームを定義し、プラグインを呼び出す実装をする

プラグイン

【Unity-Android 】 (1) Android StudioでUnity向けにmoduleを作る方法 - Cross Roadを参考にさせて頂きました。これで目的のプラグインのモジュール(jar)が取り出せます。
具体的にパラメータを橋渡しするコードは、[Unity][Android] カスタムURLスキームを使ってアプリを起動する(Unity Android編) : うえすと開発メモUnityでカスタムURLスキームを使用してアプリを起動する【iOS】【Android】 - LotosLaboなどが参考になります。 ポイントは、

  • Activityを継承した独自Activityを実装する
  • オーバーライドしたonCreateメソッド内で、Intentからパラメータを取得
  • 取り出したパラメータをUnityから呼べるようにする

となります。

呼び出す時は

Intent i = new Intent(Intent.ACTION_VIEW, uri);
i.putExtra("KEY", "VALUE");

で設定し、取り出す時は

Intent intent = getIntent();
String key = intent.getStringExtra("KEY");

// Unityからパラメータ取得するために取っておく
IntentParameter.Key = key;

で取得します。
Unityから呼び出せるように口を用意しておきましょう。

public class IntentParameter {
    public static String Key;

    public static String getKey(){
        return Key;
    }
}

Intent の putExtra は Bundleに値を保存しActivityに直接紐付けているようなので、パラメータ付きのURLを直にやり取りしたい場合は、呼び出し側のURLスキームに直接書き足すのが楽そうです。

その場合呼び出す時は

Uri uri = Uri.parse("myScheme://myHost?param=mogmog");
Intent i = new Intent(Intent.ACTION_VIEW, uri);

で、取り出す時は

Intent i = getIntent();
String urlString = i.getDataString();

myScheme://myHost?param=mogmogが出力されます。

起動される側

AndroidManifest.xmlの修正

次にURLスキームを定義します。パラメータがない場合とは定義の仕方が変わるので注意が必要です。
プラグインで実装したActivityを利用するので、 <action android:name="android.intent.action.MAIN" />が定義されている<activity>と並列に別の<activity>を定義します。

    <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:screenOrientation="fullSensor" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>
    <activity android:name="com.example.myPlugin.MyActivity">
      <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="myScheme" android:host="myHost" />
      </intent-filter>
    </activity>
  </application>

これで、myScheme://myHostで起動した場合に、プラグインで実装したActivityを使用するようになります。

プラグインの取り込みとパラメータの取得

UnityでカスタムURLスキームを使用してアプリを起動する【iOS】【Android】 - LotosLaboを見ながら、Unityにプラグインを取り込みます。
Plugins\Android に追加したclasses.jar は適当な名前にリネームするとわかりやすくなります。 f:id:yotiky:20180521015221p:plain

あとはプラグインに用意しておいたUnity用の口を使って値を取り出します。

        using (AndroidJavaClass plugin = new AndroidJavaClass("com.example.myPlugin.IntentParameter"))
        {
            var key = plugin.CallStatic<string>("getKey");
        }

パラメータを付けてカスタムURLスキームで起動した場合、プラグインの独自Activityにより値が取得できます。既に起動していてる場合でも、独自Activityのコードを通るようです。
パラメータが設定されてない場合や直接アプリを起動した場合は値が取れません。


  1. Unityでプロジェクト新規作成時などデフォルトでは、AndroidManifest.xml は含まれていません。追加する場合は、PlatformをAndroidにしてビルドすると、Xxx.Unity\Temp\StagingArea にUnityが現在の構成で良しなに作成したAndroidManifest.xmlが生成されているのでこれを雛形にすると良いかもです。