使用AlarmReceiver和OnBootReceiver为android应用程序设置警报

使用AlarmReceiver和OnBootReceiver为android应用程序设置警报,android,alarmmanager,Android,Alarmmanager,我浏览了大量StackOverflow和Youtube教程来创建一个报警服务(作为我正在构建的更大应用程序的一部分使用),但它们似乎都给出了不同的、不起作用的答案,或者依赖于不推荐的不再起作用的方法 我目前使用以下代码的问题是,当我使用以下代码时:alrmMgr.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pendInt)它似乎没有向报警管理器发送适当的时间(它或多或少总是发送当前时间) 但是,我知道calendar.getTim

我浏览了大量StackOverflow和Youtube教程来创建一个报警服务(作为我正在构建的更大应用程序的一部分使用),但它们似乎都给出了不同的、不起作用的答案,或者依赖于不推荐的不再起作用的方法

我目前使用以下代码的问题是,当我使用以下代码时:
alrmMgr.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pendInt)它似乎没有向报警管理器发送适当的时间(它或多或少总是发送当前时间)

但是,我知道calendar.getTimeInMillis()会给我设置的时间(setAlarmText文本视图会正确更改)。我想知道是否有人有这方面的经验

此外,
AlarmReceiver
类似乎从未被调用过,尽管我的印象是
AlarmManager
会为您解决这个问题

代码如下:

