Android 開発環境構築 ＆ Amazon SNS で PUSH 通知のサンプルアプリを作る

# <i class="fa fa-map-marker"></i> 目的
Android Studio で Android アプリの開発環境を構築して、
Amazon Simple Notification Service (SNS) + Google Cloud Messaging for Android (GCM) で PUSH 通知するアプリを作ってみましたので、その記録です。

## <i class="fa fa-globe"></i> やりたいこと
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/172e2f8a-d90f-e975-ea93-c1eb860ec48a.png)

これ。

## 実施環境
Windows 7 / 32bit
Android 6.0.1 (Nexus 5X)

## 概要
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/af68808a-2786-301e-9096-b9e6007826b8.png)
( 画像引用元：https://docs.aws.amazon.com/ja_jp/sns/latest/dg/SNSMobilePush.html ）
Amazon SNS から Android 端末へは GCM を利用して PUSH 通知します。Amazon SNS を利用すると iOS (APNS) やメール通知なども同じ仕組みで PUSH できるのがいいところですね。

## 実施結果
Android Studio は触るところから初めてだったので、試行錯誤しながらで丸2日ほどかかりました。
Eclipse を使用したら (AWS のデモ Android アプリは Eclipse 向けに作られているので）もっと早くできたかもしれません。
iOS も試してみたいんだけれど、iPhone が手元にないので iPhone5 中古購入しようかな。

# <i class="fa fa-map-o"></i> 前提資料
こちらを見ながら進めました。

- Google Cloud Messaging for Android の使用開始 - Amazon Simple Notification Service
    - https://docs.aws.amazon.com/ja_jp/sns/latest/dg/mobile-push-gcm.html
- Try Cloud Messaging for Android  |  Cloud Messaging  |  Google Developers
    - https://developers.google.com/cloud-messaging/android/start

# <i class="fa fa-location-arrow"></i> 実施内容
## Google Cloud Messaging から API キー取得
https://developers.google.com/mobile/add?platform=android
こちらから API キーを取得します。手続きには Goolge アカウントが必要ですのでログインしてから臨みます。

### アプリケーション情報を入力
とくに使う情報ではないので、てきとうに入力します。

- AppName：MYAPPNAME
- Android Package Name: MY.ANDROID.PACKAGE.NAME
- Your country/region:  日本

### Cloud Messaging を選択して有効化
- ENABLE CLOUD MESSAGING

### APIキーが表示される
Server API Key は AWS の Application に。 Sender ID は Android アプリケーションに埋め込みますので控えておきます。

- メモ
    - Server API Key：xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxx
    - Sender ID(プロジェクト番号)：000000000000

## Android Studio にて APK 作成
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/f43dd3ea-2f76-f978-2448-e573fbc06c57.png)

### Android Studio インストール
https://developer.android.com/studio/index.html
android-studio-bundle-143.2915827-windows.exe
※インストール手順は省略

### サンプルプログラムのダウンロード
https://docs.aws.amazon.com/ja_jp/sns/latest/dg/mobile-push-gcm.html#create-project-gcm
リンク先にある snsmobilepush.zip をダウンロード、ローカルに解凍します。

### インポート
リンクには「Eclipseで開いてね」とあるけれど、あえて Android Studio でやります。
先ほど解凍したパスをインポート。

- MENU > File > New > Import Project
    - snsmobilepush\AndroidMobilePushApp\AndroidMobilePushApp


### strings.xmlファイルを編集
さきほどメモした API の Sender ID(プロジェクト番号) で strings.xml の project_number を更新入力。

- PROJECT > app > res > values
    - strings.xml

```xml:strings.xml
    <string name="project_number">000000000000</string>
```

### ビルドに向けた修正作業
本来はこれだけやればビルドに進めるのはずなのですが、Android Studio ではインポートしただけではビルドできませんでした[^1]。以下に Android Studio でビルドまでこぎつける手順を記載します。
※ビルド時の試行錯誤は記事後半にもメモしてあります。

#### SDK の追加
Android SDK Manager を開き、SDK をインストール。

<!--
- SKD Platforms
    - Show Package Details をチェック
    - Android 4.3 (Jelly Bean) 
        - Android SDK Platform 18 (platforms;android-18)
→ ビルドプラットフォームを変更するのでいれなくていい
-->
- Estras
    - Google Play services (extras;google;google_play_services)
    - Google USB Driver (extras;google;usb_driver)


#### png→jpgリネーム
- PROJECT > app > src > main > res
    - drawable-hdpi
        - ic_launcher.png
    - drawable-mdpi
        - ic_launcher.png
    - drawable-xhdpi
        - ic_launcher.png
    - drawable-xxhdpi
        - ic_launcher.png

これらの .png を **.jpg** にリネームします[^2]。

#### ビルドファイル修正

- PROJECT > app 
    -  build.gradle

※プロジェクト直下の build.gradle ではなく、app 配下のほうを修正します。

コンパイル SDK のバージョンを 18 -> 23 に修正、下位 SKD を 8 -> 9 に修正。

```text:build.gradle
android {
    compileSdkVersion 23
    buildToolsVersion "24.0.1"

    defaultConfig {
        applicationId "com.amazonaws.androidtest"
        minSdkVersion 9
        targetSdkVersion 23
    }
```

以下を追記してライブラリ参照を追加。

```text:build.gradle
dependencies {
    compile 'com.google.android.gms:play-services:+'
    compile 'com.android.support:support-v4:+'
}
```

#### Manifest も修正
- PROJECT > app > src > main 
    -  AndroidManifest.xml

コンパイル SDK のバージョンを 18 -> 23 に修正、下位 SKD を 8 -> 9 に修正。

```xml:AndroidManifest.xml
    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="23" />
```

### ビルド確認
メニューから APK を作成してみて、ビルドエラーが起きないことをとりあえず確認します。

- MENU > Build > Build APK

<!-- Nexus Sのエミュレータ作ってデバッグ起動したりしたけど、APK 作れたならやらなくていいみたい -->

### Android 端末と接続
先ほど Google USB Drivers をインストールしているので、規定では下記パスに USB ドライバがあるはずです。

- C:\Users\USERNAME\AppData\Local\Android\Sdk\extras\google\usb_driver

Android 端末と Windows PC を USB 接続し、次のデバイスのドライバを更新します。

- コンピュータの管理 > デバイスマネージャ > ほかのデバイス
    - Android ADB Interface

### Android 端末の設定
Android 端末側では、USBデバッグを有効化します。
※説明省略

### デバッグで起動
Android Studio に戻り、Debug 'app'を実行すると
Android 端末に APK がインストールされて起動します。
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/35b83b43-d779-0b4d-923b-fe18ea1f16ac.png)

