【问题标题】:Android - Trouble with service sending multiple local notificationsAndroid - 服务发送多个本地通知时出现问题
【发布时间】:2023-03-23 08:05:01
【问题描述】:

我继承了一个 Android 应用程序的代码库,但我在本地通知方面遇到了一个特别严重的问题。

这个想法是为将来安排的每个事件发送通知,同时考虑到用户希望在事件前多少分钟收到通知的提醒偏好。

一切正常,除了在第一次引发通知后,如果用户在事件开始之前打开应用程序,通知会再次引发。每次在(事件开始日期 - 提醒)和事件开始日期之间打开应用程序时都会发生这种情况。

我已经看过thisthis,但没有运气。 我读过使用服务可能会导致这个问题,有些人建议删除它,但我认为这是必要的,因为在应用关闭时也必须抛出通知。

目前代码结构如下:

编辑 - 更新 TabBarActivity 的描述

在 TabBarActivity 中,我有方法 scheduleTravelNotification 来安排 AlarmManager。 每次在本地数据库中添加新事件或更新现有事件时,都会执行此方法。 TabBarActivity 在 onCreate 和 onResume 方法中运行此方法。 TabBarActivity 也是通知的目标 - onclick 事件。

private static void scheduleTravelNotification(Context context, RouteItem routeItem) {

    long currentTime = System.currentTimeMillis();
    int alarmTimeBefore = routeItem.getAlarmTimeBefore();
    long alarmTime = routeItem.getStartTime() - (alarmTimeBefore * 1000 * 60);

    if(alarmTimeBefore < 0){
        return;
    }

    if(alarmTime < currentTime){
        return;
    }

    Intent actionOnClickIntent = new Intent(context, TravelNotificationReceiver.class);
    PendingIntent travelServiceIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis(), actionOnClickIntent, PendingIntent.FLAG_ONE_SHOT);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(alarmTime);
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), travelServiceIntent);

    Log.e("NEXT ALARM", "Time: " + String.valueOf(calendar.getTimeInMillis()));
}

这是 TravelNotificationReceiver.java(我应该使用 LocalBroadcastReceiver 而不是 BroadcastReceiver 吗?)

public class TravelNotificationReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("RECEIVER", "received TravelNotification request");
        Intent notificationIntent = new Intent(context, TravelNotificationService.class);
        context.startService(notificationIntent);
    }
}

TravelNotificationService.javaNotificationService.java 设置扩展为 type = "Travel"、flags = 0、title = "something" 和 text = "something else"。

public abstract class NotificationService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        sendNotification();
        return super.onStartCommand(intent, flags, startId);
    }

    public abstract String setNotificationType();
    public abstract int setNotificationFlags();
    public abstract String setNotificationTitle();
    public abstract String setNotificationText();

    /**
     * Executes all the logic to init the service, prepare and send the notification
     */
    private void sendNotification() {

        int flags = setNotificationFlags();
        String type = setNotificationType();

        NotificationHelper.logger(type, "Received request");

        // Setup notification manager, intent and pending intent
        NotificationManager manager = (NotificationManager) this.getApplicationContext().getSystemService(this.getApplicationContext().NOTIFICATION_SERVICE);
        Intent intentAction = new Intent(this.getApplicationContext(), TabBarActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, intentAction, flags);

        // Prepares notification
        String title = setNotificationTitle();
        String text = setNotificationText();
        Notification notification = NotificationHelper.buildNotification(getApplicationContext(), title, text, pendingIntent);

        // Effectively send the notification
        manager.notify(101, notification);

        NotificationHelper.logger(type, "Notified");
    }
}

编辑 - 这是 NotificationHelper.buildNotification 的代码

    public static Notification buildNotification(Context context, String title, String text, PendingIntent pendingIntent) {

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

        builder.setAutoCancel(true);
        builder.setContentText(text);
        builder.setContentTitle(title);
        builder.setContentIntent(pendingIntent);
        builder.setSmallIcon(R.mipmap.launcher);
        builder.setCategory(Notification.CATEGORY_MESSAGE);
        builder.setVisibility(Notification.VISIBILITY_PUBLIC);

        return builder.build();
    }

