まえがき
私は初心者のAndroid開発者で、この分野で約1.5年の経験があります。 私はかなり大きなプロジェクトを引き受けました。チームには私以外の誰もいません。バックエンドを書くことはできません。 プラットフォームとしてFirebaseを選択することにしました。 私のアプリケーションの詳細には絶え間ない作業が必要であり、バックグラウンドでデータベースからデータを受信するため、すべてのEventListenerをサービスに挿入するだけで満足しました。 iOSバージョンを書くことにした瞬間まで。 スウィフトを学んだ後、私は戦いに突入しました。 幸いなことに、Firebase SDKは非常に優れており、両方のシステムで類似していることが判明したため、すぐに主要部分を記述しました...なぜ機能しないのですか?
問題の本質と問題の説明
iOSは、控えめに言っても、バックグラウンドで実行されているアプリケーションを尊重しません。 システムが強制終了した(そしてくしゃみのために強制終了する)アプリケーションを起動する唯一の方法は、APNS通知を使用することです。 さらに、Android 6以降では、GCMを介して実装されていない場合、永続的な接続は持続せず、最終的に通知は5分から2時間(7.1で)遅れます。 Firebase Cloud MessagingがAPNSとGCMの両方をサポートしているのは良いことです。 このために追加のサーバーが必要になるのは悪いことです。 データベース内の特定の変更について通知が自動的に送信されると便利です。 エンジニアは来年同様のことをすることを約束します...そしてそれは今すぐ動くはずです。
実際、認証とXMPPを備えた本格的なサーバーを実装するために、誰もが欲求/知識/リソースを持っているわけではありません。 したがって、2つの問題があります。プッシュを送信するユーザーの承認と、実際にプッシュを送信することです。 これは私の場合です。 データベース内の新しいデータ(記事など)の出現を追跡し、このトピックにサブスクライブしているすべての人に通知を送信する必要がある場合は、それでも簡単です。
準備する
最初は、すべてがPythonで書かれていましたが、 最近の記事からも同様の状況が起こりました。
Pythonは、デバイスを読み取り用に再度開く際に問題が発生しました-2度目はデータが読み取れなくなりました。 Golangで同じことを理解せず、書き直しただけです。その後、すべてが機能しました。
それでは、どのように機能しますか? Firebase REST APIを使用して関心のあるブランチの変更を監視し、新しい要素を追加する場合は、FCMを通じてプッシュを送信します。 どこで機能しますか? はい、どこでも。 そして、これは主な利点の1つです。 静的IPと適切なホスティングは必要ありません(ただし、これは送信されたプッシュの数に直接依存します)。
しかし、ビジネスに取りかかる前に、2つのことを理解する必要があります。
まず、データベース全体を追跡するには、事前にロードする必要があります。 また、「ヘルプサーバー」が横たわっている(または別のコンピューターに移動している)場合は、すべてを再度ダウンロードし、プッシュを再度送信します。 この問題を解決するために、データベースのルートにnotifブランチを作成しました。ユーザー(またはコンテンツダウンローダー)がユーザーに送信する必要がある通知を追加し、サーバーは送信後にそれらを削除します。 私はこの構造を使用します:
"notif" { "$key" { // push() "from": "2vgajTP5Vd...", // UID "to": "all_users", // , UID "value": "Hello, Habr!", // , "type": "message"// , } }
第二に、どこに送るかを知る必要があります。 そのため、デバイスが登録トークンをFCMに書き込む別の「トークン」ブランチを作成しました。 クライアントデバイスへの実装の微妙さは、すでに別の記事のトピックです。 次の形式でこのスレッドに保存します。
"tokens" { "userId": "fcmToken" }
また、他の人に代わってメッセージを送信したり、他の人がメッセージを受信したりできないように、Firebase Database Rulesを追加しました。
{ "rules": { /// "notif": { ".read": "false", "$key": { ".write": "auth != null && newData.child('from').val() === auth.uid" } }, "tokens": { ".read": "false", "$key": { ".write": "auth != null && $key == auth.uid" } } } }
キーとライブラリも必要です。
- Firebase Database Secret-読書禁止のデータを読むために、オホーホ(ここで笑顔があります)。 Firebase Consoleの設定で取得できます。
- FCM APIキー-プッシュを送信します。 次のタブでアクセスできます。
- FireGo-データベース追跡用
- FCM-自分で書いてみませんか?
実装(最後に!)
例を簡単にするために、トークンのキャッシュ、廃止されたトークンの削除、およびAndroid Publisher APIを介したアプリケーション購入の検証を削除しましたが、これらのいずれかに興味がある場合は、コメントを書いてコード全体を共有してください。
だから、プログラムの主要部分:
package main import ( "github.com/zabawaba99/firego" "github.com/edganiukov/fcm" "fmt" "log" ) const ( //TODO FDBSecret = "P3cUiIQytto**************NzQM5TrzERjEDO" FCMAPIKey = "AIzaSyDXjRG**************8oOCMrPj18JVD8" DAY_IN_SEC = 86400 // TOKENS = "tokens" NOTIFICATIONS = "notif" ) var ( FBDB = firego.New("https://kidgl.firebaseio.com", nil) // FCM, _ = fcm.NewClient(FCMAPIKey) // ) func main() { FBDB.Auth(FDBSecret) FBDB.Child(NOTIFICATIONS).ChildAdded(gotPush) // , , for { var res string fmt.Scanln(&res) if res == "exit" { return } else { println(`Type "exit" to stop service`) } } }
ChildAdded関数は、データベースが変更された場合に呼び出す関数を受け入れます。 これはすべて、いわゆるゴルーチンとは別のスレッド(またはスレッドではないかもしれません)で行われます。 したがって、プログラムが終了しないように、私は永遠のループを使用します(ただし、何らかの例外で終了します。再起動は、stderrを入力として使用するbashスクリプトで行われます)。
これですべてが明らかになりました。現在はgotPush関数です。
func gotPush(snapshot firego.DataSnapshot, previousChildKey string) { // , FBDB.Child(NOTIFICATIONS).Child(snapshot.Key).Remove() // data := snapshot.Value.(map[string]string{}) from := data["from"] to := data["to"] typ := data["type"] // , , var token string FBDB.Child(TOKENS).Child(to).Value(&token) msg := &fcm.Message{ Token: token, // Data - , Data: &fcm.Data{ "from": from, "type": typ, "value": data["value"], }, CollapseKey: typ + from + to, // Priority: "high", ContentAvailable: true, TimeToLive: DAY_IN_SEC, // } response, err := FCM.Send(msg) if (err!=nil) { log.Println(err) } println(": ", response.Success) println(": ", response.Failure) if response.Results[0].Unregistered() { // TODO: , } }
さて、それですべて、
読んでくれてありがとう!