サーバーからスマートフォンに通知を送信するいくつかの方法

このチュートリアルでは、サーバーからスマートフォン(またはスマートフォンではありません)に通知を送信する方法、これに必要なツールについて順を追って説明します。 これらの方法は普遍的であり、あらゆるプログラミング言語に適しています。 ライブラリを使用せずに、Google APIを直接使用します。 Android、iOSを搭載したスマートフォン、Push APIをサポートするブラウザー(今日はChrome、Firefox、およびその派生物)に送信できます。



一般的に、これは長い間ホームサーバーからスマートフォンに通知を送信したいが、どこから始めればよいかわからなかったすべての人に捧げられています。



少し歴史。 最初に(Android 2.2のバージョンから)、Googleは配信にC2DMシステム(Androidクラウドからデバイスメッセージング)を使用しました。2012年6月から、 GCM (Googleクラウドメッセージング)の使用を提案し始めました。



現在、ユニバーサルFirebaseプラットフォームが使用されており、通知の配信に加えて、他の多くの機能を備えています。 Firebaseも進化しており、第1世代のプロトコルはすでに廃止されていると見なされているため、メッセージ配信には第2世代のプロトコルを使用することをお勧めします。



技術的には、通知はサーバーからスマートフォンに直接ではなく、必要に応じて最大4週間(構成可能)保管される特定の中間サーバーに送信され、可能であれば受信者に送信されます。 すなわち スマートフォンがオフラインの場合、サーバーは待機しています。 機会が発生するとすぐに-送信します。



1. Firebaseに登録する



Firebaseに登録するには Googleアカウント必要です。







[コンソールに移動]をクリックします。







次に「プロジェクトを追加」。







プロジェクトの名前を入力します。 8〜16文字の範囲でお勧めします。

国を選択してください。 [プロジェクトの作成]をクリックします。



2. Firebaseを構成する







「通知」ブロックまでスクロールし、「開始」をクリックします。



通知を送信するアプリケーションを選択するよう求められます。







Andriodアプリケーションの手順:







ステップ1-Andriodのプロジェクトの名前を入力します。

[アプリケーションの登録]をクリックします。







ステップ2-[google-services.comのダウンロード]をクリックします。

ダウンロードした構成ファイルをbuild.gradleファイル(アプリケーションに個人的なファイル)の隣のプロジェクトに追加します。

[続行]をクリックします。







ステップ3-プロジェクトに依存関係を追加します。

/build.gradleの行へ

クラスパス 'com.google.gms:google-services:3.1.0'

/<app-module>/build.gradle行に移動します

プラグインの適用: 'com.google.gms.google-services'

以上で、「完了」をクリックします。



アプリケーションを設定したら、テストメッセージを送信して、接続が機能するかどうかをすぐにテストできます(いいえ、できません。クライアントIDがまだないので、送信先を指定してください)。



3. Androidアプリケーションをセットアップして通知を受信します。



重要な注意:MIUIなどの一部のシェルは、アプリケーションが実行されていないか、バックグラウンドでハングしていない場合、通知をブロックできます。 これは、バッテリー電力を節約するために行われたと言われています。



大まかに言えば、2種類の通知を送信できます。

-リクエストに応じて通知、

-ペイロード通知。

これらには、アプリケーションと対話するさまざまな方法があります。



通知通知は、アプリケーションが最小化されている場合にのみ、通知領域に通知を表示します。 ユーザーがテープを作成すると、事前に選択したアプリケーションアクティビティが開き(送信時)、追加のパラメーターがバンドルに転送されます。



ペイロードを含む通知では、アプリケーションに、制御が転送されるサービスのペアが必要ですが、その期間は10秒以内です。



以下は、クライアントIDの生成を担当するサービスの例です。

