Help us understand the problem. What is going on with this article?

Android から Amazon SNS を使ってみる

More than 1 year has passed since last update.

0.はじめに

以前、こちらの記事を投稿したんですが、

書いた以上はやらないとな…、
と思い、

Android 全く使ったことないですが…、
頑張って Android から Amazon SNS を使ってみました。

1.Android Studio で、新規プロジェクトを作成する

  1. Android Studio を起動し、「Start a new Android Studio project」リンクをクリックします。
    • 1001.png

  2. 「Create Android Project」画面が表示されるので、以下の項目を入力し「Next」ボタンを押下します。
    • Application name : ※任意
    • Company domain : ※任意

    • 1002.png

  3. 「Target Android Devices」画面が表示されるので、とりあえずデフォルトの値のままで「Next」ボタンを押下します。
    • 1003.png

  4. 「Add an Activity to Mobile」画面が表示されるので、「Empty Activity」を選択し「Next」ボタンを押下します。
    • 1004.png

  5. 「Configure Activity」画面が表示されるので、とりあえずデフォルトの値のままで「Next」ボタンを押下します。
    • 1005.png

  6. そのまま待っていると、プロジェクトが作成されます。
    • 1006.png

2.Android Studio で、作成したプロジェクトを実機で動かす

  1. 実機を接続して、以下のサイトを参考に開発モードの設定をします。
  2. Android Studio からプロジェクトを開き、「Run」アイコンをクリックします。
    • 2001.png

  3. 「Select Deployment Target」ダイアログが表示されるので、接続した実機が表示されていることを確認し、選択後、「OK」ボタンを押下します。
    • 2002.png

  4. 実機にアプリがインストール後、実行され、以下の「Hello World!」の画面が表示されます。
    • 2003.jpg

3.Firebase プロジェクトを作成する

  1. 以下のサイトから、FireBase のコンソールを開き、「プロジェクトを追加」ボタンを押下します。
    • 3001.png

  2. 「プロジェクトを追加」ダイアログが表示されるので、以下の項目を設定し、「プロジェクトを作成」ボタンを押下します。
    • プロジェクト名 : ※任意
    • 国 / 地域 : ※任意

    • 3002.png

  3. 以下の画面が表示され、Firebase プロジェクトが作成されます。
    • 3003.png

4.Firebase プロジェクトにアプリを登録する。

  1. 作成した Firebase プロジェクトのオーバービューから、「Android アプリに Firebase を追加」ボタンを押下します。
    • 4001.png

  2. 「Android アプリに Firebase を追加」ダイアログが表示されるので、以下の項目を設定し、「アプリの登録」ボタンを押下します。
    • Android パッケージ名 : ※作成した Android アプリのパッケージ名を入力
      • ※ MainActivity.java に記載。

    • 4002.png

  3. 「Android アプリに Firebase を追加」ダイアログが更新され、「Android Studio の使用手順」が表示されるので、手順に従って google-services.json をダウンロードし、所定の場所に移動後、「続行」ボタンを押下します。
    • 4003.png

  4. 「Android アプリに Firebase を追加」ダイアログが更新され、「Gradle の使用手順」が表示されるので、手順に従って2つの build.gradle を変更し、「Sync now」リンククリック後、「終了」ボタンを押下します。
    • 4004.png

  5. 「Android アプリに Firebase を追加」ダイアログが閉じ、Firebase プロジェクトの「概要」画面が表示されるので、アプリが登録されたことを確認します。
    • 4005.png

5.Firebase プロジェクトに登録したアプリのサーバーキーを確認する

  1. Firebase プロジェクトの「概要」画面に表示されている登録したアプリエリアの右上のメニューを開き、「設定」を選択します。
    • 5001.png

  2. Firebase プロジェクトの「設定」画面が表示されるので、「クラウドメッセージング」タブを選択します。
    • 5002.png

  3. Firebase プロジェクトの「設定」画面に「クラウドメッセージング」の情報が表示されるので、「プロジェクト認証情報」エリアの「サーバーキー」を確認します。
    • 5003.png

