質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

解決済

1回答

20315閲覧

FCM Push通知が届かない場合がある

ludolf

総合スコア39

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

0グッド

0クリップ

投稿2020/09/09 11:35

いつもお世話になっております。

早速ですが表題の件でご教授頂きたく本件を投稿しました。

基本情報

先ずは基本情報から説明させていただくと、
Push通知を送信するのに使用しているツールは、FirebseCloudMessenging(以降FCMとする)を使用して送信しております。
FirebaseAdminSDKで送信していてNode.jsを通して送信しております。
その他諸々の設定はすべて出来ており、通知もIOS・Android届いている状況です。

ただ表題に書いてある通り、届かない場合があります。
といってもこういうパターンで送ると届かないとかではなく、急に届かなくなったりと不規則な動きをしています。
送れなかったパターンを再度1~2時間後に送信してみると、治ってたりしているので具体的にどういった送信が届いていないのか説明ができません。

やったこと

Push通知が送れない場合こういったケースがあると紹介しているサイトをたくさん見てみましたが、該当するケースがありませんでした。

自分が調べて届かないケースをまとめてみました。(文字数が越えてしまったので代表的なやつを記入しています)
「Dozeモードだと届かない」
「メッセージには有効期限がある」
「省エネモードだと届かない」
「重要度設定を低めにすると届かない」
「チャンネルが登録されていない」
「トピックが正しく設定されていない」
「最大文字数(4KB)を超えている」
「バックグランド・フォアグランドでは設定が必要」
「指定バックオフで送れなかった場合は再度送信するロジックを導入する」

チャンネルやトピックスに関しては普通に通知を受け取れているので、関係ないかと思っています。

以前フォアグランドは届くがバックグランドでは通知が届かない問題があり、質問している記事があるので載せておきます。
URL:https://teratail.com/questions/261240

上のURLで書いてあるようにデータペイロードのみで送信しています。

以上が基本情報です。

プログラム

Nodeでやっている処理は以下になります。

Node.js

