Как обрабатывать уведомления, когда приложение в фоновом режиме в Firebase
Вот мой манифест
<service android:name=".fcm.PshycoFirebaseMessagingServices">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".fcm.PshycoFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
Когда приложение работает в фоновом режиме и приходит уведомление, приходит уведомление по умолчанию, и мой код onMessageReceived
,
Вот мой onMessageReceived
код. Это вызывается, если мое приложение работает на переднем плане, а не когда приложение в фоновом режиме. Как запустить этот код, когда приложение тоже в фоновом режиме?
// [START receive_message]
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO(developer): Handle FCM messages here.
// If the application is in the foreground handle both data and notification messages here.
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
data = remoteMessage.getData();
String title = remoteMessage.getNotification().getTitle();
String message = remoteMessage.getNotification().getBody();
String imageUrl = (String) data.get("image");
String action = (String) data.get("action");
Log.i(TAG, "onMessageReceived: title : "+title);
Log.i(TAG, "onMessageReceived: message : "+message);
Log.i(TAG, "onMessageReceived: imageUrl : "+imageUrl);
Log.i(TAG, "onMessageReceived: action : "+action);
if (imageUrl == null) {
sendNotification(title,message,action);
} else {
new BigPictureNotification(this,title,message,imageUrl,action);
}
}
// [END receive_message]
32 ответа
1. Почему это происходит?
В FCM (Firebase Cloud Messaging) есть два типа сообщений:
- Сообщения на дисплее: эти сообщения вызывают
onMessageReceived()
обратный вызов только тогда, когда ваше приложение находится на переднем плане - Сообщения данных: эти сообщения вызывают
onMessageReceived()
обратный вызов, даже если ваше приложение находится на переднем плане / фоне / убито
Команда Firebase не разработала пользовательский интерфейс для отправки data-messages
на ваши устройства, пока.
2. Как?
Чтобы достичь этого, вы должны выполнить POST
запрос на следующий URL:
Заголовки
- Ключ:
Content-Type
Значение:application/json
- Ключ:
Authorization
Значение:key=<your-server-key>
Тело с использованием тем
{
"to": "/topics/my_topic",
"data": {
"my_custom_key": "my_custom_value",
"my_custom_key2": true
}
}
Или, если вы хотите отправить его на определенные устройства
{
"data": {
"my_custom_key": "my_custom_value",
"my_custom_key2": true
},
"registration_ids": ["{device-token}","{device2-token}","{device3-token}"]
}
ПРИМЕЧАНИЕ. Убедитесь, что вы не добавляете ключ JSON.
notification
ПРИМЕЧАНИЕ. Чтобы получить ключ сервера, вы можете найти его в консоли Firebase:Your project -> settings -> Project settings -> Cloud messaging -> Server Key
3. Как обрабатывать push-уведомления?
Вот как вы обрабатываете полученное сообщение:
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
String myCustomKey = data.get("my_custom_key");
// Manage data
}
Чтобы сделать библиотеку firebase для вызова вашего onMessageReceived() в следующих случаях
- Приложение на переднем плане
- Приложение в фоновом режиме
- Приложение было убито
Вы не должны помещать ключ JSON "извещение" в свой запрос к API Firebase, а вместо этого использовать "данные", см. ниже.
Следующее сообщение не будет вызывать ваш onMessageReceived(), когда ваше приложение находится в фоновом режиме или убито, и вы не можете настроить свое уведомление.
{
"to": "/topics/journal",
"notification": {
"title" : "title",
"text": "data!",
"icon": "ic_notification"
}
}
но вместо этого будет работать
{
"to": "/topics/dev_journal",
"data": {
"text":"text",
"title":"",
"line1":"Journal",
"line2":"刊物"
}
}
Как правило, сообщение отправляется в аргументе RemoteMessage вместе с вашим объектом данных как Map, тогда вы можете управлять уведомлением в onMessageReceived, как здесь во фрагменте
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
//you can get your text message here.
String text= data.get("text");
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
// optional, this is to make beautiful icon
.setLargeIcon(BitmapFactory.decodeResource(
getResources(), R.mipmap.ic_launcher))
.setSmallIcon(smallIcon) //mandatory
.......
/*You can read more on notification here:
https://developer.android.com/training/notify-user/build-notification.html
https://www.youtube.com/watch?v=-iog_fmm6mE
*/
}
Мне кажется, что все ответы неполные, но во всех них есть что-то, что вам нужно для обработки уведомления, содержащего данные, когда ваше приложение находится в фоновом режиме.
Выполните следующие действия, и вы сможете обрабатывать уведомления, когда ваше приложение находится в фоновом режиме.
1. Добавьте фильтр намерений следующим образом:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name=".MainActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
к деятельности, которую вы хотите обработать данные уведомления.
Отправлять уведомления в следующем формате:
{ "notification" : { "click_action" : ".MainActivity", "body" : "new Symulti update !", "title" : "new Symulti update !", "icon" : "ic_notif_symulti" }, "data": { ... }, "to" : "c9Vaa3ReGdk:APA91bH-AuXgg3lDN2WMcBrNhJZoFtYF9" }
Ключ здесь добавить
"click_action" : ".MainActivity"
где.MainActivity - это действие с фильтром намерений, добавленным на шаге 1.
Получить информацию "data" из уведомления в onCreate ".MainActivity":
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //get notification data info Bundle bundle = getIntent().getExtras(); if (bundle != null) { //bundle must contain all info sent in "data" field of the notification } }
И это должно быть все, что вам нужно сделать. Надеюсь, это кому-нибудь поможет:)
В соответствии с документацией по firebase в send downstream с использованием firebase, существует 2 типа полезной нагрузки:
данные
Этот параметр указывает пользовательские пары ключ-значение полезной нагрузки сообщения. Клиентское приложение отвечает за обработку сообщений с данными. Сообщения данных имеют только пользовательские пары ключ-значение.
уведомление
Этот параметр указывает предопределенные видимые пользователем пары ключ-значение полезной нагрузки уведомления. FCM автоматически отображает сообщение для устройств конечного пользователя от имени клиентского приложения. Уведомительные сообщения имеют предопределенный набор видимых пользователю ключей.
Когда вы находитесь на переднем плане, вы можете получить данные внутри FCM с помощью onMessageReceived (), вы можете получить свои данные из полезной нагрузки данных.
data = remoteMessage.getData();
String customData = (String) data.get("customData");
Когда вы находитесь в фоновом режиме, FCM будет отображать уведомления в системном трее на основе информации из полезных данных уведомления. Заголовок, сообщение и значок, используемые для уведомления на панели задач, извлекаются из полезной нагрузки уведомления.
{
"notification": {
"title" : "title",
"body" : "body text",
"icon" : "ic_notification"
}
}
Эта полезная нагрузка используется, когда вы хотите автоматически отображать уведомления на панели задач, когда ваше приложение находится в фоновом режиме. Чтобы получать данные уведомлений, когда ваше приложение находится в фоновом режиме, вы должны добавить click_action внутри уведомления.
Если вы хотите открыть свое приложение и выполнить определенное действие [в то время как в фоновом режиме], установите click_action в полезной нагрузке уведомления и сопоставьте его с фильтром намерений в Activity, которую вы хотите запустить. Например, установите для click_action значение OPEN_ACTIVITY_1, чтобы активировать фильтр намерений, как показано ниже:
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Поместите этот фильтр намерений в свой манифест, внутри тега приложения. Когда вы щелкнете по уведомлению, оно откроет приложение и сразу перейдет к активности, которую вы определили в click_action, в данном случае "OPEN_ACTIVTY_1". И внутри этой деятельности вы можете получить данные:
Bundle b = getIntent().getExtras();
String someData = b.getString("someData");
Я использую FCM для своего приложения для Android и использую обе полезные нагрузки. Вот пример JSON, который я использую:
{
"to": "FCM registration ID",
"notification": {
"title" : "title",
"body" : "body text",
"icon" : "ic_notification"
},
"data": {
"someData" : "This is some data",
"someData2" : "etc"
}
}
Работает с июля 2019 г.
Android compileSdkVersion 28, buildToolsVersion 28.0.3 и обмен сообщениями firebase:19.0.1
После многих часов изучения всех других вопросов и ответов Stackru и опробования бесчисленных устаревших решений этому решению удалось отобразить уведомления в следующих трех сценариях:
- Приложение находится на переднем плане:
уведомление получено методом onMessageReceived в моем классе MyFirebaseMessagingService
- Приложение было убито (оно не работает в фоновом режиме): FCM автоматически отправляет уведомление в панель уведомлений. Когда пользователь касается уведомления, приложение запускается путем вызова действия, в манифесте которого есть android.intent.category.LAUNCHER. Вы можете получить информационную часть уведомления, используя getIntent(). GetExtras() в методе onCreate().
- Приложение работает в фоновом режиме: FCM автоматически отправляет уведомление в панель уведомлений. Когда пользователь касается уведомления, приложение выводится на передний план путем запуска действия, в манифесте которого есть android.intent.category.LAUNCHER. Поскольку в моем приложении в этом действии есть launchMode="singleTop", метод onCreate() не вызывается, поскольку одно действие того же класса уже создано, вместо этого вызывается метод onNewIntent() этого класса, и вы получаете часть данных уведомление там с помощью intent.getExtras().
Шаги: 1- Если вы определяете основное действие вашего приложения следующим образом:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:largeHeap="true"
android:screenOrientation="portrait"
android:launchMode="singleTop">
<intent-filter>
<action android:name=".MainActivity" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
2- добавьте эти строки в метод onCreate() вашего MainActivity.class
Intent i = getIntent();
Bundle extras = i.getExtras();
if (extras != null) {
for (String key : extras.keySet()) {
Object value = extras.get(key);
Log.d(Application.APPTAG, "Extras received at onCreate: Key: " + key + " Value: " + value);
}
String title = extras.getString("title");
String message = extras.getString("body");
if (message!=null && message.length()>0) {
getIntent().removeExtra("body");
showNotificationInADialog(title, message);
}
}
и эти методы к тому же MainActivity.class:
@Override
public void onNewIntent(Intent intent){
//called when a new intent for this class is created.
// The main case is when the app was in background, a notification arrives to the tray, and the user touches the notification
super.onNewIntent(intent);
Log.d(Application.APPTAG, "onNewIntent - starting");
Bundle extras = intent.getExtras();
if (extras != null) {
for (String key : extras.keySet()) {
Object value = extras.get(key);
Log.d(Application.APPTAG, "Extras received at onNewIntent: Key: " + key + " Value: " + value);
}
String title = extras.getString("title");
String message = extras.getString("body");
if (message!=null && message.length()>0) {
getIntent().removeExtra("body");
showNotificationInADialog(title, message);
}
}
}
private void showNotificationInADialog(String title, String message) {
// show a dialog with the provided title and message
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title);
builder.setMessage(message);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
3- создайте класс MyFirebase следующим образом:
package com.yourcompany.app;
import android.content.Intent;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
public MyFirebaseMessagingService() {
super();
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(Application.APPTAG, "myFirebaseMessagingService - onMessageReceived - message: " + remoteMessage);
Intent dialogIntent = new Intent(this, NotificationActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
dialogIntent.putExtra("msg", remoteMessage);
startActivity(dialogIntent);
}
}
4- создайте новый класс NotificationActivity.class следующим образом:
package com.yourcompany.app;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ContextThemeWrapper;
import com.google.firebase.messaging.RemoteMessage;
public class NotificationActivity extends AppCompatActivity {
private Activity context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
Bundle extras = getIntent().getExtras();
Log.d(Application.APPTAG, "NotificationActivity - onCreate - extras: " + extras);
if (extras == null) {
context.finish();
return;
}
RemoteMessage msg = (RemoteMessage) extras.get("msg");
if (msg == null) {
context.finish();
return;
}
RemoteMessage.Notification notification = msg.getNotification();
if (notification == null) {
context.finish();
return;
}
String dialogMessage;
try {
dialogMessage = notification.getBody();
} catch (Exception e){
context.finish();
return;
}
String dialogTitle = notification.getTitle();
if (dialogTitle == null || dialogTitle.length() == 0) {
dialogTitle = "";
}
AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(context, R.style.myDialog));
builder.setTitle(dialogTitle);
builder.setMessage(dialogMessage);
builder.setPositiveButton(getResources().getString(R.string.accept), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
5- Добавьте эти строки в свой манифест приложения внутри тегов
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id"/>
<activity android:name=".NotificationActivity"
android:theme="@style/myDialog"> </activity>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/notification_icon"/>
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/color_accent" />
6- добавьте эти строки в свой метод Application.java onCreate() или в метод MainActivity.class onCreate():
// notifications channel creation
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create channel to show notifications.
String channelId = getResources().getString("default_channel_id");
String channelName = getResources().getString("General announcements");
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(new NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_LOW));
}
Готово.
Теперь, чтобы это работало в трех упомянутых сценариях, вам нужно отправить уведомление из веб-консоли Firebase следующим образом:
В разделе уведомлений: Название уведомления = Заголовок для отображения в диалоговом окне уведомления (необязательно) Текст уведомления = Сообщение для отображения пользователю (обязательно) Затем в разделе Цель: Приложение = ваше приложение Android и в разделе Дополнительные параметры: Канал уведомлений Android = default_channel_id Ключ настраиваемых данных: значение заголовка: (здесь тот же текст, что и в поле заголовка в разделе уведомлений) ключ: значение тела: (здесь тот же текст, что и в поле сообщения в разделе уведомлений) ключ: значение click_action: .MainActivity
Sound= Отключено.
Срок действия истекает =4 недели.
Вы можете отлаживать его в эмуляторе с API 28 с Google Play.
Удачного кодирования!
Согласно документам
Обрабатывать сообщения в фоновом приложении
Когда ваше приложение находится в фоновом режиме, Android направляет уведомления в системный трей. При нажатии на уведомление пользователя открывается панель запуска приложения по умолчанию.
Это включает в себя сообщения, которые содержат как уведомления, так и данные. В этих случаях уведомление доставляется в системный трей устройства, а полезная нагрузка данных доставляется в дополнениях к назначению вашего средства запуска.
Если вы хотите открыть свое приложение и выполнить определенное действие, установите click_action в полезной нагрузке уведомления и сопоставьте его с фильтром намерений в Activity, которую вы хотите запустить. Например, установите для click_action значение OPEN_ACTIVITY_1, чтобы активировать фильтр намерений, как показано ниже:
<intent-filter> <action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Редактировать:
На основе этой темы:
Вы не можете установить полезную нагрузку click_action с помощью Firebase Console. Вы можете попробовать протестировать с помощью команды curl или пользовательского http-сервера.
curl --header "Authorization: key=<YOUR_KEY_GOES_HERE>"
--header Content-Type:"application/json" https://fcm.googleapis.com/fcm/send
-d "{\"to\":\"/topics/news\",\"notification\":
{\"title\": \"Click Action Message\",\"text\": \"Sample message\",
\"click_action\":\"OPEN_ACTIVITY_1\"}}"
По документам: 17 мая 2017 г.
Когда ваше приложение находится в фоновом режиме, Android направляет уведомления в системный трей. При нажатии на уведомление пользователя открывается панель запуска приложения по умолчанию.
Это включает в себя сообщения, которые содержат как уведомления, так и полезные данные (и все сообщения, отправленные из консоли уведомлений). В этих случаях уведомление доставляется в системный трей устройства, а полезная нагрузка данных доставляется в дополнениях к назначению вашего средства запуска.
Итак, вы должны использовать оба уведомления полезных данных + данные:
{
"to": "FCM registration ID",
"notification": {
"title" : "title",
"body" : "body text",
"icon" : "ic_notification"
},
"data": {
"someData" : "This is some data",
"someData2" : "etc"
}
}
Нет необходимости использовать click_action. Вы просто должны получить отрывки из намерений о деятельности LAUNCHER.
<activity android:name=".MainActivity">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Код Java должен быть в методе onCreate для MainActivity:
Intent intent = getIntent();
if (intent != null && intent.getExtras() != null) {
Bundle extras = intent.getExtras();
String someData= extras.getString("someData");
String someData2 = extras.getString("someData2");
}
Вы можете проверить оба уведомления полезной нагрузки + данные из консоли уведомлений Firebase. Не забудьте заполнить пользовательские поля данных в разделе "Дополнительные параметры"
Для захвата сообщения в фоновом режиме вам нужно использовать BroadcastReceiver
public class FirebaseDataReceiver extends WakefulBroadcastReceiver {
private final String TAG = "FirebaseDataReceiver";
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "I'm in!!!");
Bundle dataBundle = intent.getBundleExtra("data");
Log.d(TAG, dataBundle.toString());
}
}
и добавьте это в свой манифест:
<receiver
android:name="MY_PACKAGE_NAME.FirebaseDataReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
Вот более четкие понятия о сообщении Firebase. Я нашел это от их команды поддержки.
Firebase имеет три типа сообщений:
Уведомления: Уведомление работает на заднем или переднем плане. Когда приложение находится в фоновом режиме, уведомления уведомлений доставляются в системный трей. Если приложение находится на переднем плане, сообщения обрабатываются onMessageReceived()
или же didReceiveRemoteNotification
Обратные вызовы. По сути, это то, что называется отображением сообщений.
Сообщения данных: На платформе Android сообщение данных может работать как на заднем, так и на переднем плане. Сообщение данных будет обработано onMessageReceived(). Примечание для конкретной платформы будет следующим: На Android полезная нагрузка данных может быть получена в Intent, используемом для запуска вашей деятельности. Для уточнения, если у вас есть "click_action":"launch_Activity_1"
Вы можете получить это намерение через getIntent()
только из Activity_1
,
Сообщения с полезными нагрузками как для уведомлений, так и для данных. В фоновом режиме приложения получают полезную нагрузку для уведомлений в области уведомлений и обрабатывают полезные данные только тогда, когда пользователь нажимает на уведомление. На переднем плане ваше приложение получает объект сообщения с обоими полезными нагрузками. Во-вторых, click_action
Этот параметр часто используется в полезных данных уведомлений, а не в полезных данных. Если этот параметр используется внутри полезных данных, этот параметр будет обрабатываться как настраиваемая пара ключ-значение, и поэтому вам нужно будет реализовать настраиваемую логику, чтобы он работал как задумано.
Кроме того, я рекомендую вам использовать onMessageReceived
метод (см. Сообщение данных) для извлечения пакета данных. По вашей логике я проверил объект пакета и не нашел ожидаемого содержимого данных. Вот ссылка на похожий случай, который может дать больше ясности.
Для получения дополнительной информации посетите мою эту тему
Так как display-messages
которые отправляются из Firebase Notification UI, работает, только если ваше приложение находится на переднем плане. За data-messages
необходимо выполнить POST-вызов FCM
меры
Установите расширенное клиентское расширение Google Chrome Extension
Добавьте следующие заголовки
Ключ: Content-Type, Значение: application/json
Добавить тело
При использовании тем:
{ "to" : "/topics/topic_name", "data": { "key1" : "value1", "key2" : "value2", } }
При использовании регистрационного идентификатора:
{ "registration_ids" : "[{"id"},{id1}]", "data": { "key1" : "value1", "key2" : "value2", } }
Это оно!. Теперь слушай onMessageReceived
обратный звонок как обычно.
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
String value1 = data.get("key1");
String value2 = data.get("key2");
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
}
не вызывается каждый раз, когда вызывается только когда приложение находится в forground
есть один метод переопределения, этот метод вызывается каждый раз, независимо от того, какое приложение находится на переднем плане или в фоновом режиме или убито, но этот метод доступен с этой версией API Firebase
это версия, которую вы должны импортировать из Gradle
compile 'com.google.firebase:firebase-messaging:10.2.1'
это метод
@Override
public void handleIntent(Intent intent) {
super.handleIntent(intent);
// you can get ur data here
//intent.getExtras().get("your_data_key")
}
с предыдущим API Firebase этот метод не существовал, так что в этом случае сам обработчик базы огня, когда приложение находится в фоновом режиме.... теперь у вас есть этот метод, что когда-либо вы хотите... вы можете сделать это здесь, в этом методе......
если вы используете предыдущую версию, тогда действие по умолчанию начнется в этом случае вы можете получить данные таким же образом
if(getIntent().getExtras() != null && getIntent().getExtras().get("your_data_key") != null) {
String strNotificaiton = getIntent().getExtras().get("your_data_key").toString();
// делай что хочешь.... }
как правило, это структура с сервера, мы получаем в уведомлении
{
"notification": {
"body": "Cool offers. Get them before expiring!",
"title": "Flat 80% discount",
"icon": "appicon",
"click_action": "activity name" //optional if required.....
},
"data": {
"product_id": 11,
"product_details": "details.....",
"other_info": "......."
}
}
это зависит от того, как вы хотите дать этот ключ данных или вы хотите уведомить все, что вы можете дать....... то, что когда-либо вы дадите здесь с тем же ключом, вы получите эти данные........,
Есть несколько случаев, когда вы не отправляете действие клика в том случае, когда вы нажимаете на уведомление, откроется действие по умолчанию, но если вы хотите открыть свое конкретное действие, когда приложение находится в фоновом режиме, вы можете вызвать это действие с помощью метода handleIntent, потому что это вызывается каждый раз
Простое резюме, как это
если ваше приложение работает;
onMessageReceived()
это триггеры.
если ваше приложение не запущено (убито смахиванием);
onMessageReceived()
не запускается и не доставляется по адресу direclty. Если у вас есть особая пара ключ-значение. Они не работают, потому что onMessageReceived() не работает.
Я нашел этот путь;
В своей деятельности по запуску вставьте эту логику,
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState, R.layout.activity_splash);
if (getIntent().getExtras() != null && getIntent().getExtras().containsKey("PACKAGE_NAME")) {
// do what you want
// and this for killing app if we dont want to start
android.os.Process.killProcess(android.os.Process.myPid());
} else {
//continue to app
}
}
в этом блоке ищите ваши ключи в соответствии с пользовательским интерфейсом Firebase.
В этом примере мой ключ и значение, как указано выше; (простите за язык =))
Когда мой код работает, я получаю "com.rda.note".
android.os.Process.killProcess(android.os.Process.myPid());
с этой строкой кода я закрыл свое приложение и открыл Google Play Market
счастливого кодирования =)
Я выяснил сценарии,
Когда приложение находится на переднем плане, из FirebaseService вызывается метод onMessageReceived(). Поэтому будет вызываться pendingIntent, определенный в классе обслуживания.
И когда приложение находится в фоновом режиме, вызывается первое действие.
Теперь, если вы используете всплесковую активность, то должны иметь в виду, что будет вызываться splashactivity, иначе, если не будет splashActivity, будет вызываться то, чем является первая активность.
Затем вам нужно проверить getIntent() firstActivity, чтобы увидеть, есть ли у него какой-либо пакет. Если все в порядке, вы увидите, что пакет есть с заполненными значениями. Если значение в теге данных, отправленное с сервера, выглядит следующим образом,
"data": {
"user_name": "arefin sajib",
"value": "user name notification"
}
Затем в первом упражнении вы увидите, что есть действительное намерение (getIntent() не равно NULL), допустимый пакет и внутренний пакет, там будет весь JSON, упомянутый выше, с данными в качестве ключа.
Для этого сценария код для извлечения значения будет выглядеть так:
if(getIntent()!=null){
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
try {
JSONObject object = new JSONObject(bundle.getStringExtra("data"));
String user_name = object.optString("user_name");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
У меня была такая же проблема. После некоторого покопания, почему моя MainActivity вызывается с намерением без данных, я понял, что моя активность LAUNCHER (как в Manifest) - это SplashActivity. Там я нашел данные сообщения и отправил их в MainActivity. Работает как шарм. Я верю, что это может кому-то помочь.
Спасибо за все остальные ответы.
Полностью удалите полезные данные уведомлений из запроса вашего сервера. Отправляйте только данные и обрабатывайте их в onMessageReceived()
иначе твой onMessageReceived
не будет срабатывать, когда приложение находится в фоновом режиме или убито.
Вот что я отправляю с сервера:
{
"data":{
"id": 1,
"missedRequests": 5
"addAnyDataHere": 123
},
"to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......"
}
Таким образом, вы можете получить свои данные в onMessageReceived(RemoteMessage message)
вот так: (скажем, я должен получить идентификатор)
Object obj = message.getData().get("id");
if (obj != null) {
int id = Integer.valueOf(obj.toString());
}
И так же вы можете получить любые данные, которые вы отправили с сервера в течение onMessageReceived()
,
Спасибо всем вам за ваши ответы. Но я решил это, отправив сообщение с данными вместо отправки уведомления. Код сервера
<?php
$url = "https://fcm.googleapis.com/fcm/send";
$token = "C-l6T_a7HouUK****";
$serverKey = "AAAAaOcKS00:********";
define( 'API_ACCESS_KEY', $serverKey );
$registrationIds = array($token);
// prep the bundle
$msg = array
(
'message' => 'here is a message. message',
'title' => 'This is a title. title',
'subtitle' => 'This is a subtitle. subtitle',
'tickerText' => 'Ticker text here...Ticker text here...Ticker text
here',
'vibrate' => 1,
'sound' => 1,
'largeIcon' => 'large_icon',
'smallIcon' => 'small_icon'
);
$fields = array
(
'registration_ids' => $registrationIds,
'data' => $msg
);
$headers = array
(
'Authorization: key=' . API_ACCESS_KEY,
'Content-Type: application/json'
);
$ch = curl_init();
curl_setopt( $ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send'
);
curl_setopt( $ch,CURLOPT_POST, true );
curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
$result = curl_exec($ch );
curl_close( $ch );
echo $result;
?>
И поймал данные в onMessageReceived
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
sendNotification(remoteMessage.getData().get("message"));
}
// Check if message contains a notification payload.
else if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
sendNotification(remoteMessage.getNotification().getBody());
}
}
private void sendNotification(String messageBody) {
Intent intent = new Intent(this, Notify.class).putExtra("msg",messageBody);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
String channelId = "idddd";
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(MyFirebaseMessagingService.this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("FCM Message")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
В общем
В FCM (Firebase Cloud Messaging) есть два типа сообщений:
Отображение сообщений: эти сообщения вызывают обратный вызов onMessageReceived() только тогда, когда ваше приложение находится на переднем плане.
Сообщения с данными: эти сообщения запускают обратный вызов onMessageReceived(), даже если ваше приложение находится на переднем плане / в фоновом режиме / убито
Пример сообщений с данными:
{
"to": "/path",
"data":
{
"my_custom_key": "my_custom_value",
"my_custom_key2": true
}
}
Пример отображения сообщений:
{
"notification": {
"title" : "title",
"body" : "body text",
"icon" : "ic_notification",
"click_action" : "OPEN_ACTIVITY_1"
}
}
Сторона Android может обрабатывать такие уведомления, как:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
…
@Override public void onMessageReceived(RemoteMessage remoteMessage){
Map<String, String> data = remoteMessage.getData();
String myCustomKey = data.get("my_custom_key");
}
…
}
Более подробную информацию о FCM вы можете найти здесь: Настройка клиентского приложения Firebase Cloud Messaging на Android
Чтобы иметь возможность получать данные из уведомления Firebase, отправленного, когда приложение находится в фоновом режиме, вам необходимо добавить запись click_action в набор данных уведомлений.
Установите дополнительные параметры вашего уведомления в консоли Firebase следующим образом: ( здесь вы должны указать любые дополнительные данные, которые вы хотите получить в своем приложении ):
И включите фильтр намерений в файл манифеста под активностью, которую нужно запустить.
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MyApp.SplashScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<intent-filter>
<action android:name="FIREBASE_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Затем получите данные пакета в своей активности onNewIntent:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Bundle data = intent.getExtras();
if (data != null) {
for (String key : data.keySet()) {
Object value = data.get(key);
// do what you want with the data entries
Log.d(FIREBASE_TAG, "Key: " + key + " Value: " + value);
Toast.makeText(this, "Key: "+key+".... Value: "+value, Toast.LENGTH_LONG).show;
}
}
}
Когда ваше приложение находится на переднем плане, вы можете установить onMessageReceived следующим образом:
@Override
public void onMessageReceived(@NonNull RemoteMessage message) {
Log.d(FIREBASE_TAG, "Message From: " + message.getFrom());
if (message.getNotification() != null) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Map<String, String> data = message.getData();
if(data != null && !data.isEmpty()){
for(Map.Entry<String ,String > entry : data.entrySet()) {
intent.putExtra(entry.getKey(), entry.getValue());
}
}
//.......
// implement the rest of the code to show notification
//
}
}
Я добавил следующий код в
firebase-messaging-sw.js
,
messaging.onBackgroundmessage((payload)=>{
console.log("background message detected!!");
console.log("message : ", payload);
})
это срабатывало каждый раз при получении сообщения в фоновом режиме. но я не смог использовать полезную нагрузку в основном потоке, так как SW не поддерживал ее. Итак, я много исследовал и нашел решение на форуме Android.
поэтому решение состоит в том, что нам придется удалить полезную нагрузку уведомления из полезной нагрузки запроса.
поэтому я изменил свою полезную нагрузку с
{
"notification": {
"title": "Hey there",
"body": "Subscribe to AMAL MOHAN N youtube channel"
},
"to": "your-browser-token",
"data": {
"value1": "text",
"value2": "",
"value3": "sample3",
"value4": "sample4"
}
}
к
{
"to": "your-browser-token",
"data": {
"value1": "text",
"value2": "",
"value3": "sample3",
"value4": "sample4"
}
}
изменение полезной нагрузки автоматически сделало
receiveMessage()
запускать в сообщениях переднего плана, а также в фоновых сообщениях.
Я нашел это на форуме Android, и это сработало для меня! пожалуйста, дайте мне знать, работает ли это для вас.
Простой способ отправки сообщений, даже если приложение находится в фоновом режиме и на переднем плане, как указано ниже:- Чтобы отправить сообщение с помощью API, вы можете использовать инструмент под названием AdvancedREST Client, его расширение chrome, и отправить сообщение со следующими параметрами.
Инструмент для отдыха клиентов Ссылка: https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo
используйте этот URL:- https://fcm.googleapis.com/fcm/send Тип контента:application/json Авторизация: ключ = ключ вашего сервера от или ключ авторизации (см. ниже ссылка)
{ "data": {
"image": "https://static.pexels.com/photos/4825/red-love-romantic-flowers.jpg",
"message": "Firebase Push Message Using API"
"AnotherActivity": "True"
},
"to" : "device id Or Device token"
}
Ключ авторизации можно получить, посетив консоль разработчиков Google и нажав кнопку Credentials в левом меню вашего проекта. Среди перечисленных ключей API ключом авторизации будет ключ сервера.
И вам нужно поместить tokenID получателя в раздел "to" вашего POST-запроса, отправленного с помощью API.
Вы хотите работать с onMessageReceived(RemoteMessage remoteMessage) в фоновом режиме и отправлять только часть данных, уведомляющую эту часть:
"data": "image": "", "message": "Firebase Push Message Using API",
"AnotherActivity": "True", "to": "идентификатор устройства или токен устройства"
При этом onMessageRecivied является фоном вызова и передним планом, нет необходимости обрабатывать уведомления с помощью панели уведомлений о вашей активности запуска. Обработайте данные, используя это:
public void onMessageReceived(RemoteMessage remoteMessage) if (remoteMessage.getData (). size ()> 0) Log.d (TAG, "Полезные данные сообщения: " + remoteMessage.getData());
Предоставленные решения в моем случае не сработали.
В конце концов, я выяснил, что некоторые приложения для оптимизации заряда батареи дают пользователям возможность разрешать или блокировать отправку уведомлений приложениями. В моем случае это был Smart Manager от Samsung, который автоматически блокировал мое приложение, как только оно было убито / удалено из списка последних.
Отключение этой функции для моего приложения было единственным решением, которое я смог найти.
Обновленный ответ в соответствии с OAUTH 2.0:
Принятый ответ больше не работает (ошибка 401). В этом случае будет проблема с аутентификацией, поскольку FCM теперь использует OAUTH 2
Поэтому я читаю документацию Fire Base и, согласно документации, новый способ публикации сообщений данных;
POST: https://fcm.googleapis.com/v1/projects/YOUR_FIREBASEDB_ID/messages:send
Заголовки
Key: Content-Type, Value: application/json
Auth
Bearer YOUR_TOKEN
Пример тела
{
"message":{
"topic" : "xxx",
"data" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message"
}
}
}
В URL есть идентификатор базы данных, который вы можете найти на своей консоли Firebase. (Идут проектные расчеты)
А теперь давайте возьмем наш токен (он будет действителен только 1 час):
Сначала в консоли Firebase откройте " Настройки"> "Учетные записи служб". Нажмите " Создать новый закрытый ключ", надежно сохраните файл JSON, содержащий ключ. Мне был нужен этот JSON-файл для авторизации запросов к серверу вручную. Я скачал это.
Затем я создаю проект node.js и использую эту функцию для получения моего токена;
var PROJECT_ID = 'YOUR_PROJECT_ID';
var HOST = 'fcm.googleapis.com';
var PATH = '/v1/projects/' + PROJECT_ID + '/messages:send';
var MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
getAccessToken().then(function(accessToken) {
console.log("TOKEN: "+accessToken)
})
});
function getAccessToken() {
return new Promise(function(resolve, reject) {
var key = require('./YOUR_DOWNLOADED_JSON_FILE.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function(err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
Теперь я могу использовать этот токен в своем почтовом запросе. Затем я публикую свое сообщение с данными, и теперь оно обрабатывается функцией приложения onMessageReceived.
С 2019 года Google Firebase сильно изменил свои API, я имею в виду: 'com.google.firebase:firebase-messaging:18.0.0'
в 18.0.0 они удалили MyFirebaseInstanceIDService
и вам нужно получить токен в MyFirebaseMessagingService
так что вам просто нужно написать:
@Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
}
а также в вашем AndroidManifest.xml вы должны удалить:
<service android:name=".service.MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
Также рекомендуется установить значения по умолчанию, чтобы настроить внешний вид уведомлений. Вы можете указать пользовательский значок по умолчанию и пользовательский цвет по умолчанию, которые применяются, когда эквивалентные значения не установлены в полезной нагрузке уведомления.
Добавьте эти строки внутри тега приложения, чтобы установить значок по умолчанию и цвет:
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/push_channel" />
Теперь для обработки уведомительных сообщений в фоновом приложении вы должны определить Intent в своей первой Деятельности, даже если это SplashScreen. Когда ваше приложение находится в фоновом режиме, Android направляет уведомления в системный трей. При нажатии на уведомление пользователя открывается панель запуска приложения по умолчанию.
например, если ваш Json такой:
"data": {
"message": "2",
"title": "1",
"pushType" : "banner",
"bannerLink": "http://www.google.com",
"image" : "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"}
вам просто нужно написать простое намерение получить эти значения:
Bundle extras = intent.getExtras();
String bannerLink = extras.getString("bannerLink");
...
String channelId = extras.getString("channelId");
Июнь 2018 г. Ответ -
Вы должны убедиться, что в сообщении нет ни одного ключевого слова "уведомления". Включайте только "данные", и приложение сможет обработать сообщение в onMessageReceived, даже если оно находится в фоновом режиме или уничтожено.
Использование облачных функций:
const message = {
token: token_id, // obtain device token id by querying data in firebase
data: {
title: "my_custom_title",
body: "my_custom_body_message"
}
}
return admin.messaging().send(message).then(response => {
// handle response
});
Затем в вашем onMessageReceived(), в вашем классе, расширяющем com.google.firebase.messaging.FirebaseMessagingService:
if (data != null) {
Log.d(TAG, "data title is: " + data.get("title");
Log.d(TAG, "data body is: " + data.get("body");
}
// build notification using the body, title, and whatever else you want.
Используя этот код, вы можете получить уведомление в фоновом режиме / на переднем плане, а также поместить действие:
//Data should come in this format from the notification
{
"to": "/xyz/Notifications",
"data": {
"key1": "title notification",
"key2": "description notification"
}
}
В приложении используйте этот код:
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
String key1Data = remoteMessage.getData().get("key1");
// use key1Data to according to your need
}
Я решил эту проблему, используя Broadcast Message.
создать широковещательную рассылку и отправить полезную нагрузку в широковещательном сообщении от работника службы.
затем получите полезную нагрузку внутри приложения и обработайте ее так, как хотите.
Этот случай предназначен только для уведомления администратора Firebase. Если приложение находится в фоновом режиме, FirebaseMessagingService не запускается.
Для обработки этого случая перейдите к действию запуска и проверьте наличие прилагаемого пакета намерений и распечатайте все данные, используя этот код:
intent?.extras?.let {it->
for (key in bundle.keySet()) {
val value = bundle[key]
Log.d("NotificationData", String.format("%s %s (%s)", key, value.toString(), value!!.javaClass.name))
}
}
журнал регистрации отображаются все данные с типами данных;
Пример :
Я хочу получить неделю и заголовок из уведомлений, буду использовать этот код
intent?.extras?.let {it->
if (it.containsKey("week")){
}
if (it.containsKey("title")){
}
В дополнение к ответам выше, если вы тестируете push-уведомления с помощью консоли FCM, ключ и объект "данные" не добавляются в комплект Push-уведомлений. Таким образом, вы не получите детальное push-уведомление, когда приложение находится в фоновом режиме или убито.
В этом случае вам нужно выбрать консоль администратора для тестирования фонового сценария приложения.
Здесь вы добавите ключ "data" в ваш push-пакет. Таким образом, подробный толчок будет показан, как и ожидалось. Надеюсь, это поможет немногим.