Google Cloud Messaging(GCM)からFirebase Cloud Messaging(FCM)への移行(android)
GCM/FCMとは
GCMとは、android端末のpush通知をサポートするクラウドサービスのこと
しかし、GCMは2019.4.11でサポートが完全に停止する
そのかわり、googleは新しくFCMの利用を呼びかけている
またFCMはandroidだけでなく、iOSもサポート対応のクロスプラットフォームである
移行のきっかけ
なぜ移行しようという話になったのかだが、サポート終了するっていうのは調べ始めてから発覚したこと。
そもそもの理由としては、targetSdkVersion 26以上じゃないとだめだよっていうgoogleのお達しがあったので、バージョンあげたことが発端だった。
2018年8月からtargetSdkVersionをAPIレベル26(Android 8.0)以上にする必要がある
バージョンアップをしたところ、バックグラウンドでの通知が届かなくなってしまったのである。
アプリが開いている状態なら問題なく通知が届くのだが、アプリを終了すると以下のようにエラーがでて落ちてしまっていた。
そしてログもめっちゃ吐かれてた
09-03 16:30:23.564 27698-27698/your-package E/AndroidRuntime: FATAL EXCEPTION: main Process: your-package, PID: 27698 java.lang.RuntimeException: Unable to start receiver com.google.android.gcm.GCMBroadcastReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=your-package cmp=your-package/.GCMIntentService (has extras) }: app is in background uid UidRecord{a11f8da u0a520 RCVR idle procs:1 seq(0,0,0)} at android.app.ActivityThread.handleReceiver(ActivityThread.java:3267) ...
社内の人に相談したところ、「FCM使ってますけどそんなの見たことないですよ?」って言われたので、FCMに移行することにした。
移行の手順
基本的には以下のURLの通りに進めれば問題ない
Migrate a GCM Client App for Android to Firebase Cloud Messaging | Cloud Messaging | Google Developers
1 . 以下のURLからfirebaseプロジェクトの作成
https://console.firebase.google.com
google-services.json
をプロジェクトに追加する必要があるため、その手順
1.1. 対象のプラットフォームを選ぶ(この記事ではandroidを扱う)
1.2. 必要事項の入力
証明書の確認は以下のように行う
$ keytool -v -list -alias androiddebugkey -keystore ~/.android/debug.keystore $ keytool -v -list -alias <your-key-name> -keystore <path-to-production-keystore>
https://developers.google.com/android/guides/client-auth
1.3. google-services.json
をダウンロード
app/
直下に配置する
1.4. それぞれのbuild.gradeに追記してsync
最新のバージョンがあればandroid studioが教えてくれるのでそれに合わせる
1.5. アプリを実行、同期確認
2 . build.gradleの変更
3 . AndroidManifest.xmlの変更
自分が行ったものをdiff形式でぺたり
4 . server endpointsの変更
https://android.googleapis.com/gcm/send
を
http://fcm.googleapis.com/fcm/send
に変更
5 . その他設定
それぞれの実装状況に合わせて対応部分を修正
自分の所属プロジェクトでは、真ん中の
Optional: migrate your GcmListenerService
が修正対象でした
簡単に説明すると、AndroidManifest.xmlの変更と、serviceの変更
remoteMessageからデータを取得するとき、一般的な記事ではgetNotification()
とかを使ったら値がnullになっていた
そこで、従来どおりintent経由で取得する方法として、toIntent()
というまさに求めていた機能があったのでこれを利用
(あとで確認したらgetData()
の方にはデータが入ってたのでこっち使ったほうがいいかも?)
Bundle extras = intent.getExtras();
を
Bundle extras = remoteMessage.toIntent().getExtras();
認証まわりはPHPを使ってたので元から特に書いてなかった
これで無事に今までどおり通知が届くようになった
参考URL
claspを使ったGoogle App Scriptのローカル開発・自動コンパイル
claspとは
claspは、Google App Script(GAS)をローカル環境で開発するためのgoogle製ツールである
ローカルで作業できる一番のメリットは、やはりgitとの連携が可能になることだろう
しかし、gasはES6には対応しておらず、イマドキのjsのコーディングスタイルは使えない
そこで本記事では、本ブログの前回の記事を利用して、
をすべて自動で行えるようにする
claspのセットアップ
1 . claspのインストール
npm install -g @google/clasp
googleアカウントをプロジェクトごとに使い分ける場合はローカルにインストールし、package.jsonのscriptにloginみたいなのを作ればいいと思う
2 . 以下のサイトでGoogle Apps Script APIをオンにする
https://script.google.com/home/usersettings
3 . ログインする
clasp login
ここまでの作業は最初の一度だけ行えばよい
ローカル環境の構築
claspの設定
- gasやスプレッドシートを新規作成する場合
clasp create <project_name>
とすれば、gasが連携したアカウントのgoogle drive上に作成され、ローカルに.clasp.json
とappsscript.json
の2つのファイルが生成される
それぞれ以下のようなファイルになっている
- gasやスプレッドシートがすでに存在する場合
.clasp.jsonに記述するscript idを取得し、手動で作成する
idの場所は
・ gasのURL
・ ファイル > プロジェクトのプロパティの「スクリプトID」
のいずれかから取得する(どちらも同じid)
前まではプロジェクトキーを使っていたが、現在は変わったらしい
古いプロジェクトだとurlがプロジェクトキーのものになっていて使えないので要注意
そして
clasp pull
を実行することで、google drive上のgasファイルをローカルに同期することができる
.claspignoreの追加
基本的に、ローカルのファイルをgas上に同期(push)しようとすると、すべてのファイルが同期されてしまう
そのため、.claspignore
ファイルを作成することで、どのファイルをpushするかを決める(.gitignore
と同じ役割)
このとき、pushするファイルにはappsscript.json
が必ず含まれていなくてはいけない
以下のようなファイルを作成するとよい(main.jsはgas上に同期するファイル)
(余談)
本来なら、.claspignore
など作らずとも、.clasp.json
のプロパティにrootDirなるものが存在し、それを指定すれば自動的にそのディレクトリ内のファイルのみをターゲットとしてpushするファイルを決めてくれるはず、だった。
しかし、appsscript.json
がどう頑張ってもignoreされてしまい、2018/08/20現在まだその問題が続いている
$ clasp status Not ignored files: └─ dist/main.js Ignored files: └─ dist/appsscript.json
なので、今は諦めて.claspignore
を使おう
Command Failed: clasp push · Issue #281 · google/clasp · GitHub
babelの設定
前回の記事を参考に構築
そして、package.jsonを以下のように書き換える
"scripts": { "start": "watch 'npm run build' src/", "build": "node index.js && npm run push", "push": "clasp push" }
build後にclasp push
を実行するコマンドを追加
これで、前回同様
$ npm start
を実行すれば、自動的にgas上のファイルの変更まで行うことができる
gasのファイルを変更しても、ブラウザ更新しないと反映されないので一瞬ドキッとする…
なにか組み合わせてできないものだろうか
追記:20181218
20181218時点でclaspの最新版が 1.7.0
だったので、それに移行した。
$ npm info @google/clasp versions [ '1.0.0', '1.0.1', '1.0.2', '1.0.3', '1.0.4', '1.0.5', '1.0.6', '1.0.7', '1.1.0', '1.1.1', '1.1.2', '1.1.3', '1.1.4', '1.1.5', '1.3.0', '1.3.1', '1.3.2', '1.3.3', '1.4.0', '1.4.1', '1.5.0', '1.5.1', '1.5.2', '1.5.3', '1.6.0', '1.6.1', '1.6.2', '1.6.3', '1.7.0' ] $ npm install @google/clasp@1.7.0 -g /usr/local/bin/clasp -> /usr/local/lib/node_modules/@google/clasp/src/index.js + @google/clasp@1.7.0 added 16 packages from 11 contributors, removed 4 packages and updated 32 packages in 8.627s $ npm list --depth=0 -g /usr/local/lib ├── @google/clasp@1.7.0 ├── npm@6.4.0 ├── npm-check-updates@2.15.0 └── pure-prompt@1.7.0
また、このときに、このまま使おうとすると、以下のエラーが発生する
$ clasp push Error retrieving access token: TypeError: Cannot read property 'expiry_date' of undefined
そのため、再度ログインし直す
$ clasp logout $ clasp login
1.7.0に移行したことで、 .clasp.json
のrootDirを指定してpushするディレクトリを選べるようになった。
参考サイト
babelを使ったjavascriptの自動コンパイル
Babel
ECMAScript2015 (ES6)やECMAScript7などで書かれたソースコードを一般的なブラウザがサポートしているECMAScript5の形式に出力することができます。
つまり、新しい言語仕様を昔の仕様に落としてトランスコンパイルしてくれるコンパイラ
セットアップ
babel v6.0以降、pluginまたはpresetの指定が必要になっている
基本的には.babelrc
というファイルに記述するのだが、設定が少ないため、package.json
に合わせて記述する
"babel": { "presets": [ "env" ] }
env
というのは、コンパイル環境を自動で決定してくれるすぐれもの
【追記 20180827】
index.js
(下記参照)にenvの設定をして、そこを参照していたので、package.jsonに追加する必要はありませんでした。
あわせて、次の2つのパッケージをインストール
npm install babel-cli npm install babel-preset-env
コンパイラの作成
実際にコンパイルするためには、babelコマンドが必要になってくる
それをjs側で管理して実行できるようにする
ファイル名は、package.jsonのmainタグで管理されているファイル名に設定(デフォルトはindex.js
)
const babel = require('babel-core') const fs = require('fs') const srcFile = 'src/test.js' const distFile = 'dist/test.js' const options = {"presets": ["env"]} const code = fs.readFileSync(srcFile, 'utf-8') const newCode = babel.transform(code, options).code fs.writeFileSync(distFile, newCode) console.log('babel build finished!')
サンプルとして、ここでは
src/test.js
の内容を、
dist/test.js
にコンパイルして出力するようにしている
例えば、
class Hoge { constructor() { this.x = 3; this.y = 2; } add() { return this.x + this.y; } } const exp = (x, y) => x ** y; const hoge = new Hoge(); console.log(exp(hoge.x, hoge.y)); console.log(hoge.add());
を変換する。
$ node index.js
を実行すると、
"use strict"; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Hoge = function () { function Hoge() { _classCallCheck(this, Hoge); this.x = 3; this.y = 2; } _createClass(Hoge, [{ key: "add", value: function add() { return this.x + this.y; } }]); return Hoge; }(); var exp = function exp(x, y) { return Math.pow(x, y); }; var hoge = new Hoge(); console.log(exp(hoge.x, hoge.y)); console.log(hoge.add());
になる。ちゃんと実行もできる
自動コンパイル環境の構築
今のままだと、変更・保存して毎回node index.js
を叩かなくてはいけない
そのため、ファイルを変更したら自動でコンパイルしてくれるようにする
gulpやgruntは使わない
まず、ファイルの変更を監視するwatchというパッケージがあるので、それをインストールする
npm install watch
次に、package.jsonのscriptを変更する
"scripts": { "start": "watch 'npm run build' src/", "build": "node index.js" }
これは、src/
下のファイルが変更されたら、npm run build
を実行するということ
そして、buildには、先程も実行していたnode index.js
を記述している
これで、
$ npm start
を実行するだけ
あとは、変更を加えるたびに自動でコンパイルしてくれるようになる
参考サイト