public class AlarmStartPage extends Activity {
    AlarmManager alrmMgr;
    PendingIntent pendInt;                         
    private TimePicker alrmTimePicker;
    private static AlarmStartPage inst;
    Intent myIntent;
    private TextView alrmStatusView;`

    protected static AlarmStartPage instance() {
        return inst;                                        // returns an instance of the current Activity
    }

    @Override
    public void onStart() {
        super.onStart();                                    // calls the super classes onStart, and then sets the instance to the current one
        inst = this;
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alarm_start_page);                             // sets the various buttons and other containers on the website
        alrmTimePicker = (TimePicker) findViewById(R.id.alarmTimePicker);
        ToggleButton alrmTogg = (ToggleButton) findViewById(R.id.toggleAlarmButton);
        alrmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alrmStatusView = (TextView) findViewById(R.id.alarmStatus);

        setVolumeControlStream(AudioManager.STREAM_ALARM);                              // sets the volume to be controlled to the audiomanager so that the user can control the alarm's volume
    }

    public void onToggleClicked(View view) {
        if (((ToggleButton) view).isChecked()) {
            Log.d("MyActivity", "Alarm On!");
            int hourToSet, minuteToSet;                                                 // if the toggle button is pushed, then it creates an alarm. Otherwise it cancels a previously created alarm
            Calendar calendar = Calendar.getInstance();
            if (Build.VERSION.SDK_INT >= 23)                                            // the code here and the one below in the else statement are identical except for which API they cater to
            {
                hourToSet = alrmTimePicker.getHour();
                minuteToSet = alrmTimePicker.getMinute();                               // gets the TimePicker's time that the user wants if using Android Marshmallow
            } else {
                hourToSet = alrmTimePicker.getCurrentHour();                            // gets the TimePicker's time that the user wants if using any Android Lolipop or below
                minuteToSet = alrmTimePicker.getCurrentMinute();
            }
            // this is the code to actually do the "magic" of the REM time
            int currhr = calendar.get(Calendar.HOUR_OF_DAY);                        // gets the current time from the system's clock
            int currmin = calendar.get(Calendar.MINUTE);

            boolean lessThan90 = false;                                             // boolean to check if the current alarm is less than 90 Minutes away (1 REM cycle)
            int hrDiff = 0;
            int minDiff = 0;

            if (hourToSet >= currhr) {
                hrDiff = hourToSet - currhr;                                        // calculating the difference between the current hour and the hour of the alarm to get the difference in the time
                if (hrDiff == 0) {
                    if (minuteToSet > currmin)                                      // if the alarm is for after the current time, but same hour, then it is less than 1 hour away
                        minDiff = minuteToSet - currmin;
                    else {
                        hrDiff = 23;                                                // otherwise the alarm us for more than 23 hours away (same hour, but earlier time)
                        minDiff = 60 - (currmin - minuteToSet);
                    }
                } else {
                    if (minuteToSet > currmin)
                        minDiff = minuteToSet - currmin;
                    else {
                        hrDiff--;
                        minDiff = 60 - (currmin - minuteToSet);
                    }
                }

                if (60 * hrDiff + minDiff < 90)                                       // if prior to the 15 min shift, the alarm time is less than 90 minutes away, then it will be set as the alarm time
                    lessThan90 = true;
            }

            currmin += 15;                                                            // add 15 min to the current time, and below, change the hour and minute accordingly
            if (currmin >= 60) {
                currmin = currmin % 60;
                currhr++;
                if (currhr >= 24)
                    currhr = currhr % 24;
            }
            if (!lessThan90)                                                        // only if the alarm time is more than 90 minutes away, it will try to do this (which it will try to do
            {                                                                       // by defualt since lessThan90 is initalized to false (or it is set to true by the above if else statement
                if (hourToSet >= currhr) {
                    hrDiff = hourToSet - currhr;
                    if (hrDiff == 0)                                                // same logic as earlier, checks if the same hour as the alarm, then checks if the alarm is before or after the current time
                    {
                        if (minuteToSet > currmin)                                  // if the alarm is set for a later time (which means that it is less than 90 minutes away)
                            minDiff = minuteToSet - currmin;
                        else                                                        // otherwise the alarm is set for 23 hours and some minutes away
                        {
                            minDiff = 60 - (currmin - minuteToSet);
                            hrDiff = 23;
                        }
                    } else {
                        if (minuteToSet > currmin)
                            minDiff = minuteToSet - currmin;
                        else {
                            hrDiff--;
                            minDiff = 60 - (currmin - minuteToSet);
                        }
                    }
                } else if (hourToSet < currhr)                                        // if the alarm time is before the current time (then it must loop over midnight and restart from 0 again)
                    hrDiff = 24 - (currhr - hourToSet);
            }


            int totalMinutesInBetween = 60 * hrDiff + minDiff;

            if (totalMinutesInBetween < 90)                                         // if the total minutes between the alarm and the current time (after the 15 min shift) is less than 90 minutes
                lessThan90 = true;                                                  // it is less than 1 REM shift away

            if (!lessThan90)                                                        // If there are more than 90 minutes of difference, then a REM cycle is ACTUALLY possible
            {
                int possibleRem = totalMinutesInBetween / 90;                         // the possible amount of REM cycles between now and the alarm time
                for (int i = 0; i < possibleRem; i++) {
                    currhr++;                                                       // the time is altered by 90 minute cycles (looping around after 60 minutes or after 24 hours) to get the appropiate REM time
                    if (currhr >= 24)
                        currhr = currhr % 24;
                    currmin += 30;
                    if (currmin >= 60) {
                        currmin = currmin % 60;                                       // looping the minutes over 60
                        currhr++;
                        if (currhr >= 24)
                            currhr = currhr % 24;                                     // looping the hours after 24 hours
                    }
                }
                hourToSet = currhr;
                minuteToSet = currmin;
            }

            calendar.set(Calendar.HOUR_OF_DAY, hourToSet);                          // the calendar sets the final REM time
            calendar.set(Calendar.MINUTE, minuteToSet);

            myIntent = new Intent(AlarmStartPage.this, AlarmReceiver.class);
            pendInt = PendingIntent.getBroadcast(AlarmStartPage.this, 0, myIntent, 0);             // new intent as well as a pending intent to notify the system of the alarm (uses Alarm Receiver and Alarm Service)

            alrmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendInt);                     // alarmmanager is used to set the alarm
            if (minuteToSet > 9)
                setAlarmText("An alarm has been placed for " + hourToSet + ":" + minuteToSet + " (in military time). If you shut down" +
                        " this app, please do not open it again until the alarm that you set is over (otherwise the alarm will reset itself).");    // alarm text is changed to notify the user
            else
                setAlarmText("An alarm has been placed for " + hourToSet + ":0" + minuteToSet + " (in military time). If you shut down" +
                        " this app, please do not open it again until the alarm that you set is over (otherwise the alarm will reset itself).");
        } else {
            alrmMgr.cancel(pendInt);                                                //cancels the current Intent (effectively stopping the alarm)
            stopService(myIntent);
            setAlarmText("The previous alarm was canceled.");                       // changes the text on the textbox under the time picker
            Log.d("MyActivity", "Alarm OFF");
        }
    }

    public void setAlarmText(String textToShow) {
        alrmStatusView.setText(textToShow);             // sets the text for the textbox below the TimePicker
    }

    @Override
    public void onDestroy() {
        super.onDestroy();                              // calls the super classes destroy method to destroy the activity
    }
}
AlarmService.java:

public class AlarmService extends IntentService {
    private NotificationManager alarmNotificationManager;`

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

