【FCM】FCM＋Spring bootを用いたメッセージ送信の実装

# 概要
GoogleのFCMサービスを用いてAndroid端末にメッセージを送信する。
HTTP v1に対応したサーバーAPIを用いてメッセージを送信し、Android端末でPush通知を受け取ることが出来るようにする。

# 準備
## Firebase Consoleのアカウントを用意し、ログインする。
プロジェクト名を入力（任意の名前）
アナリティクスと課金の地域：日本
プロジェクトを作成を押下

## 証明書の発行とSHA-1キーの取得

```bash
keytool -exportcert -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore
```

パスワードの入力を求められます。

```
android
```
で発行できました。

デバッグ用の署名証明書 SHA-1（省略可）に、debug.keystoreを発行した際にコンソールで出力されたSHA-1キーをそのまま貼り付ける。

アプリを登録を押下。

## Android Studioで新規プロジェクトを作成(Empty Activity）

google-services.jsonをダウンロードすし、Android Studioのapp直下に配置する。

AndroidManifest.xmlに下記のサービスを追記。

```xml
        <service
            android:name="com.example.shosakaguchi.notifytest.FcmMessagingService"
            >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <service
            android:name="com.example.shosakaguchi.notifytest.FcmInstanceIdService"
            >
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
```

アプリのbuild.gradleに下記の依存関係を追加。

```gradle
    api 'com.google.firebase:firebase-core:+'
    api 'com.google.firebase:firebase-messaging:+'
    api 'com.fasterxml.jackson.core:jackson-databind:+'
    api 'com.google.firebase:firebase-auth:+'
```

プロジェクトのbuild.gradleに下記の依存関係を追加+google-servicesを適用。

```gradle
dependencies {
api "com.google.firebase:firebase-messaging:10.2.4"
}
apply plugin: 'com.google.gms.google-services'
```


プロジェクトのRebuildをする。

Android Studioで下記のクラスを生成する。


FirebaseMessagingServiceを継承したクラス（中身は空でOK）

```java
任意のクラス名　extends FirebaseMessagingService
```

FirebaseInstanceIdServiceを継承したクラス

```java
任意のクラス名 extends FirebaseInstanceIdService
    @Override
    public void onTokenRefresh() {
        // Get updated InstanceID token.
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d(TAG, "Refreshed token: " + refreshedToken);

    }
```
MainActivity.javaにトークンを端末トークンを取得するコードを追加。

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //get device token
        FcmInstanceIdService sv = new FcmInstanceIdService();
        sv.onTokenRefresh();
    }
}

```

プロジェクトを、プッシュ通知受け取る端末でRunする。

Android Studio上のLogcatコンソールに出てきたトークンを控える。

## サーバーAPIの実装(Spring boot)
Eclipse上でSTSを用い、Spring Projectを新規作成する。

build.gradleに下記の依存関係を追加する。

```gradle
	compile('com.fasterxml.jackson.core:jackson-databind:+')
	compile('com.google.firebase:firebase-admin:+')
```
Gradleタスクから、buildDependentsを叩く。

下記の情報をFirabaseコンソールページの設定ページから準備する。
・POST送信先のエンドポイントURL
https://fcm.googleapis.com/v1/projects/全般ページのプロジェクトID/messages:send"

・秘密鍵
Firabaseコンソールの設定ページからサービスアカウントをクリック
Firebase Admin SDKタブの「新しい秘密鍵の生成」を押下。
ほにゃらら.jsonがダウンロードされるので、Springプロジェクトの適当なリソースフォルダに配置しておく。

### initializeメソッドの実装

```java
	public static void initializeToken() throws IOException{
		FileInputStream serviceAccount = new FileInputStream("ダウンロードした秘密鍵のパス");

		FirebaseOptions options = new FirebaseOptions.Builder()
		    .setCredentials(GoogleCredentials.fromStream(serviceAccount))
		    .setDatabaseUrl("https://プロジェクトID.firebaseio.com")
		    .build();

		FirebaseApp.initializeApp(options);
	}
```
### アクセストークン取得メソッドの実装

```java
	public static  String getAccessToken() throws IOException {
		  GoogleCredential googleCredential = GoogleCredential
		      .fromStream(new FileInputStream("ダウンロードした秘密鍵のパス"))
		      .createScoped(Arrays.asList("https://www.googleapis.com/auth/firebase.messaging"
		    		  ));
		  googleCredential.refreshToken();
		  return googleCredential.getAccessToken();
	}
```

とりあえずサーバーアプリを実行したらメッセージを送信するようにするため、mainクラスに処理をべたがきする

TestProject.java

```java
	public static void main(String[] args) {
		SpringApplication.run(PushAppTestApplication.class, args);
		System.out.println("start");

		try {
		//initialize firebasio
		initializeToken();

		//post param create
		URL url = new URL(POST送信先のエンドポイントURL);
		HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
		httpURLConnection.setRequestMethod("POST");
		httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
//		System.out.println("token" + getAccessToken());
		httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
		httpURLConnection.setDoOutput(true);

		httpURLConnection.connect();

		OutputStream out = httpURLConnection.getOutputStream();
		PrintStream ps = new PrintStream(out);
		ps.print(リクエストメッセージJSONのBODY部（後述）);
		ps.close();

		//return response output
		InputStream is = httpURLConnection.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        String s;
	    while ((s = reader.readLine()) != null) {
			System.out.println(httpURLConnection.getResponseMessage() + httpURLConnection.getResponseCode());
	        }
			reader.close();
			httpURLConnection.disconnect();
			} catch(Exception e) {
				e.printStackTrace();
		}
		System.out.println("end");
	}
```

リクエストメッセージJSONのBODY部


```json
	private final static String reqJson = "{\n" +
			"  \"message\":{\n" +
			"    \"token\" : \"Android Studio上のLogcatコンソールに出てきたトークン",\n" +
			"    \"notification\" : {\n" +
			"      \"body\" : \"テストメッセージ\",\n" +
			"      \"title\" : \"テストタイトル\",\n" +
			"      }\n" +
			"   }\n" +
			"}";
```

実装は以上。

今回は単一の端末にPush通知を送信することを主に実装しました。
実務では、複数の端末に送る場合の想定や、送信時間の想定(cronバッチなど）、デバイストークンをサーバに送信する仕組みやそれを保管することなど、密に考える必要がります。

次回以降、それらのHowToや事例を紹介したいと考えてます。

お疲れ様でした。