手順4: Amazon Device Messaging(ADM)を統合する


手順4: Amazon Device Messaging(ADM)を統合する

Lambdaはディレクティブを処理した後、Amazon Device Messaging(ADM)と呼ばれるプッシュ通知技術を使用してアプリにメッセージを送信します。Lambdaディレクティブを処理するには、アプリでADMを統合する必要があります。

ビデオスキルでADMを使用する場合、最も一般的なADMのユースケースとは使用方法が異なることに注意してください。ビデオスキルでは、ADMはLambdaからアプリにメッセージを(一方向で)送信するためだけに使用されます。このため、このトピックではADMのドキュメントの関連セクションのみを記載しています。Fire TVのビデオスキルに関連する以外の用途や詳細を確認するには、ADMのドキュメントを直接参照してください。

Fire TV対応サンプルアプリに関する注意点

Fire TV対応サンプルアプリを使用する場合は、以下の手順を省略できます。ただし、ADMを統合するためのコードについて理解を深める場合は、サンプルアプリ: ADMの統合セクションを参照してください。サンプルアプリでどのファイルを調べればよいかについて、詳細を確認できます。

手順A: アプリのマニフェストを更新する

ADM経由で送信されたメッセージを受信するには、まずAndroidManifest.xmlファイルの更新が必要です。既存のファイルを次のように変更します。

  1. AndroidManifest.xmlファイルを開き、以下に記載されたAmazonの名前空間を追加します。

    xmlns:amazon="http://schemas.amazon.com/apk/res/android"
    
  2. ADMのサポートに必要なパーミッションを宣言するには、permission要素とuses-permission要素をmanifest要素の後に追加します。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:amazon="http://schemas.amazon.com/apk/res/android"
       package="[パッケージ名]"
       android:versionCode="1"
       android:versionName="1.0">
    <!-- このパーミッションにより、ADMメッセージがほかのアプリにインターセプトされる
    危険性が回避されます。 -->
    <permission
         android:name="[パッケージ名].permission.RECEIVE_ADM_MESSAGE"
         android:protectionLevel="signature" />
    <uses-permission android:name="[パッケージ名].permission.RECEIVE_ADM_MESSAGE" />
    
    <!-- このパーミッションにより、ADMからのプッシュ通知をアプリが受信できるように
    なります。 -->
    <uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />
    
    <!-- メッセージ受信時にプロセッサがスリープ状態になるのを防ぐため、ADMはWAKE_LOCKを使用します。 -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    ...
    </manifest>
    
  3. 明示的にADMを有効化し、アプリにADMが必要(android:required="true")か、ADMがなくても動作する(android:required="false")か宣言します。ここでandroid:required="false"を指定した場合、ADMが利用できないときにアプリの機能が適切に制限されるようにする必要があります。マニフェストのapplicationノードで、amazon:enable-feature要素を追加します。

    ...
    <application
          android:icon="@drawable/ic_launcher"
          android:label="@string/app_name"
          android:theme="@style/AppTheme">
    
          <!-- 明示的にADMを有効化し、アプリにADMが必要
             (android:required="true")か、ADMがなくても動作する(android:required="false")か宣言します。
             ここでandroid:required="false"を指定した場合、ADMが利用できないときにアプリの機能が
             適切に制限されるようにする必要があります。 -->
             <amazon:enable-feature
    	  android:name="com.amazon.device.messaging"
                 android:required="true"/>
    ...
    
  4. ADMから送信されるREGISTRATIONインテントとRECEIVEインテントを処理するために、ブロードキャストレシーバーを宣言します。レシーバーはプログラムではなく、AndroidManifest.xmlファイルで定義する必要があります。amazon:enable-featureの直後に、以下の要素を追加します。

     <!-- serviceタグとreceiverタグ内のname値は、パッケージで使用する
         適切な名前に置き換えてください。 -->
    
     <service
         android:name="[サービス名]"
         android:exported="false" />
    
     <receiver
         android:name="[レシーバー名]"
    
         <!-- このパーミッションにより、ADMのみがアプリ登録ブロードキャストを送信できます-->
         android:permission="com.amazon.device.messaging.permission.SEND" >
    
         <!-- アプリがADMとやり取りを行うには、以下のインテントをリッスンする必要があります。 -->
         <intent-filter>
     <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
     <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
    
     <!-- categoryタグ内のname値をアプリのパッケージ名に置き換えます。 -->
     <category android:name="[パッケージ名]" />
         </intent-filter>
     </receiver>
    
  5. AndroidManifest.xmlファイルの更新後、ADMManifest.checkManifestAuthoredProperly()を呼び出して、変更が適切かどうかを確認します。

    java.lang.NoClassDefFoundError: com.amazon.device.messaging.ADMエラーが発生した場合は、マニフェストを調べて、手順3で指定された場所にamazon:enable-feature要素が追加されていることを確認します。

