PAYATHON 2026

Android Studio 中第二个 notification 覆盖第一个

支付小周

结论

可以在 onReceive() 中使用 if 或 switch,但无法直接读取 PendingIntent 的 requestCode。101 和 102 只用于区分 PendingIntent,系统不会自动将它们写入接收到的 Intent。

可以按下面的方式处理:

  1. 创建用于触发 AlarmReceiver 的 PendingIntent 时,通过 Intent.setAction() 或 putExtra() 标记事件类型。
  2. 在 onReceive() 中读取标记,只调用对应的通知方法。
  3. 为两个通知设置不同的 notification ID。当前两处都使用 notify(1, notification),第二个通知会更新或覆盖第一个。
  4. Android 8.0 及以上版本需要在调用 build() 前设置 channel ID。

PendingIntent 的 request code、notification ID 和 notification channel ID 是三种不同的标识,不能混用。

推荐实现

先定义两个广播 action 和两个 notification ID:

public class AlarmReceiver extends BroadcastReceiver {

    public static final String ACTION_BILL_DUE =
            "com.example.app.ACTION_BILL_DUE";

    public static final String ACTION_PAYMENT_SUCCESS =
            "com.example.app.ACTION_PAYMENT_SUCCESS";

    private static final String CHANNEL_ID = "bills.channelId";

    private static final int NOTIFICATION_ID_BILL_DUE = 101;
    private static final int NOTIFICATION_ID_PAYMENT_SUCCESS = 102;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null) {
            return;
        }

        String action = intent.getAction();

        if (ACTION_BILL_DUE.equals(action)) {
            userHasBill(context);
        } else if (ACTION_PAYMENT_SUCCESS.equals(action)) {
            userPaymentIsSuccessful(context);
        }
    }

    // ...
}

设置提醒时,将不同的 action 写入发送给 AlarmReceiver 的 Intent。

账单到期提醒:

Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction(AlarmReceiver.ACTION_BILL_DUE);

PendingIntent billAlarmIntent = PendingIntent.getBroadcast(
        context,
        101,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

alarmManager.setExactAndAllowWhileIdle(
        AlarmManager.RTC_WAKEUP,
        billDueTimeMillis,
        billAlarmIntent
);

付款成功通知:

Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction(AlarmReceiver.ACTION_PAYMENT_SUCCESS);

PendingIntent paymentIntent = PendingIntent.getBroadcast(
        context,
        102,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

context.sendBroadcast(intent);

如果付款成功事件也需要由 AlarmManager 定时触发,可以同样使用 PendingIntent.getBroadcast() 和相应的闹钟 API。

创建通知

两种通知的大部分逻辑相同,可以合并到一个公共方法中:

private void userHasBill(Context context) {
    showNotification(
            context,
            NOTIFICATION_ID_BILL_DUE,
            101,
            "Bill Reminder!",
            "Just a reminder that you have a bill due!"
    );
}

private void userPaymentIsSuccessful(Context context) {
    showNotification(
            context,
            NOTIFICATION_ID_PAYMENT_SUCCESS,
            102,
            "Payment successful!",
            "Your payment was successful!"
    );
}

private void showNotification(
        Context context,
        int notificationId,
        int contentRequestCode,
        String title,
        String message
) {
    NotificationManager notificationManager =
            (NotificationManager) context.getSystemService(
                    Context.NOTIFICATION_SERVICE
            );

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(
                CHANNEL_ID,
                "Bills",
                NotificationManager.IMPORTANCE_DEFAULT
        );
        notificationManager.createNotificationChannel(channel);
    }

    Intent notificationIntent =
            new Intent(context, MainMenuActivity.class);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    stackBuilder.addParentStack(MainMenuActivity.class);
    stackBuilder.addNextIntent(notificationIntent);

    PendingIntent contentIntent = stackBuilder.getPendingIntent(
            contentRequestCode,
            PendingIntent.FLAG_UPDATE_CURRENT
                    | PendingIntent.FLAG_IMMUTABLE
    );

    NotificationCompat.Builder builder =
            new NotificationCompat.Builder(context, CHANNEL_ID)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentTitle(title)
                    .setContentText(message)
                    .setContentIntent(contentIntent)
                    .setAutoCancel(true)
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);

    notificationManager.notify(notificationId, builder.build());
}

这里为两种通知设置了不同的 notification ID:

notificationManager.notify(101, billNotification);
notificationManager.notify(102, paymentNotification);

这样两个通知可以同时存在。如果希望新的同类通知更新旧通知,可以让每种通知继续使用固定 ID。例如,所有账单提醒都使用 101,所有付款成功通知都使用 102。

为什么原代码总显示第二个通知

原来的 onReceive() 每次收到广播后,都会依次执行:

userHasBill(context, intent);
userPaymentIsSuccessful(context, intent);

两个方法最后又都调用了:

notificationManager.notify(1, notification);

在 NotificationManager 看来,它们的 notification ID 都是 1。第一个通知发布后,第二个通知立即使用同一个 ID 更新它,因此最终只能看到付款成功通知。

修改 channel ID 无法解决这个问题。Channel 控制的是通知类别的声音、振动和重要程度,不是单条通知的唯一标识。

也可以使用 Extra

如果不想使用 action,也可以在触发广播时写入事件类型:

public static final String EXTRA_NOTIFICATION_TYPE = "notification_type";
public static final int TYPE_BILL_DUE = 101;
public static final int TYPE_PAYMENT_SUCCESS = 102;

Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra(EXTRA_NOTIFICATION_TYPE, TYPE_BILL_DUE);

接收广播时读取并判断这个值:

@Override
public void onReceive(Context context, Intent intent) {
    int type = intent.getIntExtra(EXTRA_NOTIFICATION_TYPE, -1);

    switch (type) {
        case TYPE_BILL_DUE:
            userHasBill(context);
            break;

        case TYPE_PAYMENT_SUCCESS:
            userPaymentIsSuccessful(context);
            break;

        default:
            break;
    }
}

需要注意的是,Intent 的 extras 不参与 PendingIntent 的身份匹配。如果两个广播只有 extras 不同,仍应使用不同的 request code,或者设置不同的 action。

其他注意事项

  • setChannelId() 必须在 build() 之前调用。原代码先执行 build(),之后才调用 builder.setChannelId(),这不会修改已经构建好的 Notification。
  • 建议使用 NotificationCompat.Builder,以便统一处理不同的 Android 版本。
  • Android 12 及以上版本通常要求为 PendingIntent 明确指定 FLAG_IMMUTABLE 或 FLAG_MUTABLE。这里不需要修改其内容,因此使用 FLAG_IMMUTABLE。
  • Android 13 及以上版本在发布通知前还需要获得 POST_NOTIFICATIONS 权限,否则即使接收器正常执行,通知也可能无法显示。
  • TaskStackBuilder.addParentStack() 通常应传入最终打开 Activity 的父级关系,例如 MainMenuActivity.class。具体配置取决于 AndroidManifest.xml 中定义的 Activity 层级。

备注:内容仅供参考。