    @Override
    public void onHandleIntent(Intent intent) {
        sendNotification("Wake Up! Your alarm has been rung!!!!");                      // sends the notification to the phone that the alarm is ringing
    }

    private void sendNotification(String msg) {
        Log.d("AlarmService", "Preparing to send notification...: " + msg);
        alarmNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AlarmStartPage.class), 0);                        // creates the notification and sets the icon for the notification

        NotificationCompat.Builder alarmNotificationBuilder = new NotificationCompat.Builder(
                this).setContentTitle("Alarm").setSmallIcon(R.mipmap.ic_launcher).setStyle(new NotificationCompat.BigTextStyle().bigText(msg)).setContentText(msg);


        alarmNotificationBuilder.setContentIntent(contentIntent);

        alarmNotificationManager.notify(1, alarmNotificationBuilder.build());
        Log.d("AlarmService", "Notification sent.");
    }
}
OnBootReceiver:

public class OnBootReceiver extends BroadcastReceiver {
    private static final int WAITING_PERIOD = 10000;    // 10 seconds (aka 10000 milliseconds)`

    @Override
    public void onReceive(Context context, Intent intent)
    {
        AlarmManager aMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);          // allows previously created alarms to still exist on bootup.
        Intent i = new Intent(context, AlarmReceiver.class);
        PendingIntent pI = PendingIntent.getBroadcast(context, 0, i, 0);

        aMgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), WAITING_PERIOD, pI);
    }
}
代码也可以在上找到

任何帮助都将不胜感激

编辑:

这是我们提出的解决方案,以防对任何人有所帮助:

尼克:所以广播确实启动了运行通知的服务,但问题是声音应该在哪里改变,而不是在哪里

尼克:好的,那么有什么原因让你从接收器播放声音,而不是直接从通知对象播放声音?我想那会管用的

我:嗯,我在听几个不同的教程,它们似乎都在听筒里

尼克:在service sendNotification方法中,尝试更改为:

    private void sendNotification(String msg) {
    Log.d("AlarmService", "Preparing to send notification...: " + msg);
    alarmNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);

    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AlarmStartPage.class), 0);                        // creates the notification and sets the icon for the notification


    Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
    NotificationCompat.Builder alarmNotificationBuilder = new NotificationCompat.Builder(
            this)
            .setContentTitle("Alarm")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
            .setContentText(msg)
            .setSound(soundUri);

    alarmNotificationBuilder.setContentIntent(contentIntent);

    alarmNotificationManager.notify(1, alarmNotificationBuilder.build());
    Log.d("AlarmService", "Notification sent.");
}
尼克:很确定这是正确的通知方式。。使用铃声的旧方法可能已被弃用。现在请评论铃声部分

尼克:从AlarmManager运行服务的正确方法是让它启动一个广播接收器,然后启动你的IntentService


尼克:我完全忘了为什么会这样。。但你肯定想这样做。我曾考虑过在接收器中运行所有程序,但最好是这样做,alarm->receiver->IntentService,其中包含所有代码

要在正确的时间触发警报,请在
AlarmStartPage
活动中实例化日历对象后添加此行:

calendar.setTimeInMillis(System.currentTimeMillis());
因此,当您创建日历时,您的代码应该如下所示:

Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());

这将确保其他日历参数设置正确,因为在设置之前,日历参数仅设置小时/分钟,而其余参数未完全配置。

这可能不是问题所在,但请先尝试一下,看看是否有任何变化。添加
calendar.setTimeInMillis(System.currentTimeMillis())就在这一行之后:
Calendar Calendar=Calendar.getInstance()AlarmStartPage
活动中的code>。添加该行有助于不再在当前时间创建报警,而是在时间选择器设置的时间创建报警。非常感谢。然而,出于某种原因,即使我在AlarmReceiver.java中设置了断点,程序也不会进入其中(即使警报最终响起并发出祝酒声)。酷!我为问题的那一部分添加了一个回复帖子。我不完全理解你关于AlarmReceiver的意思,你到底想实现什么?我希望能弄明白。让我澄清一下。警报响起后,会向用户敬酒,告诉用户警报正在响。这是在AlarmService类中完成的。但是,运行的报警依赖于默认的通知噪声,而不是默认的报警噪声(这是我在AlarmReceiver.java中尝试更改的)。然而OnReceive方法似乎从未被调用。所有日志语句都不会被打印出来。好的。但是,我没有看到在您的代码中任何地方制作
吐司
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());