以下のコードは、ADMに対応したAndroidManifest.xmlファイルの一例です。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:amazon="http://schemas.amazon.com/apk/res/android"
   package="[パッケージ名]"
   android:versionCode="1"
   android:versionName="1.0" >

   <uses-sdk
      android:minSdkVersion="15"
      android:targetSdkVersion="15" />

   <permission
      android:name="[パッケージ名].permission.RECEIVE_ADM_MESSAGE"
      android:protectionLevel="signature" />
   <uses-permission android:name="[パッケージ名].permission.RECEIVE_ADM_MESSAGE" />

   <uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />

   <uses-permission android:name="android.permission.WAKE_LOCK" />

   <application
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
      android:theme="@style/AppTheme" >

      <amazon:enable-feature
         android:name="com.amazon.device.messaging"
         android:required="true"/>

      <service
         android:name="[サービス名]"
         android:exported="false" />

      <receiver
         android:name="[レシーバー名]"
         android:permission="com.amazon.device.messaging.permission.SEND" >

         <intent-filter>
         <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
         <action android:name="com.amazon.device.messaging.intent.RECEIVE" />

         <category android:name="[パッケージ名]" />
         </intent-filter>

      </receiver>
   </application>
</manifest>

手順B: 登録とメッセージの処理を実装する

マニフェストで宣言されたブロードキャストレシーバーは、インテントをリッスンし、インテント受信時にアプリを起動します。アプリは、com.amazon.device.messaging.ADMMessageHandlerBaseクラスで定義された以下のコールバックメソッドを介して、ブロードキャストレシーバーとのやり取りを行います。

  • onRegistered。アプリインスタンスの登録IDが利用可能になると呼び出されます。アプリ側は、この登録IDを開発者サーバーに送信する必要があり、そうすることで、開発者サーバーがアプリインスタンスにメッセージを送信できるようになります

  • onUnregistered。アプリインスタンスがADMから登録解除された場合に呼び出されます。

  • onRegistrationError。アプリのADM登録リクエストが何らかの理由(デバイスにサインインしているAmazonユーザーがいないなど)で失敗した場合に呼び出されます。

  • onMessage。ADMクライアントがアプリインスタンスにメッセージを配信する際に呼び出されます。

com.amazon.device.messaging.ADMMessageHandlerBasecom.amazon.device.messaging.ADMMessageReceiverのサブクラスを実装する場合は、アプリ側でこれらのコールバックをオーバーライドする必要があります。以下のコードサンプルを参照してください。

public class MyADMMessageHandler extends ADMMessageHandlerBase
{
    public static class Receiver extends ADMMessageReceiver
    {
        public Receiver()
        {
            super(MyADMMessageHandler.class);
        }

    // ここではほかに何も必要ありません。ブロードキャストレシーバーは、処理用に
    // インテントを自動的にサービスに送信します。
    }

    @Override
    protected void onRegistered(final String newRegistrationId)
        {
        // メインアクティビティでstartRegister()を呼び出して登録プロセスを
        // 開始します。登録IDが利用可能になると、ADMはアプリのonRegistered()を
        // 呼び出します。開発者サーバーが現在のアプリインスタンスにメッセージを送信できるように、
        // 渡された登録IDを開発者サーバーに送信します。onRegistered()は、何らかの理由で
        // 登録IDのローテーションまたは変更が行われた場合にも呼び出されます。
        // その場合、アプリ側は新しい登録IDを開発者サーバーに渡す必要があります。
        // 開発者サーバーは、最大で1,536文字までの登録IDを処理できる
        // 必要があります。

        // ヘッダーのキーと値のペアを使用してHTTPで登録IDを
        // 開発者サーバーに送信する例は次のとおりです。
        URL url = new URL(YOUR_WEBSERVER_URL);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setDoInput(true);
        con.setUseCaches(false);
        con.setRequestMethod("POST");
        con.setRequestProperty("RegistrationId", newRegistrationId);
        con.getResponse();
        }

    @Override
    protected void onUnregistered(final String registrationId)
    {
        // このデバイスでアプリが登録解除されている場合は、現在のアプリインスタンスが
        // メッセージの有効なターゲットではなくなっていることを、開発者サーバーに通知します。
    }

    @Override
    protected void onRegistrationError(final String errorId)
    {
        // 登録エラーは致命的であるとみなす必要があります。その対応として、
        // アプリの機能を適切に制限するか、アプリの機能のうちこの部分が使用できないことを
        // ユーザーに通知することができます。
    }

    @Override
    protected void onMessage(final Intent intent)
    {
        // com.amazon.device.messaging.intent.RECEIVEインテントに
        // アタッチされているエクストラからメッセージコンテンツを抽出します。

        // JSONデータのメッセージフィールドとタイムスタンプフィールドにアクセスするための文字列を作成します。
        final String msgKey = getString(R.string.json_data_msg_key);
        final String timeKey = getString(R.string.json_data_time_key);

        // onMessage()コールバックでトリガーされるインテントアクションを取得します。
        final String intentAction = getString(R.string.intent_msg_action);

        // インテントに含まれていたエクストラを取得します。
        final Bundle extras = intent.getExtras();

        // インテント内のエクストラからメッセージと時刻を抽出します。
        // メッセージの配信や順序は保証されません。
        // ネットワーク状況の変化により、メッセージが複数回配信される場合もあります。
        // アプリ側でメッセージの重複インスタンスを処理できるようにしておく必要があります。
        final String msg = extras.getString(msgKey);
        final String time = extras.getString(timeKey);
    }
}

