Android-服务发送多个本地通知时出现问题 [英] Android - Trouble with service sending multiple local notifications

查看:61
本文介绍了Android-服务发送多个本地通知时出现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经继承了Android应用程序的代码库,但本地通知遇到了特别困难的问题.

I've inherited a code base for an Android app and I'm facing a particularly though problem with local notifications.

想法是针对将来安排的每个事件发送通知,同时还要考虑提醒用户在事件发生前要通知用户多少分钟的偏好.

The idea is to send a notification for each event which is scheduled in the future, considering also the reminder preference on how many minutes before the event the user wants to be notified.

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

Everything works just fine, except that after the notification is thrown for the first time, if the user opens the app before the event starts, the notification gets thrown another time. This happens every time the app is opened between (event start date - reminder) and event start date.

我已经看过 this

I've already gave a look at this and also this with no luck. I've read that using a service may cause exactly this problem and some suggest to remove it but I think this is needed since the notification must be thrown also when the app is closed.

当前代码的结构如下:

编辑-更新了TabBarActivity的描述

在TabBarActivity内部,我具有用于计划AlarmManager的方法 scheduleTravelNotification . 每当在本地数据库上添加新事件,或者已更新现有事件时,都会执行此方法. TabBarActivity在onCreate和onResume方法内运行此方法. TabBarActivity也是通知的目标-onclick事件.

Inside TabBarActivity I have the method scheduleTravelNotification that schedules the AlarmManager. This method is executed everytime there is a new event to be added on local database, or if an existing event have been updated. The TabBarActivity runs this method inside the onCreate and onResume methods. TabBarActivity is also the target of the notification - onclick event.

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吗?)

This is TravelNotificationReceiver.java (should I use LocalBroadcastReceiver instead of 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.java NotificationService.java 设置扩展为type ="Travel",标志= 0,title ="something"和text ="something else"./p>

TravelNotificationService.java extends NotificationService.java setting as type = "Travel", flags = 0, title = "something" and 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();
    }

谢谢您的回答!

编辑我也看到了,但是没有接受的答案,而帖子提出了一些我认为已经管理的内容与if(alarmTime< currentTime){return; } in scheduleTravelNotification .

Edit I've seen also this but has no accepted answers, while this post suggest something that I think it's already managed with if(alarmTime < currentTime){ return; } in scheduleTravelNotification.

推荐答案

我已经找到一种使之起作用的方法,我正在发布此消息,因为使用文章.经过几个月的测试,我可以说我对找到的解决方案感到非常满意. 关键是要避免使用服务,而要依赖AlarmScheduler和Receivers.

I've found a way to make it work, I'm posting this since it seems to be a problem of many people using the approach suggested in this and this articles. After months of testing I can say I'm pretty satisfied with the solution I've found. The key is to avoid usage of Services and rely on AlarmScheduler and Receivers.

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

1) Register the receiver in your manifest by adding this line:

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

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

2) In your activity or logic at some point you want to schedule a notification related to an object

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)定义AlarmReceiver

3) Define AlarmReceiver

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

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

这时它应该在正确的时间工作并安排/触发通知,并且当打开通知时,它将仅在启动通知待定意图中声明的活动后才会出现.

At this point it should work and schedule / trigger notification at the right time, and when notification is opened it will appear only once starting the activity declared in notification pending intent.

仍然存在问题,AlarmManager在用户设备上具有易失性"存储,因此,如果用户重新启动或关闭电话,您将丢失先前计划的所有意图. 但幸运的是,还有一个解决方案:

There is still a problem, AlarmManager have a "volatile" storage on user device, so if user reboots or switch off the phone you will lose all intents that you previously scheduled. But fortunately there is also a solution for that:

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

5) Add at top of your manifest this uses permission

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

6)在第1步添加的行的正下方,注册启动接收器

6) Right below the line added at step 1 register the boot receiver

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

7)定义BootReceiver

7) Define the BootReceiver

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

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

At this point your app should be able to schedule / trigger notification and restores those reminders even if the phone is switched off or rebooted.

希望此解决方案将对某人有所帮助!

Hope this solution will help someone!

这篇关于Android-服务发送多个本地通知时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