### Registration ID 取得
この *Wecome!* 画面はさほど重要じゃないのでログを確認します。
Android Monitor ウィンドウを見ると、**registrationId** という文字列が表示されていて、このIDを控えます。

```text:AndroidMonitor
08-17 13:15:14.565 31789-31890/com.amazonaws.androidtest I/OpenGLRenderer: Initialized EGL, version 1.4
08-17 13:15:14.587 31789-31789/com.amazonaws.androidtest I/onResume: no missed messages
08-17 13:15:14.873 31789-31890/com.amazonaws.androidtest V/RenderScript: 0x7f79a82000 Launching thread(s), CPUs 6
08-17 13:15:14.921 31789-31895/com.amazonaws.androidtest I/registrationId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
08-17 13:15:18.573 31789-31789/com.amazonaws.androidtest I/onResume: no missed messages
```

- メモ
    - registrationId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

やっと Android 側の作業が終わりました。（後半、説明をだいぶ端折りました）

## AWS から PUSH
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/2ad9d179-287b-684d-000d-71c0e9496f20.png)

AWS Console に移動します。

- AWS CONSOLE > SNS

## Create Topic
トピックを作成します。エンドポイントに配信する単位グループのようです。以下設定値は箇条書きです。

- Topic name: TOPICNAME
- Display name: DISPLAYNAME

