2016-05-21 3 views
108

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

Когда я тестировал это, когда мое приложение находилось на переднем плане, вызывается метод 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")); 
     } 
    } 
} 
+0

В дополнение к телу JSon, где ваш [onTokenRefresh] (https://firebase.google.com/docs/cloud-messaging/downstream#access-the -registration-token_1) код? Вы завершили настройку [Android setup] (https://firebase.google.com/docs/cloud-messaging/android/client#set-up-firebase-and-the-fcm-sdk)? – Kato

+4

Что вы имеете в виду под заголовком json?Кроме того, мой код onTokenRefresh находится внутри моей службы FirebaseInstanceID. – Cyogenos

+0

Можете ли вы разместить полезную нагрузку на образец, которую вы отправляете? –

ответ

73

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

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

Когда цель запуска вы можете использовать

getIntent().getExtras(); 

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

Подробнее об уведомлении см. docs.

+2

Есть ли способ установить 'click_action', когда Я использую Firebase Notification Console? –

+0

Не в это время. –

+2

нормально, но что, если приложение убито (нет фона или фона)? – michalu

1

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

22

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

В этом классе вы можете сделать собственное уведомление с помощью органайзера уведомлений.

Пример:

@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()); 
    } 
+1

Спасибо .. Я изменил код сервера и использовал «данные» вместо «уведомления», и теперь он отлично работает, –

+3

@ Koot работает, только если приложение находится на переднем плане, если оно находится в фоновом режиме. может ли помочь мне инициировать это событие в обоих случаях? –

+0

@AnantShah как выглядит ваш POST на сервер Firebase? – Koot

17

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

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

Метод тип- POST

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> 
+0

Где я могу создать намерение и открыть ли он конкретное мероприятие? Я знаю, что это регистрирует намерение OPEN_ACTIVITY_1 в манифесте, но где я его на самом деле называю? – Cyogenos

+0

Проверьте это: https://firebase.google.com/docs/cloud-messaging/downstream#sample-receive –

5

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

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

У меня была такая же проблема, и я сделал еще кое-что. Когда приложение работает в фоновом режиме, сообщениеуведомление отправляется в системный трей, НО сообщениеданные передаются в 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 ТСМ:.. Установите только ключ может быть или сборно-разборные или нескладной»
См https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

0

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

15

Здесь более понятные понятия о сообщении о пожарной базе. Я нашел его в своей команде поддержки.

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

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

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

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

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

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

1

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

Он упоминается (но не так подчеркивается в ТСМ документации) здесь:

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

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

29

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

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

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

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

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

Я обнаружил, что если я отправляю только сообщение данных, тогда приложение, которое очищается от диспетчера задач, не сможет получить уведомление. Это предполагаемое поведение? –

+1

Это было решение для меня получать сообщения в фоновом режиме! –

+0

Означает ли это, что в консоли мы не можем обрабатывать данные уведомления, и если мы отправим чистый HTTP-запрос без раздела уведомлений, мы сможем обрабатывать данные уведомления? Я начал использовать Firebase Console, ожидая, что смогу это сделать, поэтому я возвращаюсь к уведомлению HTTP POST :-( –

4

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

Чтобы отправить эти пользовательские данные с изображением вы можете использовать инструмент под названием AdvancedREST Client, это расширение хром, и отправить сообщение со следующими параметрами:

Rest client tool Link: https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo 

используют эту url:- https://fcm.googleapis.com/fcm/send Content-Type:application/json Authorization:key=Your Server key From or Authorization key (см исх)

{ "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 и нажмите кнопку «Учетные данные» в меню слева для вашего проекта. Среди перечисленных ключей API ключ сервера будет вашим ключом авторизации.

И вы должны положить tokenID получателя в раздел to вашего POST, отправленный с использованием API.

И этот кусок кода андроида // сообщение будет содержать принудительное сообщение

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); 
1

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

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

Ключом здесь является добавление данных в сообщение Уведомление.

Пример:

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

Reference

2

Просто позвоните это в OnCreate метод вашего MainActivity в:

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

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

Так MainActivity.java на OnCreate проверка для Дополнительно:

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

Как я могу получить название, тело и данные уведомления? –

+0

Спасибо, это ответ. @ Md.Tarikul Islam, используя onMessageReceived(), как указано в этот вопрос – felixwcf

1

Если ваша проблема связана с показывая большое изображение, т.е. если вы отправляете толчок уведомление с изображением из 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" } 
+0

вы потеряли запятую перед «AnotherActivity». Мой андроид вибрирует, но на самом деле ничего не отображается (нет текста, нет изображения, нет толчка). – jekaby

47

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

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

  1. Улучшайте firebase версии компиляции com.google.firebase:firebase-messaging:10.2.1
  2. Override handleIntent(Intent intent) метод в вашем классе FirebaseMessagingService.

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

+0

Спасибо! Я потерял пару дней на этой проблеме, и этот меня спас. –

+0

действительно идеальное решение! – EduXavier

+0

идеальное решение я искал ....., и сохранить свой день человек ......... –

4

Переопределение 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); 
    } 
} 
+0

handleIntent() окончательно – Ettore

+0

@Ettore Как я могу вызвать функцию handleIntent? – Hiroto

+0

Метод вызывается, когда вы получаете сообщение от Firebase, но вы не можете переопределить, потому что метод объявлен окончательным. (Firebase 11.6.0) – Ettore

2

в соответствии с решением от T3H Exi я хотел бы опубликовать чистый код здесь. Просто поместите его в MyFirebaseMessagingService, и все работает отлично, если приложение находится в фоновом режиме. Вам нужно по крайней мере, для компиляции com.google.firebase: firebase-сообщений: 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); 
    } 
} 
+0

Я пытаюсь реализовать ваше решение, но метод handleIntent() является окончательным в моей версии SDK (SDK 27), поэтому я не могу переопределить его в своем сервисе ... – matdev

+0

Да, но это не мое ошибка или любая причина дать -1 !!! Очень высокомерный. Он работает до тех пор, пока firebase 11.4.2, а затем Google изменит его (сделал этот метод окончательным). Итак, теперь вы должны запрограммировать свое собственное решение с уведомлением. – Frank

+0

Я не дал -1, а +1! – matdev

3

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

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

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); 
     } 
     } 
    } 
    } 
} 

В Manifest.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> 

Испытано это решение в последней андроида версии 8.0. Благодаря

+0

whare использовать этот FirebaseBackgroundService class ?? @Nagendra Badiganti – Vijay

+0

где использовать этот код public class FirebaseBackgroundService extends WakefulBroadcastReceiver @Nagendra Badiganti – Vijay

+0

создайте класс обслуживания в своем пакете и зарегистрируйтесь в вашем manifest.xml. Убедитесь, что у вас есть фильтр для ваших уведомлений. Поскольку служба запускается для каждого уведомления GCM. –

1

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

Удалите полезную нагрузку уведомления полностью из запроса на сервер. Отправляйте только данные и обрабатывайте их в 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 
    } 
} 

ПРИМЕЧАНИЕ: увидеть эту строку в коде выше
«текст»: «John Doe делил контакт в группе Биржа контактов» в полезной нагрузке Data вы должны использовать параметр «текст» вместо параметров «тело» или «сообщение» для описания сообщения или того, что вы хотите использовать.

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()); 
      } 
     } 
    } 
Смежные вопросы