AlarmManager alarm does not trigger after the phon

2019-05-24 08:46发布

问题:

In my app the user joins a plan, then the next day at noon there is an alarm notification. Here is my code:

First, I set an alarm in AlarmManager like so:

//set alarm to the next day 12:00 noon of the join date
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");  
try {  
    alarm_date = format.parse(join_date);
} catch (ParseException e) {  
    e.printStackTrace();  
}

GregorianCalendar calender = new GregorianCalendar();
calender.setTime(alarm_date);
calender.add(Calendar.DATE, 1);
calender.add(Calendar.HOUR_OF_DAY, 12);
//calender.add(Calendar.HOUR_OF_DAY, 14); //temp testing data
//calender.add(Calendar.MINUTE, 43);

AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctx, AlarmReceiver.class);
am.set(AlarmManager.RTC_WAKEUP, calender.getTimeInMillis(), PendingIntent.getBroadcast(ctx, 1, i, PendingIntent.FLAG_UPDATE_CURRENT));

Then, at the scheduled time, it triggers the receiver like so:

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, AlarmService.class);
        context.startService(service);
    }
}

Finally, it calls a service to either show notification or bring the app to the front if it is in the background. Here is the code for that:

public class AlarmService extends Service {
    private Context ctx;
    private MyApp gs;
    private SharedPreferences prefs;
    NotificationManager notificationManager;
    Notification myNotification;
    private String joinPlanID;
    private String joinPlanDate;
    private int period;

    public AlarmService() {
    }

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

    public void checkPlanPeriod(String planID) {
        if (joinPlanID.equals("1"))
            period = 15;
        else if (joinPlanID.equals("2"))
            period = 30;
        else if (joinPlanID.equals("3"))
            period = 45;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        ctx = getApplicationContext();
        gs = (MyApp) getApplication();
        prefs = PreferenceManager.getDefaultSharedPreferences(ctx);

        if (prefs.getString("joinPlanID", null) != null) {
            Date alarmDate = null;
            joinPlanID = prefs.getString("joinPlanID", null);
            joinPlanDate = prefs.getString("joinPlanDate", null);
            checkPlanPeriod(joinPlanID);

            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");  
            try {  
                alarmDate = format.parse(joinPlanDate);
            } catch (ParseException e) {  
                e.printStackTrace();  
            }

            GregorianCalendar planCalendar = new GregorianCalendar();
            planCalendar.setTime(alarmDate);
            planCalendar.add(Calendar.DATE, period);

            Calendar now = Calendar.getInstance();
            now.add(Calendar.DATE, 1);

            Calendar tomorrowAlarm = Calendar.getInstance();
            tomorrowAlarm.set(Calendar.YEAR, now.get(Calendar.YEAR));
            tomorrowAlarm.set(Calendar.MONTH, now.get(Calendar.MONTH));
            tomorrowAlarm.set(Calendar.DAY_OF_MONTH, now.get(Calendar.DAY_OF_MONTH));
            tomorrowAlarm.set(Calendar.HOUR_OF_DAY, 12);
            tomorrowAlarm.set(Calendar.MINUTE, 0);
            tomorrowAlarm.set(Calendar.SECOND, 0);
            tomorrowAlarm.set(Calendar.MILLISECOND, 0);

            if (planCalendar.compareTo(tomorrowAlarm) != -1) {
                //set the next day alarm
                AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
                Intent i = new Intent(ctx, AlarmReceiver.class);
                am.set(AlarmManager.RTC_WAKEUP, tomorrowAlarm.getTimeInMillis(), PendingIntent.getBroadcast(ctx, 1, i, PendingIntent.FLAG_UPDATE_CURRENT));
            }
        }

        if (gs.getIsAppStart()) {
             Intent dialogIntent = new Intent(this, Main.class);
             dialogIntent.putExtra("is_remind", true);
             dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             this.startActivity(dialogIntent);
        } else {
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx)
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle(getResources().getString(R.string.notify_title))
            .setContentText(getResources().getString(R.string.notify_msg));

            Intent toLaunch = new Intent(getApplicationContext(), Main.class);
            toLaunch.putExtra("is_remind", true);
            toLaunch.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
            PendingIntent intentBack = PendingIntent.getActivity(ctx, 0, toLaunch,PendingIntent.FLAG_UPDATE_CURRENT);

            mBuilder.setContentIntent(intentBack);
            NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);

            // Send Notification
            Notification primaryNotification = mBuilder.build();
            mNotificationManager.notify(10001, primaryNotification);
        }

    }
}

The problem is, if I reset my device it neither triggers the alarm nor my Receiver gets any thing. How do I fix it?

回答1:

Add a BootReceiver class

public class BootReceiver extends BroadcastReceiver {

    private static final String BOOT_COMPLETED =
            "android.intent.action.BOOT_COMPLETED";
    private static final String QUICKBOOT_POWERON =
            "android.intent.action.QUICKBOOT_POWERON";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(BOOT_COMPLETED) ||
                action.equals(QUICKBOOT_POWERON)) {
            Intent service = new Intent(context, BootService.class);
            context.startService(service);
        }
    }

}

Add a BootService class

public class BootService extends IntentService {

    public BootService() {
        super("BootService");
    }

    private void setAlarm() {
        // Set your alarm here as you do in "1. First I set an alarm in alarm manager"
    }

    private void setAlarmsFromDatabase() {
        // Set your alarms from database here
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        setAlarm();
        setAlarmsFromDatabase(); // A nice a approach is to store alarms on a database, you may not need it
        Intent service = new Intent(this, BootService.class);
        stopService(service);
    }

}

Then in your AndroidManifest.xml add

<receiver android:name=".BootReceiver"
          android:enabled="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
         <action android:name="android.intent.action.QUICKBOOT_POWERON" />
    </intent-filter>
</receiver>
<service android:name=".BootService"
         android:enabled="true"/>

And also add this permission

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

Now you can restart your device and the alarm will be set every time your device boots



回答2:

All set alarms gets cleared after phone is restarted. For the alarm intent to work you need to create a pending intent and set it in alarm manager. You could use databases to store alarm details and recreate the alarm on phone boot. (application should listen to android.intent.action.BOOT_COMPLETED)



回答3:

Since you're starting a service there is the possibility that the device wakes up to run the alarm run the onReceive method and then just goes back to sleep before starting the service, generally when starting a service from a BroadCastReceiver you should instead use a WakefulBroadcastReceiver that handles the wake lock for you, try it out and see if that helps out, even if it doesn't help you should use it