手順C: ADMを使用できない場合に機能を適切に制限する

マニフェストファイルでは、ADMを使用できない場合にアプリが動作する(android:required="false")か、動作しない(android:required="true")か宣言します。ここでandroid:required="false"を指定した場合、ADMが利用できないときにアプリの機能が適切に制限されるようにする必要があります。

ADMがなくても動作するアプリを設計すれば、単一のAPKを構築するだけで、ADMのありなしにかかわらずデバイスにインストールして実行することができます。

適切な機能制限が行われるようにアプリを修正するには

  1. 次のようなコードを使用して、ADMを利用できるかどうか確認します。

    ADMAvailable = false ;
    try
    {
        Class.forName( "com.amazon.device.messaging.ADM" );
        ADMAvailable = true ;
    }
    catch (ClassNotFoundException e)
    {
        // 例外を処理します。
    }
    
  2. ADMライブラリランタイムを必要とするコードに、次のコードを追加します。

    if (ADMAvailable)
    {
        // ADMを必要とするコードをここに挿入します。
    }
    
  3. AndroidManifest.xmlファイルのapplication要素で、次のように指定されていることを検証します。

    amazon:enable-feature android:name="com.amazon.device.messaging" android:required="false" /
    

サンプルアプリ: ADMの統合

サンプルアプリでは、既にADMが統合されているため、ここに記載されている統合の手順は省略できます。ただし、パッケージ名はカスタマイズする必要があるため、以下の手順に従ってパッケージ名をカスタマイズしていることを確認してください。パッケージ名は、ADMがアプリを特定して通信できるようにするために、APIキーで使用されます。

サンプルアプリでパッケージ名を変更するには、次の手順を実行します。

AndroidアプリでADMの統合について調べて理解を深めるには、以下を実行します。

  • Cmd+Shift+Fキーを押して、xmlns:amazon="http://schemas.amazon.com/apk/res/androidを検索します。これがAndroidManifest.xmlファイルでどのように統合されているかを確認します。
  • 同じAndroidManifest.xmlファイルで、アプリのADMパーミッションを定義する次のコードを探します。

    <permission
         android:name="com.fireappbuilder.android.calypsovsk.RECEIVE_ADM_MESSAGE"
         android:protectionLevel="signature" />
     ...
    

    AndroidManifest.xmlファイルには、ADMの統合に関するコードがほかにも含まれています。ファイルをスクロールして、ADMの統合を示しているコメントを探してみてください。

  • このサンプルアプリのパッケージ名はcom.fireappbuilder.android.calypsovskであることを覚えておいてください(Fire App Builderで自分でアプリを作成する場合は、このパッケージ名をカスタマイズする必要があります)。
  • Shiftキーを2回押して、TenFootADMessageHandlerを検索します。このファイルでは、ADMの統合に関する詳細なコードを確認できます。

このドキュメントでは、ADMのコードについての説明は省略しています。こうした説明については、ADMのドキュメントを参照してください。バンドルされたサンプルアプリは、あくまでもこのコードがAndroidプロジェクトでどのように実装されるかについてのコンテキストを提供するためのものです。

ADMに代わるもの

ADMを使用してLambda関数からアプリに命令を送信する代わりに、独自のプロトコルとクラウドサービスを使用してアプリにアクションを送信することもできます。このオプションを使用する場合、アプリへのコマンドの配信は開発者が担当します。通信のプロトコルと特性はすべて開発者が決定でき、Lambdaコードのほか、インフラストラクチャへのサービスの呼び出しによっても管理できます。

この方法では、このアプリインスタンス固有の識別子を、初期化時にAlexa Client Libraryに提供する必要があります。この識別子は、ディレクティブがLambdaコードに送信される際、このアプリインスタンスをターゲットにするために使用されます(アプリインスタンスの識別子がまだない場合や、Alexaに識別子を渡したくない場合は、ANDROID_IDを使用することもできます)。

この方法を選択した場合は、実装コードの大半を自分で構築する必要があります。これには、次の手順が含まれます。

  • プロジェクトにAlexa Client Libraryを追加する
  • 初期化時にアプリインスタンスIDをAlexa Client Libraryに渡す
  • アプリインスタンスIDがクラウドで認識されていることを確認する
  • 必要に応じてLambda関数とアプリの通信メカニズムを実装する
  • Alexa Client Libraryのアプリ統合の要件を満たす

次のステップ

次の 手順5: アプリに署名してセキュリティプロファイルを構成するに進みます。