package ru.pyur.loga; import android.util.Log; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceIdService; public class TestFirebaseInstanceIdService extends FirebaseInstanceIdService { public static final String TAG = "TestFbseInstIdSvc"; @Override public void onTokenRefresh() { String refreshedToken = FirebaseInstanceId.getInstance().getToken(); Log.d(TAG, "Refreshed token: " + refreshedToken); //~sendRegistrationToServer(refreshedToken); } }
      
      







そして、メッセージを受信するサービスのコードの例。 アプリケーションが実行されているか、バックグラウンドでハングしている必要があります。そうでない場合、メッセージの受信は保証されません。 MIUIなどの一部のシェルは、経済性のために、バックグラウンドサービスの特権を含むすべてを連続してカットします。



 package ru.pyur.loga; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import static ru.pyur.loga.AcMain.context; public class TestFirebaseMessagingService extends FirebaseMessagingService { public static final String TAG = "TestFbseMsgngSvc"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { Log.d(TAG, "From: " + remoteMessage.getFrom()); if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); String val1 = remoteMessage.getData().get("val1"); String val2 = remoteMessage.getData().get("val2"); String val3 = remoteMessage.getData().get("val3"); int color = (1<<16)|(1<<8)|(0); ShowNotification(val1, val2, color); } if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); } } @Override public void onDeletedMessages() { // In some situations, FCM may not deliver a message. This occurs when there are too many messages (>100) pending for your app on a particular device // at the time it connects or if the device hasn't connected to FCM in more than one month. In these cases, you may receive a callback // to FirebaseMessagingService.onDeletedMessages() When the app instance receives this callback, it should perform a full sync with your app server. // If you haven't sent a message to the app on that device within the last 4 weeks, FCM won't call onDeletedMessages(). } void ShowNotification(String title, String text, int color) { NotificationCompat.Builder mNotify = new NotificationCompat.Builder(context, ""); mNotify.setLights(color, 100, 200); mNotify.setSmallIcon(R.drawable.service_icon); mNotify.setContentTitle(title); mNotify.setContentText(text); mNotify.setDefaults(Notification.DEFAULT_SOUND); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); int mId = 1001; try { mNotificationManager.notify(mId, mNotify.build()); } catch (Exception e) { e.printStackTrace(); } } }
      
      







マニフェストにサービスを登録することを忘れないでください。



 <service android:name=".TestFirebaseMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service> <service android:name=".TestFirebaseInstanceIdService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> </intent-filter> </service>
      
      







クライアントIDはデバイスで生成されますが、このIDをサーバーに配信する方法はユーザーが選択します。



これで、コンソールからテストメッセージを送信してテストできます。











4.サーバーから通知を送信する



Firebaseサーバーと通信するにはいくつかの方法があります。 HTTPプロトコルを使用して交換する2つの方法を検討します。



第一世代プロトコル-レガシーHTTP







キーが必要になります。 ナットをクリックして、「プロジェクト設定」を選択します。







クラウドメッセージングタブ

「古いサーバーキー」をコピーします。



 <?php // ------------------------ test fcm send. legacy ------------------------ // $socket = @fsockopen('ssl://fcm.googleapis.com', 443, $errno, $errstr, 10); if (!$socket) die('error: remote host is unreachable.'); // ----    ---- // $payload = '{ "to" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "notification" : { "title" : "  ", "body" : "(Legacy API) !", "sound": "default" } }'; //  // ----    ---- // $payload = '{ "to" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "data":{ "val1" : "  ", "val2" : "(Legacy API) !", "val3" : "-  " } }'; $send = ''; $send .= 'POST /fcm/send HTTP/1.1'."\r\n"; $send .= 'Host: fcm.googleapis.com'."\r\n"; $send .= 'Connection: close'."\r\n"; $send .= 'Content-Type: application/json'."\r\n"; $send .= 'Authorization: key=AIzaSy***************************IPSnjk'."\r\n"; $send .= 'Content-Length: '.strlen($payload)."\r\n"; $send .= "\r\n"; $send .=$payload; $result = fwrite($socket, $send); $receive = ''; while (!feof($socket)) $receive .= fread($socket, 8192); fclose($socket); echo '<pre>'.$receive.'</pre>'; ?>
      
      





ここで、「to」フィールドでは、クライアントIDを置き換える必要があります。 httpヘッダーで、「Authorization:key = "substitution" Deprecated server key。 "」



第2世代プロトコル-(最新)HTTP v1。



(ソース: developers.google.com/identity/protocols/OAuth2ServiceAccount

プロトコルの2番目のバージョンがV1と呼ばれる理由を聞かないでください。明らかに最初のバージョンはベータ版と見なされ、ゼロ番号でした。

詳細には触れませんでしたが、理解しているように、このプロトコルは通知を送信するだけでなく、より普遍的であり、より多くの機能を備えています。



 <?php // ------------------------ test fcm send. modern ------------------------ // // --  1.  JWT -- // $JWT_header = base64_encode('{"alg":"RS256","typ":"JWT"}'); $issue_time = time(); $JWT_claim_set = base64_encode( '{"iss":"firebase-adminsdk-mvxyi@<your-project>.iam.gserviceaccount.com",'. '"scope":"https://www.googleapis.com/auth/firebase.messaging",'. '"aud":"https://www.googleapis.com/oauth2/v4/token",'. '"exp":'.($issue_time + 3600).','. '"iat":'.$issue_time.'}'); // .  $private_key = ' -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwR1biSUCv4J4W **************************************************************** **************************************************************** ... **************************************************************** teTJImCT6sg7go7toh2ODfaPmeI0nA/LwSjzWs0b8gdIYPT5fAsvfQiND0vu/M3V 7C/z/SmIKeIcfOYrcbWQwTs= -----END PRIVATE KEY----- '; $data = $JWT_header.'.'.$JWT_claim_set; $binary_signature = ''; openssl_sign($data, $binary_signature, $private_key, 'SHA256'); $JWT_signature = base64_encode($binary_signature); $JWT = $JWT_header.'.'.$JWT_claim_set.'.'.$JWT_signature; // --  2.     -- // $socket = @fsockopen('ssl://www.googleapis.com', 443, $errno, $errstr, 10); if (!$socket) die('error: remote host is unreachable.'); $payload = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion='.rawurlencode($JWT); $send = ''; $send .= 'POST /oauth2/v4/token HTTP/1.1'."\r\n"; $send .= 'Host: www.googleapis.com'."\r\n"; $send .= 'Connection: close'."\r\n"; $send .= 'Content-Type: application/x-www-form-urlencoded'."\r\n"; $send .= 'Content-Length: '.strlen($payload)."\r\n"; $send .= "\r\n"; $send .= $payload; $result = fwrite($socket, $send); $receive = ''; while (!feof($socket)) $receive .= fread($socket, 8192); fclose($socket); echo '<pre>'.$receive.'</pre>'; // -- parse answer JSON (lame) -- // $line = explode("\r\n", $receive); if ($line[0] != 'HTTP/1.1 200 OK') die($line[0]); $pos = FALSE; if (($pos = strpos($receive, "\r\n\r\n", 0)) !== FALSE ) { if (($pos = strpos($receive, "{", $pos+4)) !== FALSE ) { if (($pose = strpos($receive, "}", $pos+1)) !== FALSE ) { $post = substr($receive, $pos, ($pose - $pos+1) ); $aw = json_decode($post, TRUE); $access_token = $aw['access_token']; } else die('} not found.'); } else die('{ not found.'); } else die('\r\n\r\n not found.'); // --  3.    Firebase  -- // $socket = @fsockopen('ssl://fcm.googleapis.com', 443, $errno, $errstr, 10); if (!$socket) die('error: remote host is unreachable.'); $payload = '{ "message":{ "token" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "notification" : { "title" : " ", "body" : "(Modern API)     Firebase!" } } }'; //  $payload = '{ "message": { "token" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "data":{ "val1" : " ", "val2" : "(Modern API)     Firebase!", "val3" : " " } } }'; $send = ''; $send .= 'POST /v1/projects/pyur-test-id/messages:send HTTP/1.1'."\r\n"; $send .= 'Host: fcm.googleapis.com'."\r\n"; $send .= 'Connection: close'."\r\n"; $send .= 'Content-Type: application/json'."\r\n"; $send .= 'Authorization: Bearer '.$access_token."\r\n"; $send .= 'Content-Length: '.strlen($payload)."\r\n"; $send .= "\r\n"; $send .=$payload; $result = fwrite($socket, $send); $receive = ''; while (!feof($socket)) $receive .= fread($socket, 8192); fclose($socket); echo '<pre>'.$receive.'</pre>'; ?>
      
      











console.firebase.google.com/project/poject-id/settings/serviceaccounts/adminsdkで 、「Firebaseサービスアカウント」をコピーし、「iss」フィールドの変数「$ JWT_claim_set」に置き換える必要があります。



「秘密鍵の作成」をクリックします







キーを作成して保存し、誰にも見せません。 ダウンロードしたファイルには「Private Key」が含まれ、変数「$ private_key」に置き換えます。



ヒント:手順1および2で取得したトークンは、ファイルやデータベースなどのローカルの一時ストレージにキャッシュできます。 そして、時間が経過した後にのみ(デフォルトでは1時間)、許可サーバーに次のトークンを要求します。







重要! Modern Http APIを使用する前に、 console.developers.google.com / apis / library / fcm.googleapis.com /? project = your-projectで使用を明示的に有効にする必要があります



ボーナス、通知の追加オプション:



soundは、「デフォルト」またはアプリケーション内のリソースの名前です。 「/ res / raw /」に配置する必要があります。 MP3、AAC、またはその他の適切な形式をフォーマットします。

icon-通知アイコンを変更します。 「描画可能」アプリケーションに保存する必要があります。 存在しない場合、FCMはアプリケーションアイコン(アプリケーションマニフェストで「ランチャーアイコン」として示される)を使用します。

tag-同様の通知をグループ化するために使用する必要があります。 新しい通知は、同じタグを持つ既存の通知の上に表示されます。

color-アイコンの色。「#rrggbb」として設定(MIUIでは機能しませんでした)

click_action-ユーザー通知をクリックしたときにトリガーされるアクティビティ。



おわりに



将来、APIはおそらく変更されるか、廃止されると宣言されるでしょう。 したがって、今日はHTTP v1プロトコルですぐに行う価値があると思います。



VKontakteからの新しいメッセージに加えて、コメントで通知を使用する元の方法を読むことは興味深いでしょう。 たとえば、arduinoファンの監視を設定しており、それらが停止した場合、通知が送信されます。



はい、Zabbixなどがあることは承知していますが、この記事のトピックはホームサーバーやその他のスマートホームです。 エンタープライズクラスのシステムは、アマチュアの工芸品ではやり過ぎだと思います。



All Articles