作成後に表示される ARN(TopicARN) をメモしておきます。のちほど SNS のエンドポイント（配信端末）として設定します。

- メモ
    - TopicARN: arn:aws:sns:ap-northeast-1:000000000000:TOPICNAME

## Applications
Endpoint に Android 端末を登録します。GCM から取得した Sender ID(プロジェクト番号)を Device token として入力。

- Add Endpoint
    - Device token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    - User data: MYANDROIDDEVICE

アプリケーションを作成します。GCM(Android) + APNS(iOS) など複数設定もできますが、今回は GCM のみ設定します。

- Create Platform Application
    - Application name: APPLICATIONNAME
    - Push notification platform: Google Cloud Messaging (GCM)
    - API Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxx

作成後に ARN のリンクをクリックして、 表示される EndPointARN をメモしておきます。

- メモ
    - EndPointARN: arn:aws:sns:ap-northeast-1:000000000000:**endpoint**/GCM/APPLICATIONNAME/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx


## Subscriptions
先ほどの Topic にアプリケーションを登録[^3]します。

- Create Subscription
    - Topic ARN: arn:aws:sns:ap-northeast-1:000000000000:TOPICNAME
    - Protocol: Application
    - Endpoint: arn:aws:sns:ap-northeast-1:000000000000:**endpoint**/GCM/APPLICATIONNAME/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

## やっと配信
- Topics
    - TOPICNAME にチェック
        - Publish To Topic
- Raw
    - 適当な本文
        - **Publish Message**

## できました！！
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/172e2f8a-d90f-e975-ea93-c1eb860ec48a.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/130874/167977cc-ba75-804b-5929-0d1fbd091898.png)


（以上、おつかれさまでした）

# <i class="fa fa-pencil-square-o"></i> おまけのエラー集
いたるところで躓いたので、エラーログと対応をメモとして残します。

###### ビルドエラー：Module 'AndroidMobilePushApp' is not backed by gradle
インポートしたプロジェクトでは、Android SDK Platform 18 が入っていないとbuild.grable にてビルドエラーが発生しました。Build Platform を変更するか SKD Platform 18 を入れる必要がありそうです。

```text:Message
Error running build:
Module 'AndroidMobilePushApp' is not backed by gradle
```

###### ビルドエラー：libpng error: Not a PNG file
ic_launcher.png がどうのこうのというエラーがおきました。ic_launcher.png というファイルはよく見ると jpeg 形式で[^2]拡張子どおりのフォーマットで読み込めないのが原因でした。すべてpng に変換してあげるか、拡張子を jpg に変えてあげる必要がありそうです。

```text:Message
20:51:45 Executing tasks: [:app:assembleDebug]
20:51:50 Gradle build finished with 1 error(s) in 5s 86ms
20:51:50 Build APK: Errors while building APK. You can find the errors in the 'Messages' view.
```


```text:Message
Information:Gradle tasks [:app:assembleDebug]
:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:checkDebugManifest
:app:prepareDebugDependencies
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:mergeDebugShaders UP-TO-DATE
:app:compileDebugShaders UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources
AAPT err(Facade for 2289226): libpng error: Not a PNG file
AAPT err(Facade for 15740726): libpng error: Not a PNG file
AAPT err(Facade for 27895062): libpng error: Not a PNG file
AAPT err(Facade for 30208515): libpng error: Not a PNG file
Error:Execution failed for task ':app:mergeDebugResources'.
> Crunching Cruncher ic_launcher.png failed, see logs
```

###### ビルドエラー：Gradle DSL method not found: 'compile()'
アプリケーションのビルド定義

- PROJECT > app > build.grandle

ではなく、プロジェクトのビルド定義

- PROJECT > build.grandle

