Firebase onMessageReceived не вызывается, когда приложение в фоновом режиме


Я работаю с Firebase и тестирую отправку уведомлений в мое приложение с моего сервера, пока приложение находится в фоновом режиме. Уведомление отправляется успешно, оно даже появляется в Центре уведомлений устройства, но когда появляется уведомление или даже если я нажимаю на него, метод onMessageReceived внутри моего FCMessagingService никогда не вызывается.

когда я тестировал это, пока мое приложение было на переднем плане, был вызван метод onMessageReceived и все работать отлично. Проблема возникает, когда приложение работает в фоновом режиме.

это поведение, или есть ли способ я могу это исправить?

вот мой FBMessagingService:

import android.util.Log;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class FBMessagingService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.i("PVL", "MESSAGE RECEIVED!!");
        if (remoteMessage.getNotification().getBody() != null) {
            Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getNotification().getBody());
        } else {
            Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getData().get("message"));
        }
    }
}
23 146

23 ответа:

это работает по назначению, уведомления доставляются на ваш onmessagereceived обратного вызова только тогда, когда ваше приложение находится на переднем плане. Если ваше приложение находится в фоновом режиме или закрыто, то в Центре уведомлений отображается уведомление, и любое данные из этого сообщения передаются в intent который запускается в результате нажатия пользователем на уведомление.

вы можете указать click_action, чтобы указать намерение, которое должно быть запущено когда уведомление прослушивается пользователем. Основное действие используется, если не указано click_action.

при запуске намерения вы можете использовать

getIntent().getExtras();

чтобы получить набор, который будет включать любые данные, отправленные вместе с уведомлением.

для получения дополнительной информации о сообщении уведомления см. docs.

УСТАРЕВШЕЕ РЕШЕНИЕ:

у меня есть идеальное решение для этого:

вам нужно выполнить 2 простых шага:

  1. обновите firebase до версии compile com.google.firebase:firebase-messaging:10.2.1
  2. переопределить handleIntent(Intent intent) метод FirebaseMessagingService класса.

handleIntent() метод вызывается каждый раз, находится ли приложение на переднем плане, фоне или убитом состоянии.

удалить notification поле полностью из вашего запроса сервера. Отправить только data и обрабатывать его в onMessageReceived() иначе onMessageReceived() не будет срабатывать, когда приложение находится в фоновом режиме или убиты.

не забудьте включить "priority": "high" поле в запросе уведомления. Согласно документации: сообщения данных отправляются с нормальным приоритетом, поэтому они не будут поступать мгновенно; это также может быть проблемой.

вот что я отправляю с сервера

{
  "data":{
    "id": 1,
    "missedRequests": 5
    "addAnyDataHere": 123
  },
  "to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
  "priority": "high"
}

так что вы можете получить ваши данные в onMessageReceived(RemoteMessage message) такой....допустим, я должен получить id

Object obj = message.getData().get("id");
        if (obj != null) {
            int id = Integer.valueOf(obj.toString());
        }

У меня была та же проблема. Проще использовать "сообщение данных" вместо "уведомления". Сообщение данных всегда загружает класс onMessageReceived.

в этом классе вы можете сделать свое собственное уведомление с notificationbuilder.

пример:

 @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
    }

    private void sendNotification(String messageTitle,String messageBody) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0 /* request code */, intent,PendingIntent.FLAG_UPDATE_CURRENT);

        long[] pattern = {500,500,500,500,500};

        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_stat_name)
                .setContentTitle(messageTitle)
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setVibrate(pattern)
                .setLights(Color.BLUE,1,1)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }

вот более четкие понятия о сообщении firebase. Я нашел его у их команды поддержки.

Firebase имеет три типа сообщений:

уведомления : уведомление работает на фоне или переднем плане. Когда приложение находится в фоновом режиме, уведомления доставляются в системный трей. Если приложение находится на переднем плане, сообщения обрабатываются onMessageReceived() или didReceiveRemoteNotification обратные вызовы. Это по существу то, что называется Отображать сообщения.

сообщения: на платформе Android, сообщение данных может работать на фоне и переднем плане. Сообщение данных будет обработано onMessageReceived (). Специфическое для платформы Примечание здесь будет: на Android полезная нагрузка данных может быть получена в намерении, используемом для запуска вашей деятельности. Чтобы уточнить, если у вас есть "click_action":"launch_Activity_1", вы можете получить это намерение через getIntent() только Activity_1.

