我浏览了大量的StackOverflow和Youtube教程来创建一个警报服务(用作我正在构建的更大应用程序的一部分),但似乎所有这些都提供了不同的,不工作的答案或者依赖不再使用的弃用方法。
我对以下代码的当前问题是,当我到达以下代码时:alrmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendInt);
它似乎没有向警报管理器发送适当的时间(它或多或少总是发送当前时间)。 / p>
但是,我知道calendar.getTimeInMillis()给了我设置的时间(setAlarmText textView正确更改)。我想知道是否有人有这方面的经验?
此外,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
}
}
AlarmReceiver:
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {`
Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); //this will sound the alarm tone
Log.d("Creating Alarm", "Used ALARM for ringtone " + alarmUri);
System.out.println("logging that it got to this part");
if (alarmUri == null) {
Log.d("Creating Alarm", "Used the notification instead of alarm for ringtone");
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
Ringtone ringtone = RingtoneManager.getRingtone(context, alarmUri);
ringtone.play(); // plays the ringtone of the phone as the alarm
Intent service_intent = new Intent(context, AlarmService.class);
context.startService(service_intent);
//ComponentName comp = new ComponentName(context.getPackageName(), AlarmService.class.getName());
//startWakefulService(context, (intent.setComponent(comp))); // sends the notification message and wakes up the phone
setResultCode(Activity.RESULT_OK);
}
}
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);
}
}
代码也可以在https://github.com/sahilmgandhi/REM.my/tree/master/app/src/main/java/com/sahilmgandhi/remmy
上找到任何帮助都将不胜感激。
修改
这是我们提出的解决方案,如果它可以帮助任何人:
尼克:所以广播确实启动了运行通知的服务,但问题是声音应该改变的地方而不是?
尼克:好的,你有没有理由从接收器那里获得声音播放而没有直接从通知对象播放声音?我认为这样可行
我:嗯,我正在学习几个不同的教程,所有这些教程似乎都在接收器中。
Nick:在服务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.");
}
尼克:很确定这是现在发出通知声音的正确方法。使用铃声的旧方式可能已被弃用。暂时评论铃声部分
Nick:从AlarmManager运行服务的正确方法是让它激活一个BroadcastReceiver然后启动你的IntentService。
尼克:我完全忘记了为什么会这样......但你肯定想这样做。我已经考虑过在接收器中运行所有东西但最好这样做,闹钟 - &gt;接收器 - &gt; IntentService包含其中的所有代码
答案 0 :(得分:1)
要在正确的时间触发闹钟,请在AlarmStartPage
活动中实例化日历对象后添加此行:
calendar.setTimeInMillis(System.currentTimeMillis());
因此,在创建日历时,您的代码应该如下所示:
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
这将确保正确设置其他日历参数,因为在您设置其他日历/分钟之前,其余日期参数未完全配置。