にまちがって dependencies ～の定義を追記していたところエラーがおきていた。

```text:Message
Error:(18, 1) A problem occurred evaluating root project 'AndroidMobilePushApp'.
> Could not find method compile() for arguments [com.google.android.gms:play-services:+] on root project 'AndroidMobilePushApp'.
```


```text:Message
Error:(18, 0) Gradle DSL method not found: 'compile()'
Possible causes:<ul><li>The project 'AndroidMobilePushApp' may be using a version of Gradle that does not contain the method.
<a href="open.wrapper.file">Open Gradle wrapper file</a></li><li>The build file may be missing a Gradle plugin.
<a href="apply.gradle.plugin">Apply Gradle plugin</a></li>
```

###### Android Studio プロキシ設定
社内ネットワーク環境であるため、Android SDK Manager にもプロキシ設定が必要でした。次の各項目を埋めました。

 - Appearance & Behavior > System Settings > HTTP Proxy
    - Manual Proxy Configuration
    - Host Name
    - Port Number
    - Proxy authnenication
        - Login
        - Password

###### ビルドエラー：Manifest merger failed with multiple errors
minSdkVersion は 8 にしろ、と出ていました。

```text:Message
:app:processDebugManifest
C:\Users\USERNAME\AndroidStudioProjects\AndroidMobilePushApp\app\src\main\AndroidManifest.xml:20:5-71 Warning:
	Element uses-permission#android.permission.GET_ACCOUNTS at AndroidManifest.xml:20:5-71 duplicated with element declared at AndroidManifest.xml:18:5-71
C:\Users\USERNAME\AndroidStudioProjects\AndroidMobilePushApp\app\src\main\AndroidManifest.xml:13:5-15:41 Error:
	uses-sdk:minSdkVersion 8 cannot be smaller than version 9 declared in library [com.google.android.gms:play-services:9.4.0] C:\Users\USERNAME\AndroidStudioProjects\AndroidMobilePushApp\app\build\intermediates\exploded-aar\com.google.android.gms\play-services\9.4.0\AndroidManifest.xml
	Suggestion: use tools:overrideLibrary="com.google.android.gms.play_services" to force usage
See http://g.co/androidstudio/manifest-merger for more information about the manifest merger.
:app:processDebugManifest FAILED
Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed with multiple errors, see logs
```

###### ビルドエラー：To run dex in process, the Gradle daemon needs a larger heap.
ヒープサイズ不足、2048 MB 必要なところ、989 MB しか確保できませんでしたとのこと。環境によってはおきないと思う。

```text:Message
:app:transformClassesWithDexForDebug
To run dex in process, the Gradle daemon needs a larger heap.
It currently has approximately 989 MB.
For faster builds, increase the maximum heap size for the Gradle daemon to more than 2048 MB.
To do this set org.gradle.jvmargs=-Xmx2048M in the project gradle.properties.
For more information see https://docs.gradle.org/current/userguide/build_environment.html
Error:The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
:app:transformClassesWithDexForDebug FAILED
Error:Execution failed for task ':app:transformClassesWithDexForDebug'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Program Files\Java\jdk1.8.0_101\bin\java.exe'' finished with non-zero exit value 2
```

エラーのとおり、下記ファイルを追記修正。2048 MB も空きがないので 1024 MB を設定してみる。

- PROJECT > gradle.properties

```properties:gradle.properties
org.gradle.jvmargs=-Xmx1024M
```

###### ビルドエラー：Error occurred during initialization of VM
Java VM のヒープサイズエラー。2097152KB も確保できないとのこと。

```text:Message(プロジェクト開いたときのエラー)
Gradle 'AndroidMobilePushApp' project refresh failed
Error:Unable to start the daemon process.
This problem might be caused by incorrect configuration of the daemon.
For example, an unrecognized jvm option is used.
Please refer to the user guide chapter on the daemon at https://docs.gradle.org/2.10/userguide/gradle_daemon.html
Please read the following process output to find out more:
-----------------------
Error occurred during initialization of VM
Could not reserve enough space for 2097152KB object heap
```