感谢您的回答!

编辑我也见过this,但没有可接受的答案,而this 的帖子提出了一些我认为它已经用 if(alarmTime 。

【问题讨论】:

  • 我面临同样的问题有什么建议吗? @LucioB
  • @AdeelTurk 我已经添加了我接受的答案。对我来说它工作得很好,我已经在 3 个不同的应用程序中采用了。希望对你也有帮助!
  • 感谢您的回复。就我而言,实际上这是我的错,我没有正确处理我的逻辑:)

标签: android android-intent notifications android-service android-broadcastreceiver


【解决方案1】:

这可能不是您的确切问题,但乍一看,您在 onStartCommand() 中发送通知,该通知本身可以在服务的生命周期内运行多次 - 例如,如果您发出服务启动在活动的 onCreate 中“盲目”命令,每次(重新)创建活动时都会发生这种情况。

你有几个选项来处理这个问题。

一种是创建一个布尔标志作为服务的属性,默认为false,并在发送通知之前检查它。如果为 false,则发送通知并将其设置为 true,如果已为 true,则不发送通知。

另一个是检查服务是否已经在运行,如果是,首先不要发送服务启动命令。这在任何地方都可能很乏味,并且违反 DRY,因此如果您采用这种方式,您可能希望在服务类中创建一个静态方法,该方法检查服务是否正在运行,如果没有则启动它,然后调用它明确启动服务。

【讨论】:

  • 我已尝试应用您的两个建议,但均未奏效。它一直具有相同的旧行为。第一个通知在确切的时间到达,然后如果我从通知中打开应用程序并重新关闭它,另一个通知会到达,我会一直这样循环。服务总是在启动,它似乎忽略了我的布尔标志(以及静态方法)。我还尝试添加对 alarmManager 和 pendingIntent 的检查,以查看它们是否已经在运行,以防万一删除它们,但再次没有运气。
【解决方案2】:

类似于 user3137702 的回答,您可以简单地拥有一个静态布尔值 APPISINFORGROUND,每次发送通知方法被点击时都会检查它,并从您的应用程序/活动代码中进行管理。

正如用户所说,由于应用程序/服务生命周期,您的 onStartCommand 方法可能会在奇怪的时间被调用。

或者检查您的接收器没有在您的代码的其他地方被调用。

【讨论】:

  • 试过但在我的情况下不起作用。不,在我的代码中没有调用接收器。
  • 那么发送第二个通知的时候会调用sendNotification()方法吗?如果是这个方法调用了第二个通知,你绝对可以用一点逻辑来阻止你的消息被发送,但最终这是一个补丁而不是真正的问题。您能否分享您的 TabBarActivity onCreate、onResume 和 onNewIntent 方法(或 TabBarActivity 中在用户按下通知时调用的任何其他方法),这可能是此活动方法中的某些内容导致了循环。,
  • 我已经添加了 TabBarActivity 的描述(不能分享完整的代码),一个奇怪的事情是第一次发送通知(所以预期的情况)它通过接收器传递(我已经在里面登录了)。其他时候(意外情况)这不会发生(至少我没有看到日志),它似乎直接进入了服务。
【解决方案3】:

可能是您的 NotificationHelper 类导致了问题。请分享本课程的代码。

一种想法可能是您的通知未设置为自动取消,请检查您是否在通知生成器中包含 setAutoCancel() 方法。

Notification notification = new Notification.Builder(this).setAutoCancel(true).build();

【讨论】:

  • 分享了相关代码。顺便说一句,它已经设置为自动取消。
  • 感谢您的检查!
【解决方案4】:

我找到了一种让它工作的方法,我发布了这个,因为这似乎是许多人使用thisthis 文章中建议的方法的问题。经过几个月的测试,我可以说我对我找到的解决方案非常满意。 关键是避免使用服务并依赖 AlarmScheduler 和 Receivers。

1) 通过添加以下行在清单中注册接收器:

<receiver android:name="<your path to>.AlarmReceiver" />

2) 在您的活动或逻辑中,您希望安排与对象相关的通知

private void scheduleNotification(MyObject myObject) {

    // Cal object to fix notification time
    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis(myObject.getTime());

    // Build intent and extras: pass id in case you need extra details in notification text
    // AlarmReceiver.class will receive the pending intent at specified time and handle in proper way
    Intent intent = new Intent(this, AlarmReceiver.class);
    intent.putExtra("OBJECT_ID", myObject.getId());

    // Schedule alarm
    // Get alarmManager system service
    AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(getBaseContext().ALARM_SERVICE);

    // Build pending intent (will trigger the alarm) passing the object id (must be int), and use PendingIntent.FLAG_UPDATE_CURRENT to replace existing intents with same id
    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), myObject.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);

    // Finally schedule the alarm
    alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}

3) 定义报警接收器

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        // Find object details by using objectId form intent extras (I use Realm but it can be your SQL db)
        MyObject myObject = RealmManager.MyObjectDealer.getObjectById(intent.getStringExtra("OBJECT_ID"), context);

        // Prepare notification title and text
        String title = myObject.getSubject();
        String text = myObject.getFullContent();

        // Prepare notification intent
        // HomeActivity is the class that will be opened when user clicks on notification
        Intent intentAction = new Intent(context, HomeActivity.class);

        // Same procedure for pendingNotification as in method of step2
        PendingIntent pendingNotificationIntent = PendingIntent.getActivity(context, myObject.getId(), intentAction, PendingIntent.FLAG_UPDATE_CURRENT);

        // Send notification (I have a static method in NotificationHelper)
        NotificationHelper.createAndSendNotification(context, title, text, pendingNotificationIntent);
    }
}

4) 定义 NotificationHelper

public class NotificationHelper {

    public static void createAndSendNotification(Context context, String title, String text, PendingIntent pendingNotificationIntent) {

        // Get notification system service
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);

        // Build notification defining each property like sound, icon and so on
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context);
        notificationBuilder.setContentTitle(title);
        notificationBuilder.setContentText(text);
        notificationBuilder.setSmallIcon(R.drawable.ic_done);
        notificationBuilder.setCategory(Notification.CATEGORY_MESSAGE);
        notificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
        notificationBuilder.setAutoCancel(true);
        notificationBuilder.setContentIntent(pendingNotificationIntent);
        notificationBuilder.setDefaults(Notification.DEFAULT_SOUND);
        notificationManager.notify(1001, notificationBuilder.build());
    }
}

此时它应该工作并在正确的时间安排/触发通知,当通知打开时,它只会在启动通知待定意图中声明的活动时出现。

还有一个问题,AlarmManager 在用户设备上有一个“易失性”存储,因此如果用户重新启动或关闭手机,您将丢失您之前安排的所有意图。 但幸运的是,还有一个解决方案:

5) 在清单顶部添加此使用权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

6) 在第 1 步添加的行的正下方注册引导接收器

<receiver android:name="<your path to>.BootReceiver" >
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

7) 定义引导接收器

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        // Do something very similar to AlarmReceiver but this time (at least in my case) since you have no source of intents loop through collection of items to understand if you need to schedule an alarm or not
        // The code is pretty similar to step 3 but repeated in a loop
    }
}

此时,您的应用应该能够安排/触发通知并恢复这些提醒,即使手机已关闭或重新启动。

希望这个解决方案对某人有所帮助!

【讨论】:

    猜你喜欢
    • 2021-02-01
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-23
    • 1970-01-01
    相关资源
    最近更新 更多