6.Amazon SNS にプラットフォームアプリケーションを作成する

  1. 以下のサイトから、Amazon SNS のコンソールを開きます。
  2. Amazon SNS のコンソールの左側ペインから、「アプリケーション」を選択します。
    • 6001.png

  3. Amazon SNS の「アプリケーション」画面が表示されるので、「プラットフォームアプリケーションの作成」ボタンを押下します。
    • 6002.png

  4. 「プラットフォームアプリケーションの作成」ダイアログが表示されるので、以下の項目を設定し、「プラットフォームアプリケーションの作成」ボタンを押下します。
    • アプリケーション名 : ※任意
    • プッシュ通知プラットフォーム : 「Google Cloud Messaging (GCM)」
    • API キー : ※確認しておいた Firebase プロジェクトのクラウドメッセージングの「サーバーキー」
      • ※「以前のサーバーキー」ではないので、注意!!

    • 6003.png

  5. Amazon SNS のプラットフォームアプリケーションが作成されます。
    • 6004.png

7.Amazon Cognito に ID プールを作成する

  1. 以下のサイトから、Amazon Cognito フェデレーテッドアイデンティティのコンソールを開きます。
  2. 「新しい ID プールの作成」ボタンを押下します。
    • 7001.png

  3. 「使用開始ウィザード - ステップ 1:ID プールを作成する」画面が表示され、以下の項目を設定し、「プールの作成」ボタンを押下します。
    • ID プール名 : ※任意
    • 認識されていない ID
      • 認識されていない ID に対してアクセスを有効にする : ☑︎

    • 7002.png

  4. 画面が更新されるので、新しく作成される2つ(Auth と Unauth)の IAM ロールのロール名を確認し、「許可」ボタンを押下します。
    • 7003.png

  5. 「サンプルコード」画面が表示され、ID プールが作成されます。
    • 7004.png

8.作成した Amazon Cognito ID プールの IAM ロール に Amazon SNS 用のインラインポリシーを追加する

  1. 以下のサイトから、AWS Identity and Access Management (IAM) のコンソールを開きます。
  2. AWS Identity and Access Management (IAM) のコンソールの左側ペインから、「ロール」を選択します。
    • 8001.png

  3. ロールの一覧が表示されるので、作成した Amazon Cognito ID プールの IAM ロール (Unauth) の「ロール名」リンクをクリックします。
    • 8002.png

  4. 作成した Amazon Cognito ID プールの IAM ロール (Unauth) の概要の画面が表示されるので、「アクセス権限」のタブを選択し、「インラインポリシーの追加」リンクをクリックします。
    • 8003.png

  5. 「ポリシーの作成」画面が表示されるので、「ビジュアルエディタ」のタブを選択後、以下の項目を設定し、「Review policy 」ボタンを押下します。
    • サービス : SNS
    • アクション :
      • 書き込み :
        • ☑︎ CreatePlatformEndpoint
        • ☑︎ SetEndpointAttributes

    • 8004.png

  6. 「ポリシーの作成 - ポリシーの確認」画面が表示されるので、以下の項目を設定し、「Create policy 」ボタンを押下します。
    • 名前 : ※任意

    • 8005.png

  7. 作成したインラインポリシーの内容を確認します。
    • 8006.png