```text:Message(ビルド実行したときのエラー)
Information:Gradle tasks [assemble]
Error:Failed to complete Gradle execution.

Cause:
Unable to start the daemon process.
This problem might be caused by incorrect configuration of the daemon.
For example, an unrecognized jvm option is used.
Please refer to the user guide chapter on the daemon at https://docs.gradle.org/2.10/userguide/gradle_daemon.html
Please read the following process output to find out more:
-----------------------
Error occurred during initialization of VM
Could not reserve enough space for 2097152KB object heap
```

2048 MB 割り当てから 1024 MB 割り当てに変更。

- PROJECT > gradle.properties

```properties:gradle.properties
org.gradle.jvmargs=-Xmx1024M
```
###### ビルドエラー：The number of method references in a .dex file cannot exceed 64K
.dex ファイルがでっかくなっちゃった、みたいな。

```text:Message
Error:The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
:app:transformClassesWithDexForDebug FAILED
Error:Execution failed for task ':app:transformClassesWithDexForDebug'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Program Files\Java\jdk1.8.0_101\bin\java.exe'' finished with non-zero exit value 2
```

 multiDexEnabled true を追記する。

- PROJECT > app > build.grandle

```text:build.grandle
    defaultConfig {
        applicationId "com.amazonaws.androidtest"
        minSdkVersion 9
        targetSdkVersion 23
        multiDexEnabled true
    }
```

<!--
750 MB ->  1024 MB に変更して Android Studio 再起動。

- C:\Program Files\Android\Android Studio\bin\
    - studio.exe.vmoptions

```text:studio.exe.vmoptions
-Xmx1024m
```
→build.gandleで設定するから不要
-->

# <i class="fa fa-external-link"></i> 参考リンク
（お世話になりました）

- EclipseからAndroid Studioにプロジェクトを移行 の続き - ikechampionのブログ
    - http://ikechampion.hatenablog.com/entry/2014/05/07/185512
- Android StudioでSyncすると、「gradle dsl method not found 'compile()'」エラー - 日々精進
    - http://anton0825.hatenablog.com/entry/2014/11/01/000000
- Android Studio への引っ越しトラブル ~ Manifest Merger編 ~
    - http://bitwave.showcase-tv.com/android-studio-%E3%81%B8%E3%81%AE%E5%BC%95%E3%81%A3%E8%B6%8A%E3%81%97%E3%83%88%E3%83%A9%E3%83%96%E3%83%AB-manifest-merger%E7%B7%A8/
- build.gradleに設定するAndroid SDKやbuildTools,versionCode等について - Qiita
    - http://qiita.com/tomoima525/items/870ac52ce3256e785c4c
- Android StudioでビルドしたらGradleが「Could not reserve enough space for object heap」と嘆きだした時の設定箇所 - Qiita
    - http://qiita.com/keigodasu/items/ff6360f93132246b9ce2
- Configure Apps with Over 64K Methods | Android Studio
    - https://developer.android.com/studio/build/multidex.html?hl=ja
- Google Cloud Messagingでハマったこと - Qiita
    - http://qiita.com/sakebook/items/6ea3e27025ea2e0c4997

[^1]: 素直に Eclipse でやればよかったかも。。。
[^2]: 目GREPをちょっと覚えました。Stirling 等のヘキサエディタで開いてヘッダを見ます。SJIS表示で JPEG は "JFIF"、PNG は "臼NG" が確認できます。参考：http://www.slideshare.net/murachue/grep-8132856
[^3]: おまけ：メール配信の場合はEmailを選択、指定したメールには事前に承認依頼(title:AWS Notification - Subscription Confirmation) が届き、承認リンクのクリックまではステータスが「PendingConfirmation」になるようです。何故かとどかねーなと思ったら迷惑メールフォルダを確認しましょう。