сообщения с уведомлением и сведения полезные нагрузки: в фоновом режиме приложения получают полезную нагрузку уведомления в панели уведомлений и обрабатывают только полезную нагрузку данных, когда пользователь нажимает на уведомление. Когда приложение находится на переднем плане, оно получает объект сообщения с обеими доступными полезными нагрузками. Во-вторых, параметр click_action часто используется в полезных данных уведомлений, а не в полезных данных данных. Если используется внутри полезной нагрузки данных, этот параметр будет рассматриваться как пользовательская пара ключ-значение, и поэтому вам нужно будет реализовать пользовательскую логику для того, чтобы работать, как предполагалось.

кроме того, я рекомендую вам использовать метод onMessageReceived (см. сообщение данных) для извлечения пакета данных. Из вашей логики я проверил объект bundle и не нашел ожидаемого содержимого данных. Вот ссылка на аналогичный случай, который может обеспечить большую ясность.

для получения дополнительной информации посетите мой этой теме

согласно документации Firebase Cloud Messaging-если активность находится на переднем плане, то onMessageReceived будет вызван. Если действие находится в фоновом режиме или закрыто, то уведомление отображается в Центре уведомлений для активности запуска приложений. Вы можете вызвать свою настроенную активность по щелчку уведомления, если ваше приложение находится в фоновом режиме, вызвав api службы rest для обмена сообщениями firebase как:

URL -https://fcm.googleapis.com/fcm/send

способ Типа - в должности

Header- Content-Type:application/json
Authorization:key=your api key

Тело/Полезная Нагрузка:

{ "notification": {
    "title": "Your Title",
    "text": "Your Text",
     "click_action": "OPEN_ACTIVITY_1" // should match to your intent filter
  },
    "data": {
    "keyname": "any value " //you can get this data as extras in your activity and this data is optional
    },
  "to" : "to_id(firebase refreshedToken)"
} 

и с этим в вашем приложении, вы можете добавить ниже код в вашей деятельности, называется:

<intent-filter>
                <action android:name="OPEN_ACTIVITY_1" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

Если приложение находится в фоновом режиме или нет(убили), а вы клик on уведомления, вы должны проверить для полезной нагрузки в LaunchScreen (в моем случае экран запуска является MainActivity.ява.)

Так MainActivity.java on onCreate проверить дополнительно:

    if (getIntent().getExtras() != null) {
        for (String key : getIntent().getExtras().keySet()) {
            Object value = getIntent().getExtras().get(key);
            Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
        }
    }

переопределить handleIntent метод FirebaseMessageService работает для меня.

здесь код C# (Xamarin)

public override void HandleIntent(Intent intent)
{
    try
    {
        if (intent.Extras != null)
        {
            var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");

            foreach (string key in intent.Extras.KeySet())
            {
                builder.AddData(key, intent.Extras.Get(key).ToString());
            }

            this.OnMessageReceived(builder.Build());
        }
        else
        {
            base.HandleIntent(intent);
        }
    }
    catch (Exception)
    {
        base.HandleIntent(intent);
    }
}

а вот код Java

public void handleIntent(Intent intent)
{
    try
    {
        if (intent.getExtras() != null)
        {
            RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");

            for (String key : intent.getExtras().keySet())
            {
                builder.addData(key, intent.getExtras().get(key).toString());
            }

            onMessageReceived(builder.build());
        }
        else
        {
            super.handleIntent(intent);
        }
    }
    catch (Exception e)
    {
        super.handleIntent(intent);
    }
}

у меня та же проблема. Если приложение находится на переднем плане-оно запускает мою фоновую службу, где я могу обновить свою базу данных на основе типа уведомления. Но, приложение переходит в фоновый режим-служба уведомлений по умолчанию будет заботиться, чтобы показать уведомление пользователю.

вот мое решение для идентификации приложение в фоновом режиме и запуска фоновой службы,

public class FirebaseBackgroundService extends WakefulBroadcastReceiver {

  private static final String TAG = "FirebaseService";