1 2…省略 3 4/*************************************** 5* 個別トークン配信 6**************************************/ 7async function send_push_notice_single(parameter_data, set_fcm, DB_obj,) { 8 9 10 return new Promise((resolve,reject) => { 11 var topic = parameter_data['topic']; 12 var condition = "'Android' in topics || 'info_topic' in topics"; 13 14 //デフォルト値 15 16 user_id = "0"; 17 18 } 19 20 //送られてきたURLをデコードする 21 let decodeURI = decodeURIComponent(parameter_data['url']); 22 decodeURI = decodeURIComponent(decodeURI); 23 decodeURI = decodeURIComponent(decodeURI); 24 25 let decodeImage = decodeURIComponent(parameter_data['image']); 26 decodeImage = decodeURIComponent(decodeImage); 27 decodeImage = decodeURIComponent(decodeImage); 28 29 //トークン送信 30 var message = { 31 data : { 32 title : parameter_data['title'], 33 message : parameter_data['message'], 34 url : decodeURI, 35 uid : user_id, 36 type : parameter_data['type'], 37 image : decodeImage, 38 android_channel_id: parameter_data['channel'] 39 }, 40 token: parameter_data['token'] 41 }; 42 43 console.log(message); 44 45 var response_data = ''; 46 47 // Send a message to devices subscribed to the provided topic. 48 set_fcm.messaging().send(message) 49 .then((response) => { 50 // Response is a message ID string. 51 console.log('Successfully sent message:', response); 52 53 response_data = { 54 code : 'Successfully sent message', 55 log : { 56 send_type : 'single', 57 error_info : response, 58 push_data : message 59 } 60 }; 61 resolve(response_data); 62 }) 63 .catch((error) => { 64 console.log('Error sending message:', error); 65 66 response_data = { 67 code : error['errorInfo']['code'], 68 log : { 69 send_type : 'single', 70 error_info : error['errorInfo'], 71 push_data : message 72 } 73 }; 74 resolve(response_data); 75 76 // resolve(true); 77 78 }); 79 });

Android側

java

1public class MyFirebaseMessagingService extends FirebaseMessagingService { 2 3 private static final String TAG = "MyFirebaseMsgService"; 4 5 @Override 6 public void onNewToken(String token) { 7 …省略 8 } 9 10 @Override 11 public void onMessageReceived(RemoteMessage remoteMessage) { 12 13 if (remoteMessage != null) { 14 15 long id = 0; 16 String type = remoteMessage.getData().get("type"); 17 String title = remoteMessage.getData().get("title"); 18 String message = remoteMessage.getData().get("message"); 19 String url; 20 String uid; 21 String image; 22 23 24 if(remoteMessage.getData().get("type") != null){ 25 type = remoteMessage.getData().get("type"); 26 }else{ 27 Log.d(TAG,"type何も入っていません"); 28 type = ""; 29 } 30 31 //会員機能なのか非会員機能なのかを分ける処理 32 if(remoteMessage.getData().get("uid") != null && remoteMessage.getData().get("uid") != ""){ 33 uid = remoteMessage.getData().get("uid"); 34 }else{ 35 Log.d(TAG,"uid何も入っていません"); 36 uid = "0"; 37 } 38 39 //飛ばしたいリンクがあれば登録する処理 40 if(remoteMessage.getData().get("url") != null){ 41 url = remoteMessage.getData().get("url"); 42 }else{ 43 Log.d(TAG,"url何も入っていません"); 44 url = ""; 45 } 46 47 //画像があれば登録する処理 48 if(remoteMessage.getData().get("image") != null){ 49 image = remoteMessage.getData().get("image"); 50 }else{ 51 Log.d(TAG,"image何も入っていません"); 52 image = ""; 53 } 54 55 //チャンネル 56 String channel = remoteMessage.getData().get("android_channel_id"); 57 58 //タイトルのないPush通知など存在しないはずだに 59 if (title != null || message != null) { 60 Log.d(TAG,"プッシュ通知が届きました。"); 61 // 通知メッセージを処理 62 Log.d(TAG, "これですね →" + remoteMessage.getData().toString()); 63 64 if(!type.equals("oubo") && !type.equals("scout")) { 65 66 //プッシュ通知をデータベースに入れ込み 67 PushList pushList = new PushList(); 68 pushList.type = type; 69 pushList.title = title; 70 pushList.message = message; 71 pushList.receive_time = dateTime; 72 pushList.url = url; 73 pushList.batch = 1; 74 pushList.uid = Integer.parseInt(uid); 75 pushList.image = image; 76 77 //ユーザーデータベースを宣言 78 UserDatabase db = Room.databaseBuilder(getApplication().getApplicationContext(), UserDatabase.class, myClass.TABLE_NAME).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).addMigrations(MIGRATION_4_5).build(); 79 80 //追加したレコードIDを戻り値として設定 81 id = db.pushListDao().insert(pushList); 82 } 83 84 } 85 86 //Push通知表示用メソッド呼び出し 87 //補足:URLクラスを呼び出すとtry/chatchしないといかんのだ 88 try { 89 sendNotification(id,title,message,channel,url,image); 90 } catch (IOException e) { 91 e.printStackTrace(); 92 } 93 } 94 95   /** 96 * フォアグランドでもPush通知を受け取る 97 */ 98 private void sendNotification(Long id,String title, String body, String channel, String url, String image) throws IOException { 99 100 //タップされたときの遷移画面設定 101 Intent intent = new Intent(this, MainActivity.class); 102 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); 103 intent.putExtra("PushReceive",url); 104 intent.putExtra("PushRecodeId",id.intValue()); 105 106 //senderIDを指定するために乱数を使用 107 randamId = UUID.randomUUID().hashCode(); 108 109 //一度のみ 110 PendingIntent pendingIntent = PendingIntent.getActivity(this, randamId, intent,PendingIntent.FLAG_UPDATE_CURRENT); 111 112 Bitmap bitmap = null; 113 114 Log.d(TAG,"画像生成失敗"); 115 //デフォルトスタイル適用 116 BigTextPushStyle(title,body,channel,bitmap,pendingIntent); 117 118 119 } 120 121 //大きい画像を表示させるときのスタイル 122 private void LaurgePicturePushStyle(String title, String body, String channel, Bitmap bitmap, PendingIntent pendingIntent){ 123 124 Notification bigPictureStyle = new NotificationCompat.Builder(this,channel) 125 .setSmallIcon(R.drawable.pushicon) 126 .setContentTitle(title) 127 .setContentText(body) 128 .setLargeIcon(bitmap) 129 .setAutoCancel(true) 130 .setContentIntent(pendingIntent) 131 .setPriority(NotificationCompat.PRIORITY_HIGH) 132 .setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bitmap) 133 ).build(); 134 135 //マネージャーさん呼び出し 136 NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 137 //Push通知を表示 138 notificationManager.notify(randamId, bigPictureStyle); 139 } 140 141 //長文用 142 private void BigTextPushStyle(String title, String body, String channel, Bitmap bitmap, PendingIntent pendingIntent){ 143 144 Notification bigTextStyle = new NotificationCompat.Builder(this,channel) 145 .setSmallIcon(R.drawable.pushicon) 146 .setContentTitle(title) 147 .setContentText(body) 148 .setLargeIcon(bitmap) 149 .setAutoCancel(true) 150 .setContentIntent(pendingIntent) 151 .setPriority(NotificationCompat.PRIORITY_HIGH) 152 .setStyle(new NotificationCompat.BigTextStyle() 153 ).build(); 154 155 //マネージャーさん呼び出し 156 NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 157 //Push通知を表示 158 notificationManager.notify(randamId, bigTextStyle); 159 160 }

(とりあえずAndroidで表示する文を投稿しましたが、IOSも必要であれば載せます。)

以上のプログラムで送信しています。

PHP側でPush通知を送信できる管理画面を作成していて実際に送信した内容をconsole.logで表示してみていますが、エラーはめったに表示されません。
エラーがあったとしてもトークンがありませんというエラー以外でていません。

受け取れないパターンについて

受け取れないパターンですが、特に多かったパターンがAndroidアプリを再起動?(タスク切り)してPush通知を送信すると届かないことが多いです。(IOSは特になし)
5通Push通知を30秒ほど間隔をあけて送信し、2通目までは連続で送信して3通目からはアプリを再起動して送信したのですが、3~5通目が届かないことが多いです。
たまにまとめて3通同じ時間に来たり、3通目からまったく通知が届かない場合もあります。
(1~2時間後に同じことをしたら届くこともありました)

長文で理解しずらいかと思いますが、お力添え頂ければ幸いです。
どんな些細な回答でも嬉しいのでぜひ回答ください泣

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

自己解決しました、、
Push通知が届かないのはFCMの仕様みたいですね、
めちゃくちゃ調査大変でした泣
下記に自分がまとめたので自分と似た境遇の方参考にしてください。
(IOSの送れない場合も書きたいのはやまやまなんですが、文字が上限を超えました、、)

●一度送信されたPush通知が来ない、または来ない場合がある時

折りたたみできるメッセージのスロットル処理という処理があり、同じメッセージがアプリに頻繁に繰り返し送信されている場合、ユーザーの電池の消耗を抑えるためにメッセージを遅延(スロットル)させます。このスロットル処理は、ユーザーの電池への影響を抑えるために厳重に行われています。

個別送信(token送信)に関しては、同じアプリから通知が来ても折り畳みがされません。また個別通信ではすべてのメッセージを配信する必要があります。
逆に折り畳できるメッセージはサーバーとのデータの同期を目的とした通知がメインな為、最新の通知を表示することに意味があります。
※Push通知は送信した順番に必ずくるとは限らない。
公式サイト:https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja#collapsible_and_non-collapsible_messages

●スマホのロックを解除・またはスマホを数十分触らずに開いた時にPush通知が来る

AndroidではDozeモードという一定時間スマホを使用しなかった場合にバッテリー節約のために配信を遅延させて送信することがあります。折り畳みのできる通知はこれに該当します。(折り畳みできる通知に関して、Dozeモードに入っている状態で10通Push通知を送信すると一通のみ通知されました)
優先度の高いPush通知を送信し、ユーザーがその通知に対して操作がなかった場合、自動的に優先度を下げることがあります。またAndroid9.0以上に関してはアプリスタンバイパケット制限が適用され重要度の高い通知には数が制限されます。優先度の高い通知の上限は一日に10件で上限に到達した後のメッセージは、すべて通常の優先度のメッセージとして処理される。(個別送信でDozeモードになっている機種に関して、4通応募返信を送信したところ一気にすべての通知が通知されました。)
またユーザーがアプリの設定画面でバックグラウンド制限を有効にした場合、2019年1月以降のデバイスに関しては、バックグランドでPush通知を受信できません。
公式サイト;https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja#setting-the-priority-of-a-message

●Push通知を送信してもすぐに届かない

FCMでは通常、メッセージが送信されるとすぐに配信されます。しかし、常にそのように動作するとは限りません。プラットフォームが Android の場合、デバイスが電源オフまたはオフライン、あるいは利用できない状態になっていることがあります。また、アプリによる過剰なリソースの消費やバッテリー寿命への悪影響を防ぐために FCM が意図的にメッセージの配信を遅らせることもあります。
こうした場合、FCM はメッセージを保存しておき、配信に適した状態になり次第メッセージを配信します。ほとんどの場合は、これで問題ありませんが、アプリによってはメッセージ配信の遅れが許されないことがあります。たとえば、通話着信またはビデオチャット通知のメッセージは、呼び出しが終わってから配信されたのでは意味がありません。また、イベントへの招待メッセージは、イベント終了後に届いても役に立ちません。
公式サイト:https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja#ttl

開発管理者向け

●データペイロード送信でonMessageReceivedに入ってこない場合どういった原因があるか

通知を受け取ってから20秒以内にサービスによって処理されます(AndroidMarshmallowでは10秒)。
時間ウィンドウは、onMessageReceived を呼び出す前に発生した OS の遅延に応じて短くなる場合があります。この時間が経過すると、Android O のバックグラウンド実行制限などの OS の各種動作によって、作業完了が妨げられる可能性があります。
公式サイト:https://firebase.google.com/docs/cloud-messaging/android/receive?hl=ja#handling_messages

●重要度の高い通知を送りたいとき・強制的に受け取らせたい・Dozeモード中でも受信したい

ダイレクトブートモードを使用することでダイレクトに受信することができる。しかしダイレクト ブートに対応したメッセージの可視性を考慮することが特に重要で、デバイスにアクセスできるユーザーであれば、ユーザーの認証情報を入力しなくてもこれらのメッセージを閲覧できてしまします。(LINEではNotificationペイロードとDataペイロードの両方を送信しており、バックグラウンドにいる状態では本文を変更して「〇〇から通知があります」といった個人情報を考慮した作りにしている。)
実装するにはデバイス暗号化ストレージに保存先を変更することで実現できる。何も設定を行っていない場合、認証情報暗号化ストレージが使用されており、ユーザーがデバイスのロックを解除した後にだけ使用できる保存領域になります。デバイス暗号ストレージに関してはダイレクトブートモード中・ユーザーがデバイスのロックを解除した後の両方で使用されるため送信したと同時に通知を受け取れる仕組みになっています。

常にデバイス暗号ストレージを保存場所で使用することはしないでください。ユーザーがデバイスのロックを解除している時としていない時を判定して保存先を変更してください。
UserManager.isUserUnlocked()を使用する。
公式サイト:https://firebase.google.com/docs/cloud-messaging/android/receive?hl=ja#receive_fcm_messages_in_direct_boot_mode

●Dozeモードの詳しい情報

Dozeモードには7つの状態があります。

イメージ説明

アクティブ状態から画面が消灯又は充電ケーブルが外されたことを契機に、各ステートメントに移行します。
Dozeモードになる時間は消灯・充電ケーブルから外して、一時間経過するとDoseモードに移行します。
それ以降になると×2されていき、2時間・4時間・6時間とDozeモードが変化していきます。(上限は6時間)
またDozeモード中に一時的にIDLE状態にするようになっていますので、Doze状態でもPush通知の受け取りは最低でもこのIDLE状態にPush通知を受け取るような仕組みになっているようです。

AndroidStudioでは検証できるようにADBコマンドを使用してスマホの状態を強制的に変更することが出来ます。
参考URL:https://qiita.com/komitake/items/4476e7e4d1b5ed3a8b97

●通知ペイロードでの送信で画像のサイズは1MBまでと定められている

Notificationを使用してPush通知を送信した際に、画像が届かない不具合があったので調査した所1MBまでと定められている。
公式サイト:https://firebase.google.com/docs/cloud-messaging/android/send-image?hl=ja

投稿2020/09/14 11:05

ludolf

総合スコア39

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問