当应用程序在后台

时间:2016-05-21 03:06:28

标签: android firebase firebase-cloud-messaging

我正在使用Firebase,并在应用程序处于后台时测试从我的服务器向我的应用发送通知。通知成功发送,甚至出现在设备的通知中心,但是当通知出现或者即使我点击它时,我的FCMessagingService中的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"));
        }
    }
}

27 个答案:

答案 0 :(得分:112)

这是按预期工作的,只有当您的应用位于前台时,通知消息才会传递到您的onMessageReceived回调。如果您的应用处于后台或已关闭,则通知中心会显示通知消息,并且该消息中的任何数据都会传递给因用户点击该消息而启动的意图通知。

您可以指定click_action以指示在用户点击通知时应启动的意图。如果未指定click_action,则使用主要活动。

启动意图后,您可以使用

getIntent().getExtras();

检索包含与通知消息一起发送的任何数据的Set。

有关通知消息的更多信息,请参阅docs

答案 1 :(得分:88)

从您的服务器请求中完全删除notification字段。仅发送 data 并在onMessageReceived()处理,否则当应用处于后台或被杀时,您的onMessageReceived()将不会被触发。

不要忘记在通知请求中加入"priority": "high"字段。根据文档:数据消息以正常优先级发送,因此它们不会立即到达;这也可能是问题所在。

以下是我从服务器发送的内容

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

因此,您可以像onMessageReceived(RemoteMessage message)这样收到您的数据....让我说我必须得到身份

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

答案 2 :(得分:61)

此方法handleIntent()已被折旧,因此可以按如下方式处理通知:

  1. 前景状态:通知的点击将转到您在提供通知时提供的待处理Intent的活动,因为它通常使用通知的数据有效负载创建。

  2. 后台/杀戮状态 - 此处,系统本身会根据通知有效负载创建通知,点击该通知会将您带到应用程序的启动器活动,您可以在任何生活中轻松获取Intent数据 - 循环方法。

答案 3 :(得分:25)

我遇到了同样的问题。使用“数据”消息更容易。而不是通知'。数据消息始终加载类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());
    }

答案 4 :(得分:21)

根据Firebase云消息传递文档 - 如果Activity在前台,则将调用onMessageReceived。如果活动处于后台或已关闭,则通知中心会显示应用启动器活动的通知消息。 如果您的应用处于后台,可以通过调用rest service api for firebase messaging来点击通知来调用您的自定义活动:

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>

答案 5 :(得分:21)

这里有关于firebase消息的更清晰的概念。我是从他们的支持团队那里找到的。

Firebase有三种消息类型

通知消息:通知消息适用于后台或前台。当应用程序处于后台时,通知消息将传递到系统托盘。如果应用位于前台,则消息由onMessageReceived()didReceiveRemoteNotification回调处理。这些基本上就是所谓的显示消息。

数据消息:在Android平台上,数据消息可以在后台和前台上运行。数据消息将由onMessageReceived()处理。这里的平台特定说明如下:在Android上,可以在用于启动活动的Intent中检索数据有效负载。要详细说明,如果您有"click_action":"launch_Activity_1",则只能通过getIntent()Activity_1检索此意图。

包含通知和数据有效负载的邮件:在后台,应用会在通知托盘中收到通知有效内容,并仅在用户点击通知时处理数据有效内容。在前台时,您的应用会收到一个消息对象,其中包含两个可用的有效负载。其次,click_action参数通常用于通知有效负载而不是数据有效负载。如果在数据有效内容中使用,则此参数将被视为自定义键值对,因此您需要实现自定义逻辑才能使其按预期工作。

另外,我建议您使用onMessageReceived方法(请参阅数据消息)来提取数据包。根据您的逻辑,我检查了bundle对象,但没有找到预期的数据内容。以下是对可能提供更清晰的类似案例的引用。

有关详细信息,请访问我的this threadthis thread

答案 6 :(得分:11)

如果应用处于后台模式或处于非活动状态(已杀死),而您点击 通知,则应检查LaunchScreen中的有效负载(在我的情况下,启动屏幕)是MainActivity.java)。

所以在 onCreate 上的 MainActivity.java 中检查附加内容

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

答案 7 :(得分:8)

我遇到了同样的问题。如果应用程序是前台 - 它会触发我的后台服务,我可以根据通知类型更新我的数据库。 但是,应用程序进入后台 - 默认通知服务将注意向用户显示通知。

以下是我在后台识别应用并触发后台服务的解决方案,

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>

在最新的Android 8.0版本中测试了此解决方案。感谢

答案 8 :(得分:7)

覆盖handleIntent的{​​{1}}方法适用于我。

这里是 C#(Xamarin)

中的代码
FirebaseMessageService

那就是 Java

中的代码
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);
    }
}

答案 9 :(得分:5)

默认情况下,当您的应用处于后台并且您点击通知时,您应用中的Launcher Activity将会启动,如果您的通知中有任何数据部分,您可以在以下相同的活动中处理它,< / p>

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

答案 10 :(得分:3)

如果应用程序在后台默认处理通知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
  }
}

注意:请参阅上面的代码中的这一行 &#34;文字&#34;:&#34; John Doe在小组中联系了Exchange&#34; 在数据有效负载中,你应该使用&#34; text&#34;参数而不是&#34; body&#34;或&#34;消息&#34;消息描述的参数或您想要使用的任何文本。

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

答案 11 :(得分:2)

试试这个:

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

答案 12 :(得分:2)

只需在MainActivity的onCreate方法中调用它:

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

答案 13 :(得分:2)

根据t3h exi的解决方案,我想在这里发布干净的代码。只需将其放入MyFirebaseMessagingService,如果应用程序处于后台模式,一切正常。您至少需要编译com.google.firebase:firebase-messaging: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);
    }
}

答案 14 :(得分:1)

我遇到了类似的问题。根据本页中提到的答案和参考资料,以下是我如何使用以下方法解决问题的两分钱:

我之前的消息格式如下:

    {
  "notification": {
    "title": "AppName",
    "sound": null,
    "body": "Hey!YouhaveaMessage"
  },
  "data": {
    "param1": null,
    "param2": [
      238
    ],
    "id": 1
  },
  "to": "--the device push token here--"
}

我将消息格式修改为以下内容:

    {
  "data": {
    "title": "AppName",
    "body": "Hey! You have a message",
    "param1": null,
    "param2": [
      238
    ],
    "id": 1
  },
  "priority": "high",
  "to": " — device push token here — "
}

然后我从“数据”有效负载本身检索标题、正文和所有参数。这解决了问题,然后即使应用程序在后台,我也可以获得 OnMessageReceived 回调。 我写了一篇博文解释了同样的问题,你可以找到它here

答案 15 :(得分:1)

根据以下情况调用

onMessageReceived(RemoteMessage remoteMessage)方法。

  • FCM响应,带有通知数据块:

{ “ ”:“设备令牌列表”, “ 通知”:{ “正文”:“您的通知的正文”, “ title”:“通知标题” }, “ 数据”:{ “ body”:“您的数据通知主体”, “ title”:“标题中的通知标题”, “ key_1”:“ key_1的值”, “ image_url”:“ www.abc.com/xyz.jpeg”, “ key_2”:“ key_2的值” } }

  1. 前景中的应用
调用

onMessageReceived(RemoteMessage remoteMessage),在通知栏中显示LargeIcon和BigPicture。我们可以从通知数据

中读取内容
  1. 后台应用:

onMessageReceived(RemoteMessage remoteMessage)未调用,系统托盘将接收消息并从 notification 块读取正文和标题,并在通知栏中显示默认消息和标题。

  • FCM响应,仅包含数据块:

在这种情况下,请从json中删除 notofocation

{ “ ”:“设备令牌列表”, “ 数据”:{ “ body”:“您的数据通知主体”, “ title”:“标题中的通知标题”, “ key_1”:“ key_1的值”, “ image_url”:“ www.abc.com/xyz.jpeg”, “ key_2”:“ key_2的值” } }

  1. 前景中的应用
调用

onMessageReceived(RemoteMessage remoteMessage),在通知栏中显示LargeIcon和BigPicture。我们可以从通知数据

中读取内容
  1. 后台应用:

onMessageReceived(RemoteMessage remoteMessage)被调用,由于响应中没有 notification 键,系统任务栏将不会接收到该消息。在通知栏中显示LargeIcon和BigPicture

代码

 private void sendNotification(Bitmap bitmap,  String title, String 
    message, PendingIntent resultPendingIntent) {

    NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
    style.bigPicture(bitmap);

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

    NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    String NOTIFICATION_CHANNEL_ID = mContext.getString(R.string.default_notification_channel_id);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_HIGH);

        notificationManager.createNotificationChannel(notificationChannel);
    }
    Bitmap iconLarge = BitmapFactory.decodeResource(mContext.getResources(),
            R.drawable.mdmlogo);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
            .setSmallIcon(R.drawable.mdmlogo)
            .setContentTitle(title)
            .setAutoCancel(true)
            .setSound(defaultSound)
            .setContentText(message)
            .setContentIntent(resultPendingIntent)
            .setStyle(style)
            .setLargeIcon(iconLarge)
            .setWhen(System.currentTimeMillis())
            .setPriority(Notification.PRIORITY_MAX)
            .setChannelId(NOTIFICATION_CHANNEL_ID);


    notificationManager.notify(1, notificationBuilder.build());


}

参考链接:

https://firebase.google.com/docs/cloud-messaging/android/receive

答案 16 :(得分:1)

如果您的问题与显示大图像有关,即您是否使用来自firebase控制台的图像发送推送通知,并且仅当应用程序位于前台时才显示图像。此问题的解决方案是发送仅包含数据字段的推送消息。像这样:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ApodViewHolder> {

private final Context mContext;
private final ChildEventListener mChildEventListener;
private final Query mDatabaseReference;
private final List<String> apodListIds = new ArrayList<>();
private final List<Apod> apodList = new ArrayList<>();

public RecyclerAdapter(final Context context, Query ref) {
    mContext = context;
    mDatabaseReference = ref;


    ChildEventListener childEventListener = new ChildEventListener() {


        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            int oldListSize = getItemCount();
            Apod apod = dataSnapshot.getValue(Apod.class);

            //Add data and IDs to the list
            apodListIds.add(dataSnapshot.getKey());
            apodList.add(apod);

            //Update the RecyclerView
            notifyItemInserted(oldListSize - getItemCount() - 1);
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {
            String apodKey = dataSnapshot.getKey();
            int apodIndex = apodListIds.indexOf(apodKey);

            if (apodIndex > -1) {

                // Remove data and IDs from the list
                apodListIds.remove(apodIndex);
                apodList.remove(apodIndex);

                // Update the RecyclerView
                notifyDataSetChanged();
            }
        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }

    };
    ref.addChildEventListener(childEventListener);
    mChildEventListener = childEventListener;

 }

   @Override
    public int getItemCount() {
    return apodList.size();
}
    public void cleanupListener() {
    if (mChildEventListener != null) {
        mDatabaseReference.removeEventListener(mChildEventListener);
    }
  }

}

答案 17 :(得分:1)

我正在使用的后端是使用通知消息而不是数据消息。所以在阅读完所有答案之后,我试图从发起活动的意图包中检索额外内容。 但无论我试图从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

Reference

答案 18 :(得分:1)

值得强调的一点是,您必须使用数据消息 - 仅数据键 - 即使应用程序处于后台,也可以调用onMessageReceived处理程序。您的有效负载中不应该有任何其他通知消息密钥,否则如果应用程序处于后台,则处理程序不会被触发。

这里提到了(但在FCM文档中没有这么强调):

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

  

使用您的应用服务器和FCM服务器API:仅设置数据密钥。可   要么是可折叠的,要么是不可折叠的。

答案 19 :(得分:0)

有两种类型的消息:通知消息和数据消息。 如果只发送数据消息,则消息字符串中没有通知对象。当你的应用程序在后台运行时会调用它。

答案 20 :(得分:0)

当收到消息并且您的应用程序处于后台时,通知将发送到主要活动的额外目的。

您可以在主要活动的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);
        }

}

答案 21 :(得分:0)

Firebase 推送通知有 2种类型

1-通知消息(显示消息)-> -1.1如果选择此变体,则当app位于 Background 中时,操作系统会自行创建一个通知,并将在intent中传递数据。然后,它的由客户端来处理这个数据。

- 1.2如果应用程序是在<强>前景,然后将通过被接收的通知callback-functionFirebaseMessagingService,它是由客户端来处理它<。 / p>

2-数据消息(最多4K数据) - >这些消息被用来唯一的数据发送给客户端(静默)和它是由客户端来处理它对于这两种情况下的背景/前景经由回调函数{ {1}}

这是按照官方的文档:https://firebase.google.com/docs/cloud-messaging/concept-options

答案 22 :(得分:0)

我遇到了同样的问题,并对此进行了更多的挖掘。当应用程序在后台时,会向系统托盘发送通知消息,但数据消息会发送到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

为确保您发送的邮件,文档说“使用您的应用服务器和FCM服务器API:仅设置数据密钥。可以是可折叠的,也可以是不可折叠的。”<登记/> 见https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

答案 23 :(得分:0)

检查@Mahesh Kavathiya的答案。就我而言,在服务器代码中只有这样:

{
"notification": {
  "body": "here is body",
  "title": "Title",
 },
 "to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}

您需要更改为:

{
 "data": {
  "body": "here is body",
  "title": "Title",
  "click_action": "YOUR_ACTION"
 },
"notification": {
  "body": "here is body",
  "title": "Title"
 },
 "to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}

然后,如果应用程序在后台运行,则默认的活动意图额外会得到“数据”

祝你好运!

答案 24 :(得分:0)

您可以在后台的主活动中尝试此操作

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

检查following project作为参考

答案 25 :(得分:0)

我遇到了这个问题(如果应用处于后台或已关闭,应用无法通知点击,并且问题在通知正文中无效click_action,请尝试删除或更改为有效的东西。

答案 26 :(得分:-1)

只需覆盖FirebaseMessagingService的OnCreate方法即可。当您的应用处于后台时调用它:

pod 'Firebase/Core'