  @Override
  public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "I'm in!!!");

    if (intent.getExtras() != null) {
      for (String key : intent.getExtras().keySet()) {
        Object value = intent.getExtras().get(key);
        Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
        if(key.equalsIgnoreCase("gcm.notification.body") && value != null) {
          Bundle bundle = new Bundle();
          Intent backgroundIntent = new Intent(context, BackgroundSyncJobService.class);
          bundle.putString("push_message", value + "");
          backgroundIntent.putExtras(bundle);
          context.startService(backgroundIntent);
        }
      }
    }
  }
}

в манифесте.xml

<receiver android:exported="true" android:name=".FirebaseBackgroundService" android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </receiver>

протестировано это решение в последней версии версия для android 8.0. Спасибо

по умолчанию Пусковой Деятельности в вас приложение будет запущено, когда ваше приложение находится в фоновом режиме, и вы нажимаете уведомление, если у вас есть какая-либо часть данных с вашим notifcation вы можете обрабатывать его в той же деятельности следующим образом,

if(getIntent().getExtras()! = null){
  //do your stuff
}else{
  //do that you normally do
}

я реализовал этот простой способ отправки сообщений, даже если приложение закрыто, фон и на переднем плане тоже. Я ранее использовал вашего POST запрос отправлен с помощью API.

и этот кусок кода android / / сообщение будет содержать сообщение Push

String message = remoteMessage.getData().get("message1");

//imageUri will contain URL of the image to be displayed with Notification
String imageUri = remoteMessage.getData().get("image");

//If the key AnotherActivity has  value as True then when the user taps on notification, in the app AnotherActivity will be opened.
//If the key AnotherActivity has  value as False then when the user taps on notification, in the app MainActivity2 will be opened.
String TrueOrFlase = remoteMessage.getData().get("AnotherActivity");

//To get a Bitmap image from the URL received
bitmap = getBitmapfromUrl(imageUri);

sendNotification(message, bitmap, TrueOrFlase);

просто вызовите это в методе onCreate вашего MainActivity:

if (getIntent().getExtras() != null) {
           // Call your NotificationActivity here..
            Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
            startActivity(intent);
        }

в соответствии с решением от T3h Exi я хотел бы разместить чистый код здесь. Просто поместите его в MyFirebaseMessagingService и все работает нормально, если приложение находится в фоновом режиме. Вам нужно хотя бы скомпилировать com.гуглить.огневая база:военнослужащих-сообщений:10.2.1

 @Override
public void handleIntent(Intent intent)
{
    try
    {
        if (intent.getExtras() != null)
        {
            RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");

            for (String key : intent.getExtras().keySet())
            {
                builder.addData(key, intent.getExtras().get(key).toString());
            }



           onMessageReceived(builder.build());
        }
        else
        {
            super.handleIntent(intent);
        }
    }
    catch (Exception e)
    {
        super.handleIntent(intent);
    }
}

Если приложение находится в фоновом режиме Fire-base по умолчанию обработки уведомлений, но если мы хотим, чтобы наши пользовательские уведомления, чем мы должны изменить нашу сторону сервера, которая отвечает за отправку наших пользовательских данных (полезная нагрузка данных)

удалить данные уведомления из вашего запроса сервером. Отправляйте только данные и обрабатывайте их в onMessageReceived() в противном случае ваш onMessageReceived не будет запущен, когда приложение находится в фоновом режиме или убито.

теперь, формат кода на стороне сервера похоже,

{
  "collapse_key": "CHAT_MESSAGE_CONTACT",
  "data": {
    "loc_key": "CHAT_MESSAGE_CONTACT",
    "loc_args": ["John Doe", "Contact Exchange"],
    "text": "John Doe shared a contact in the group Contact Exchange",
    "custom": {
      "chat_id": 241233,
      "msg_id": 123
    },
    "badge": 1,
    "sound": "sound1.mp3",
    "mute": true
  }
}

Примечание: смотрите эту строку в приведенном выше коде
"текст": "Джон Доу, общий контакт группы контактов обмен" в полезной нагрузке данных вы должны использовать параметр "текст "вместо параметров" тело "или" сообщение " для описания сообщения или того, что вы хотите использовать текст.

onMessageReceived()

@Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.e(TAG, "From: " + remoteMessage.getData().toString());

        if (remoteMessage == null)
            return;

        // Check if message contains a data payload.
        if (remoteMessage.getData().size() > 0) {
           /* Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());*/
            Log.e(TAG, "Data Payload: " + remoteMessage);

            try {

                Map<String, String> params = remoteMessage.getData();
                JSONObject json = new JSONObject(params);
                Log.e("JSON_OBJECT", json.toString());


                Log.e(TAG, "onMessageReceived: " + json.toString());

                handleDataMessage(json);
            } catch (Exception e) {
                Log.e(TAG, "Exception: " + e.getMessage());
            }
        }
    }

попробуйте это:

public void handleIntent(Intent intent) {
    try {
        if (intent.getExtras() != null) {
            RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
            for (String key : intent.getExtras().keySet()) {
            builder.addData(key, intent.getExtras().get(key).toString());
        }
            onMessageReceived(builder.build());
        } else {
            super.handleIntent(intent);
        }
    } catch (Exception e) {
        super.handleIntent(intent);
    }
}

У меня была эта проблема(приложение не хочет открываться при нажатии уведомления, если приложение находится в фоновом режиме или закрыто), и проблема была недопустимой click_action в тело уведомления, попробуйте удалить или изменить его на что-то действует.

У меня была та же проблема, и я еще немного покопался в этом. Когда приложение находится в фоновом режиме, а уведомления отправляется в системный трей, но a информационное сообщение отправлено onMessageReceived()
См https://firebase.google.com/docs/cloud-messaging/downstream#monitor-token-generation_3
и https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java

чтобы гарантировать, что сообщение, которое вы отправляете, документы говорят, "используйте сервер приложений и API сервера FCM: установите только ключ данных. Может быть разборной или неразборной."
Видеть https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

пункт, который заслуживает внимания, заключается в том, что вы должны использовать сообщение данных - только ключ данных - чтобы получить onmessagereceived обработчик вызывается даже тогда, когда приложение находится в фоновом режиме. У вас не должно быть никакого другого ключа уведомления в вашей полезной нагрузке, иначе обработчик не будет срабатывать, если приложение находится в фоновом режиме.

Это упоминается (но не так подчеркивается в документации FCM) здесь:

https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

используйте сервер приложений и API сервера FCM: установите только ключ данных. Может быть либо складной, либо не складной.

бэкэнд, с которым я работаю, использует уведомления а не сообщения данных. Поэтому, прочитав все ответы, я попытался получить дополнительные функции из пакета намерения, которое приходит к запущенной деятельности. Но независимо от того, какие ключи я пытался получить от getIntent().getExtras();, значение всегда было null.

тем не менее, я, наконец, нашел способ отправить данные с помощью уведомления и извлечь его из намерения.

ключ здесь это добавить полезных данных сообщение уведомления.

пример:

{
    "data": {
        "message": "message_body",
        "title": "message_title"
    },
    "notification": {
        "body": "test body",
        "title": "test title"
    },
    "to": "E4An.."
}

после того, как вы сделаете это, вы сможете получить свою информацию таким образом:

intent.getExtras().getString("title") будет message_title

и intent.getExtras().getString("message") будет message_body

ссылка

Если ваша проблема связана с показом большого изображения, т. е. если вы отправляете push-уведомление с изображением из консоли firebase, и оно отображает изображение только в том случае, если приложение находится на переднем плане. Решение этой проблемы заключается в отправке push-сообщения только с полем данных. Что-то вроде этого:

{ "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" }

существует два типа сообщений: уведомления и сообщения данных. Если вы отправляете только сообщение данных, то есть без объекта уведомления в строке сообщения. Она будет вызываться, когда приложение в фоновом режиме.

когда сообщение получено, и ваше приложение находится в фоновом режиме, уведомление отправляется в дополнительное намерение основного действия.

вы можете проверить дополнительное значение в функции oncreate() или onresume() основного действия.

вы можете проверить на поля, как данные, таблицы и т. д. ( указанный в уведомлении)

например, я отправил, используя данные в качестве ключа

public void onResume(){
    super.onResume();
    if (getIntent().getStringExtra("data")!=null){
            fromnotification=true;
            Intent i = new Intent(MainActivity.this, Activity2.class);
            i.putExtra("notification","notification");
            startActivity(i);
        }

}

просто переопределите метод OnCreate FirebaseMessagingService. Он вызывается, когда приложение находится в фоновом режиме:

public override void OnCreate()
{
    // your code
    base.OnCreate();
}