9.Android アプリへ実装する

  1. <project>/<app-module>/build.gradle へ、以下のライブラリの登録の設定を追加する。
  2. build.gradle
    
    
    dependencies {
    
        
    
        // [2018.01.12] ADD - BGN
        // 「Android端末で、FCM経由でAWSSNSを受け取るまで - Qiita」
        // <https://qiita.com/k_keisuke/items/24afa62955808721033e>
        // ...
        compile 'com.google.firebase:firebase-core:10.2.1'
        compile 'com.google.firebase:firebase-messaging:10.2.1'
        // ...
        compile 'com.amazonaws:aws-android-sdk-core:2.4.+'
        compile 'com.amazonaws:aws-android-sdk-sns:2.4.+'
        // [2018.01.12] ADD - END
    }
    
    
    

  3. AndroidManifest.xml へ、追加するクラスの設定を追加する。
  4. AndroidManifest.xml
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.kusokamayarou.android_sns_sample0001">
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme"><!-- [2018.01.12] ADD - BGN -->
            <!--「マニフェストの設定」-->
            <!-- https://docs.kii.com/ja/samples/push-notifications/push-notifications-android-fcm/modify-manifest/ -->
            <service android:name=".MyFirebaseMessagingService">
                <intent-filter>
                    <action android:name="com.google.firebase.MESSAGING_EVENT"/>
                </intent-filter>
            </service>
            <service android:name=".MyFirebaseInstanceIDService">
                <intent-filter>
                    <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
                </intent-filter>
            </service>
            <!-- [2018.01.12] ADD - END -->
    
        </application>
    
    </manifest>
    

  5. 以下の MyFirebaseInstanceIDService.java を追加する。
  6. MyFirebaseInstanceIDService.java
    package com.kusokamayarou.android_sns_sample0001;
    
    import android.annotation.SuppressLint;
    import android.content.SharedPreferences;
    import android.util.Log;
    
    import com.google.firebase.iid.FirebaseInstanceId;
    import com.google.firebase.iid.FirebaseInstanceIdService;
    
    import java.util.HashMap;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    // 「Android端末で、FCM経由でAWSSNSを受け取るまで - Qiita」
    // <https://qiita.com/k_keisuke/items/24afa62955808721033e>
    
    import com.amazonaws.regions.Regions;
    import com.amazonaws.auth.CognitoCachingCredentialsProvider;
    import com.amazonaws.services.sns.AmazonSNSClient;
    import com.amazonaws.services.sns.model.CreatePlatformEndpointRequest;
    import com.amazonaws.services.sns.model.CreatePlatformEndpointResult;
    import com.amazonaws.services.sns.model.InvalidParameterException;
    import com.amazonaws.services.sns.model.SetEndpointAttributesRequest;
    
    public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
    
        private final static String TAG = MyFirebaseInstanceIDService.class.getSimpleName();
    
        private static final String APPLICATION_ARN = "[プラットフォームアプリケーションの ARN]";
        private static final String ENDPOINT = "https://sns.ap-northeast-1.amazonaws.com";
        //private static final String ACCESS_KEY = "AWSのアクセスキー";
        //private static final String SECRET_KEY = "AWSのSecretKey";
    
        @Override
        public void onTokenRefresh() {
            super.onTokenRefresh();
            String token = FirebaseInstanceId.getInstance().getToken();
            sendRegistrationToServer(token);
        }
    
        @SuppressLint("LongLogTag")
        private void sendRegistrationToServer(String token) {
            //AmazonSNSClient client = new AmazonSNSClient(generateAWSCredentials());
            CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                    getApplicationContext(),
                    "[Amazon Cognito の ID プールの ID]", // ID プールの ID
                    Regions.AP_NORTHEAST_1 // リージョン
            );
            AmazonSNSClient client = new AmazonSNSClient(credentialsProvider);
            client.setEndpoint(ENDPOINT);
            //SharedPreferenceに保存したendpointArnが存在したらそちらから取得するようにしてもOK
            String endpointArn = createEndpointArn(token, client);
            // 「[Android] データを保存し Android Studio で確認 SharedPreferences | nyanのアプリ開発」
            // <https://akira-watson.com/android/sharedpreferences.html>
            SharedPreferences dataStore = getSharedPreferences("DataStore", MODE_PRIVATE);
            SharedPreferences.Editor editor = dataStore.edit();
            editor.putString("DeviceToken", token);
            editor.putString("EndpointArn", endpointArn);
            editor.apply();
            Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | DeviceTokenArray: " + token);
            Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | EndpointArnArray: " + endpointArn);
            HashMap<String, String> attr = new HashMap<>();
            attr.put("Token", token);
            attr.put("Enabled", "true");
            SetEndpointAttributesRequest req = new SetEndpointAttributesRequest().withEndpointArn(endpointArn).withAttributes(attr);
            client.setEndpointAttributes(req);
        }
    
        @SuppressLint("LongLogTag")
        private String createEndpointArn(String token, AmazonSNSClient client) {
            String endpointArn;
            try {
                System.out.println("Creating platform endpoint with token " + token);
                CreatePlatformEndpointRequest cpeReq =
                        new CreatePlatformEndpointRequest()
                                .withPlatformApplicationArn(APPLICATION_ARN)
                                .withToken(token);
                CreatePlatformEndpointResult cpeRes = client
                        .createPlatformEndpoint(cpeReq);
                endpointArn = cpeRes.getEndpointArn();
            } catch (InvalidParameterException ipe) {
                String message = ipe.getErrorMessage();
                System.out.println("Exception message: " + message);
                Pattern p = Pattern
                        .compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " +
                                "with the same token.*");
                Matcher m = p.matcher(message);
                if (m.matches()) {
                    // The platform endpoint already exists for this token, but with
                    // additional custom data that
                    // createEndpoint doesn't want to overwrite. Just use the
                    // existing platform endpoint.
                    endpointArn = m.group(1);
                } else {
                    // Rethrow the exception, the input is actually bad.
                    throw ipe;
                }
            }
            storeEndpointArn(endpointArn);
            return endpointArn;
        }
    
        /*
        private AWSCredentials generateAWSCredentials() {
            return new AWSCredentials() {
                @Override
                public String getAWSAccessKeyId() {
                    return ACCESS_KEY;
                }
    
                @Override
                public String getAWSSecretKey() {
                    return SECRET_KEY;
                }
            };
        }
        */
    
        private void storeEndpointArn(String endpointArn) {
            //SharedPreferenceにでもendpointArnを保存して、次回以降はcreateEndpointArnの処理を省略しても良い(公式はその方式になってる)
        }
    
        /*
        private String getEndPointArn() {
            //SharedPreferenceからendpointArnを取得して、次回以降はcreateEndpointArnの処理を省略しても良い(公式はその方式になってる)
        }
        */
    }
    

  7. 以下の MyFirebaseMessagingService.java を追加する。
  8. MyFirebaseMessagingService.java
    package com.kusokamayarou.android_sns_sample0001;
    
    import android.annotation.SuppressLint;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.support.v4.app.NotificationCompat;
    import android.util.Log;
    
    import com.google.firebase.messaging.FirebaseMessagingService;
    import com.google.firebase.messaging.RemoteMessage;
    
    public class MyFirebaseMessagingService extends FirebaseMessagingService {
    
        private final static String TAG = MyFirebaseMessagingService.class.getSimpleName();
    
        /**
         * Called when message is received.
         *
         * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
         */
        // [START receive_message]
        @SuppressLint("LongLogTag")
        @Override
        public void onMessageReceived(RemoteMessage remoteMessage) {
            // TODO(developer): Handle FCM messages here.
            // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
            Log.i(TAG, "From: " + remoteMessage.getFrom());
    
            // Check if message contains a data payload.
            if (remoteMessage.getData().size() > 0) {
                Log.i(TAG, "Message data payload: " + remoteMessage.getData());
            }
    
            // 「【Android】NotificationからIntentでデータを渡してアプリ起動するときのIntent.setFlagsメモ - Qiita」
            // <https://qiita.com/SnowMonkey/items/7cdc52c83bb9490d97a5>
            NotificationManager notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
            Intent intent = new Intent(this, MainActivity.class);
            intent.putExtra("hoge", "fuga");
            intent.setFlags(
                    Intent.FLAG_ACTIVITY_CLEAR_TOP  // 起動中のアプリがあってもこちらを優先する
                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED  // 起動中のアプリがあってもこちらを優先する
                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  // 「最近利用したアプリ」に表示させない
            );
            PendingIntent contentIntent =
                    PendingIntent.getActivity(
                            this,
                            0,
                            intent,
                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
            NotificationCompat.Builder mBuilder =
                    new NotificationCompat.Builder(this)
                            .setSmallIcon(android.R.drawable.sym_def_app_icon)
                            .setContentTitle(remoteMessage.getData().toString())
                            .setContentText("Notification Message")
                            .setStyle(new NotificationCompat.BigTextStyle().bigText("Notification Message"));
            mBuilder.setContentIntent(contentIntent);
            notificationManager.notify(0, mBuilder.build());
        }
    }
    

  9. MainActivity.java へ、以下のコードを追加する。
  10. MainActivity.java
    package com.kusokamayarou.android_sns_sample0001;
    
    import android.content.SharedPreferences;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    
    public class MainActivity extends AppCompatActivity {
    
        // [2018.01.12] ADD - BGN
        private final static String TAG = MainActivity.class.getSimpleName();
        // [2018.01.12] ADD - END
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // [2018.01.12] ADD - BGN
            // 「[Android] データを保存し Android Studio で確認 SharedPreferences | nyanのアプリ開発」
            // <https://akira-watson.com/android/sharedpreferences.html>
            SharedPreferences dataStore = getSharedPreferences("DataStore", MODE_PRIVATE);
            String DeviceTokenArray = dataStore.getString("DeviceToken", "");
            Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | DeviceTokenArray: " + DeviceTokenArray);
            String EndpointArnArray = dataStore.getString("EndpointArn", "");
            Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | EndpointArnArray: " + EndpointArnArray);
            // [2018.01.12] ADD - END
        }
    }
    

    10.アプリを実行し、Amazon SNS からの通知を確認する。

    1. こちらのサイト ( Android に Firebase Cloud Messaging クライアント アプリを設定する  |  Firebase ) にも記載がありますが、FCM の登録トークンを改めて取得したい場合は、アプリをアンインストールしたりすればいい様なので、とりあえず一度アプリをアンインストールします。アプリのアンインストールは、ググれば出て来ると思います。
    2. Android Studio からデバッグモードで RUN 実行します。
      • 10-01.png

    3. Android Studio の Logcat に登録トークンがログ出力されます。
      • 10-02.png

    4. Amazon SNS のプラットフォームアプリケーションの詳細画面を開き、登録トークンからエンドポイントが作成されたか確認します。
      • 10-03.png

    5. 作成されたエンドポイントを選択し、「エンドポイントの発行」ボタンを押下します。
      • 10-04.png

    6. 「メッセージを発行」画面が表示されるので、以下の項目を設定し、「メッセージの発行」ボタンを押下します。
      • メッセージ形式 : JSON
      • メッセージ
        • { "GCM": "{ \"data\": { \"message\": \"Test\" } }" }

      • 10-05.png

    7. 実機に通知が届いたか確認します。
      • 10-06.jpg

    99.ハマりポイント

  • 通知の確認のところでも確認しましたが、登録トークンがどうやったら改めて取得できるのかよくわからなく、ちょっと戸惑いました。

  • 今回、Firebase プロジェクトのアプリの登録を手動で行いましたが、以下のサイトを見ると Firebase Assistant を使用して登録する手順を推奨している様にも思いました。
  • 後は単純に Android 開発自体ほぼ初めてだったので、Android Studio の使い方からよくわからなかったので、ちょっと苦労しました。

  • っていうか、Android も触ったことなかったので、アプリのアンインストールのやり方とか、通知の確認の仕方とか、逐一わからなくて苦労しました。

XX.まとめ

コードにもコメントで記載していますが、今回も多くのサイトに助けられました。

特に、こちらのサイトはお世話になりました。

っていうか、
ほぼ丸コピに近い様な…。
本当にありがとうございました。

他にも、
* [Android]プッシュ通知実装[Firebase][FCM] – チェンぴログ
* マニフェストの設定
* [Android] データを保存し Android Studio で確認 SharedPreferences | nyanのアプリ開発
* 【Android】NotificationからIntentでデータを渡してアプリ起動するときのIntent.setFlagsメモ - Qiita

など、
参考にさせて頂きました。
ありがとうございました。

次は、iOS だな…。

kusokamayarou
鹿児島市の企業に勤めて、枕崎でテレワークしてる IT エンジニアです。 最近、自分で事業をしようとちょっとずつ動き始めました。
http://kusokamayarou.hatenablog.com/
genbasupport
建設業を支えるコミュニケーション&マネジメントのサービスを開発・運営するエンジニアチーム
http://www.genbasupport.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした