使用新的Firebase Cloud Messaging系统的通知图标

162

昨天Google在Google I/O上介绍了基于新Firebase的新通知系统。我用Github上的示例尝试了这个新的FCM(Firebase Cloud Messaging)。

尽管我声明了特定的可绘制对象,但通知的图标始终是ic_launcher

为什么?下面是处理消息的官方代码

public class AppFirebaseMessagingService extends FirebaseMessagingService {

    /**
     * Called when message is received.
     *
     * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
     */
    // [START receive_message]
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        // 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.
        sendNotification(remoteMessage);
    }
    // [END receive_message]

    /**
     * Create and show a simple notification containing the received FCM message.
     *
     * @param remoteMessage FCM RemoteMessage received.
     */
    private void sendNotification(RemoteMessage remoteMessage) {

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

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

// this is a my insertion looking for a solution
        int icon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.myicon: R.mipmap.myicon;
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(icon)
                .setContentTitle(remoteMessage.getFrom())
                .setContentText(remoteMessage.getNotification().getBody())
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }

}

Firebase与您创建通知的方式无关,请提供您所看到的图像。 - tyczj
1
这段代码直接来自Firebase,sendNotification()方法对于任何通知都是完全相同的。这段代码在GCM上运行良好,但在FCM上不行。它始终保持为ic_launcher,在使用新的Web界面发送消息时。 - marco
@shinypenguin,你说得对!我没有注意到其他参数也不能自定义。是的,正如你所说,当应用程序在前台时图标可以工作。 - marco
1
http://codingaffairs.blogspot.com/2016/06/firebase-cloud-messaging-push.html - Developine
这可能是因为Firebase控制台发送了另一种结构的通知,而不是人们用于操作Android通知的数据...您是从控制台发送吗? - superUser
显示剩余2条评论
11个回答

306

很遗憾,这是 Firebase Notifications SDK 9.0.0-9.6.1 的限制。当应用程序在后台运行时,控制台发送的消息使用清单中的启动器图标(并具有必要的 Android 着色)。

但是,在 SDK 9.8.0 中,您可以覆盖默认设置! 在 AndroidManifest.xml 中,您可以设置以下字段来自定义图标和颜色:

<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/google_blue" />
请确认以下翻译是否符合要求:

请注意:如果应用程序在前台(或发送数据消息),您可以完全使用自己的逻辑来自定义显示。如果从HTTP/XMPP API发送消息,您还可以始终自定义图标。


2
@Ian Barber:Google 是否有计划在不久的将来更改这种行为? - skylve
1
团队已经意识到这个问题,并正在努力修复。 - Arthur Thompson
1
今天是一个很好的调试日子!今天发布了新的SDK有一个重载功能,我已经更新了我的答案。 - Ian Barber
7
啊,我用9.8.0版本成功了。为了帮助他人,记得你的状态栏图标必须符合要求:https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html。我的没有符合要求,因此 Firebase 只能使用默认的白色方块而不是在清单中指定的图标。 - zaifrun
5
如果你尝试过这个方法并发现图标比其他通知小,那么请确保使用的是矢量图(而不是png格式的图片)。这对我解决了问题。 - riper
显示剩余21条评论

45

使用服务器实现向客户端发送消息,使用数据类型的消息而不是通知类型的消息。

这将帮助您在应用程序处于前台或后台时都能收到来自onMessageReceived的回调,然后您就可以生成自定义通知。


2
这也是 Firebase 文档中建议的解决方案,如果您依赖于数据推送而不是通知,则可以以任何喜欢的方式显示通知。 - racs
1
同意。这应该被标记为正确答案。或者是下面的我的回答 :) - Developine
4
不行,对于需要保持与其他客户端/旧版本兼容并期望通知的人来说,这是不可行的。 - Gabor
我们的生产应用程序已经期望推送通知类型,我们已经扩展了(复杂的)后端组件以添加新客户的数据。但是我们不能删除对旧版本应用程序的支持。 - Gabor
我想指出你的“解决方案”有时需要后端更改,而这并不总是可行的。 - Gabor

13

他们目前正在解决这个问题:https://github.com/firebase/quickstart-android/issues/4

当您从Firebase控制台发送通知时,默认使用您的应用程序图标,并且在通知栏中显示该图标为纯白色。

如果您不满意这个结果,您应该实现FirebaseMessagingService并在接收消息时手动创建通知。我们正在努力改进它,但目前这是唯一的方法。

编辑:使用SDK 9.8.0添加到AndroidManifest.xml

<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/my_favorite_pic"/>

如何在Marshmallow中显示自定义图标通知栏 - Harsha

6

我的解决方案与ATom的类似,但更容易实现。你不需要完全遮蔽FirebaseMessagingService创建一个类,只需重写接收Intent的方法(至少在版本9.6.1中是公共的),并从extras中获取要显示的信息。 "hacky"部分是方法名称确实被混淆,并且每次更新Firebase sdk到新版本时都会更改,但是你可以通过使用Android Studio检查FirebaseMessagingService并查找以Intent作为唯一参数的公共方法来快速查找它。在版本9.6.1中,它被称为zzm。以下是我的服务外观:

public class MyNotificationService extends FirebaseMessagingService {

    public void onMessageReceived(RemoteMessage remoteMessage) {
        // do nothing
    }

    @Override
    public void zzm(Intent intent) {
        Intent launchIntent = new Intent(this, SplashScreenActivity.class);
        launchIntent.setAction(Intent.ACTION_MAIN);
        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* R    equest code */, launchIntent,
                PendingIntent.FLAG_ONE_SHOT);
        Bitmap rawBitmap = BitmapFactory.decodeResource(getResources(),
                R.mipmap.ic_launcher);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_notification)
                .setLargeIcon(rawBitmap)
                .setContentTitle(intent.getStringExtra("gcm.notification.title"))
                .setContentText(intent.getStringExtra("gcm.notification.body"))
                .setAutoCancel(true)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager)     getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }
}

gcm.notification.title键名对于所有版本都是100%安全的吗? - pedroooo

5

如果您的应用程序在后台运行,通知图标将设置在onMessageReceive方法中,但如果您的应用程序在前台运行,通知图标将是您在清单文件中定义的那个。

enter image description here


4

只需将targetSdkVersion设置为19。通知图标将会变色。 然后等待Firebase解决此问题。


7
哇,副作用清单在哪里? - Eugen Pechanec
@EugenPechanec 是的,权衡之处在于你可能无法使用某些20+可用的API。 - benleung

3

还有一种不太美观但可行的方式。反编译FirebaseMessagingService.class并修改其行为。然后将类放在你的应用程序中正确的包中,并使用dex代替消息库中的类。这很容易且有效。

方法如下:

private void zzo(Intent intent) {
    Bundle bundle = intent.getExtras();
    bundle.remove("android.support.content.wakelockid");
    if (zza.zzac(bundle)) {  // true if msg is notification sent from FirebaseConsole
        if (!zza.zzdc((Context)this)) { // true if app is on foreground
            zza.zzer((Context)this).zzas(bundle); // create notification
            return;
        }
        // parse notification data to allow use it in onMessageReceived whe app is on foreground
        if (FirebaseMessagingService.zzav(bundle)) {
            zzb.zzo((Context)this, intent);
        }
    }
    this.onMessageReceived(new RemoteMessage(bundle));
}

这段代码来自版本9.4.0,由于混淆,不同版本的方法名称可能会有所不同。


3

请编写这段内容

<meta-data 
         android:name="com.google.firebase.messaging.default_notification_icon"
         android:resource="@drawable/ic_notification" />

请将<application.....>放置在右下方。

enter image description here


3
对我不起作用 - JanuszO

2

我正在使用 FCM 控制台和 HTTP/JSON 触发我的通知...结果相同。

我可以处理标题、完整消息,但图标始终是默认的白色圆形:

通知截图

而不是代码中自定义图标(setSmallIcon 或 setSmallIcon)或应用程序的默认图标:

 Intent intent = new Intent(this, MainActivity.class);
    // use System.currentTimeMillis() to have a unique ID for the pending intent
    PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);

    if (Build.VERSION.SDK_INT < 16) {
        Notification n  = new Notification.Builder(this)
                .setContentTitle(messageTitle)
                .setContentText(messageBody)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pIntent)
                .setAutoCancel(true).getNotification();
        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //notificationManager.notify(0, n);
        notificationManager.notify(id, n);
    } else {
        Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);

        Notification n  = new Notification.Builder(this)
                .setContentTitle(messageTitle)
                .setContentText(messageBody)
                .setSmallIcon(R.drawable.ic_stat_ic_notification)
                .setLargeIcon(bm)
                .setContentIntent(pIntent)
                .setAutoCancel(true).build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //notificationManager.notify(0, n);
        notificationManager.notify(id, n);
    }

1
明白了!我的<service>标签在AndroidManifest.xml文件中超出了<application>标签。这里有答案:https://dev59.com/1VoU5IYBdhLWcg3wi3WW#37352142 - Gonzalo
1
仅在前台应用程序运行时有效...在后台仍然会出现相同的先前行为。 - Gonzalo
当应用程序在前台时,自定义通知图标正常显示和工作,但在后台时出现白色方形图标,请帮忙解决。 - Harsha

1
将此内容添加到应用标签内的活动标签外:

add this outside the activity tag inside the application tag :

<meta-data
   android:name="com.google.firebase.messaging.default_notification_icon"
   android:resource="@mipmap/notification"
/>

请确保将通知文件复制到所有带有您自定义分辨率的目录 mipmap-* 中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接