通知在错误的时间发出(Java、Android Studio)
Notifications are being sent out on the wrong time (Java, Android Studio)
在我的应用程序中,用户可以选择发送通知的时间和日期。
但是,通知没有在正确的时间发出。如果我选择当天发送通知,则将在第二天随机发送。如果我选择在未来的某一天发出通知,它将在当天的随机时间发出。所以总而言之,它似乎忽略了时间并根据日期发送通知但是当我选择当天的通知时它甚至无法获得正确的日期。
当用户只能选择时间时,代码运行良好。在我添加了选择通知日期的功能后它就坏了。
MeeldetuletusActivity.java
public class MeeldetuletusActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
String timeText;
TextView textTime;
Button nupp_vali, nupp_katkesta, nupp_kuupaev, nupp_alarm;
Calendar c;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meeldetuletus);
nupp_vali = findViewById(R.id.button2);
nupp_katkesta = findViewById(R.id.nupp_katkesta);
nupp_kuupaev = findViewById(R.id.button);
nupp_alarm = findViewById(R.id.button3);
nupp_alarm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startAlarm(c);
}
});
nupp_vali.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DialogFragment timePicker = new TimePickerFragment();
timePicker.show(getSupportFragmentManager(), "time picker");
}
});
nupp_kuupaev.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DialogFragment kuupaevaValija = new KuupaevaFragment();
kuupaevaValija.show(getSupportFragmentManager(), "date picker");
}
});
nupp_katkesta.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
katkestaAlarm();
Toast.makeText(MeeldetuletusActivity.this, "Meeldetuletus unustatud", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
}
public void onDateSet (DatePicker view, int aasta, int kuu, int paev) {
c = Calendar.getInstance();
c.set(Calendar.YEAR, aasta);
c.set(Calendar.MONTH, kuu);
c.set(Calendar.DAY_OF_MONTH, paev);
}
private void startAlarm(Calendar c) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, NotificationPublisher.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0);
if (c.before(Calendar.getInstance())) {
c.add(Calendar.DATE, 1);
}
Objects.requireNonNull(alarmManager).setExact(AlarmManager.RTC_WAKEUP,
c.getTimeInMillis(), pendingIntent);
}
private void katkestaAlarm() {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent myIntent = new Intent(getApplicationContext(), NotificationPublisher.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), 1, myIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.cancel(pendingIntent);
}
}
KuupaevaFragment.java
public class KuupaevaFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar c = Calendar.getInstance();
int aasta = c.get(Calendar.YEAR);
int kuu = c.get(Calendar.MONTH);
int paev = c.get(Calendar.DAY_OF_MONTH);
return new DatePickerDialog(getActivity(), (DatePickerDialog.OnDateSetListener) getActivity(),
aasta, kuu, paev);
}
}
TimePickerFragment.java
public class TimePickerFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);
return new TimePickerDialog(getActivity(), (TimePickerDialog.OnTimeSetListener) getActivity(),
hour, minute, DateFormat.is24HourFormat(getActivity()));
}
}
NotificationPublisher.java
public class NotificationPublisher extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
NotificationHelper notificationHelper = new NotificationHelper(context);
NotificationCompat.Builder nb = notificationHelper.getChannelNotification();
notificationHelper.getManager().notify(1, nb.build());
Notification notification = nb.build();
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_SOUND;
}
}
NotificationHelper.java
class NotificationHelper extends ContextWrapper {
public static final String channelID = "channelID";
public static final String channelName = "Channel Name";
private NotificationManager notificationManager;
public NotificationHelper(Context base) {
super(base);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
}
@TargetApi(Build.VERSION_CODES.O)
private void createChannel() {
NotificationChannel channel = new NotificationChannel(channelID, channelName,
NotificationManager.IMPORTANCE_HIGH);
getManager().createNotificationChannel(channel);
}
public NotificationManager getManager() {
if (notificationManager == null) {
notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
}
return notificationManager;
}
public NotificationCompat.Builder getChannelNotification() {
return new NotificationCompat.Builder(getApplicationContext(), channelID)
.setContentTitle("Meeldetuletus")
.setContentText("Teie valitud aeg on käes")
.setSmallIcon(R.drawable.ic_baseline_notifications_active_24);
}
}
这很可能发生,因为在您的 onTimeSet
和 onDateSet
回调方法中,您正在初始化 c
变量 - 该变量应该代表警报发生的日期应该被解雇,因此每当您设置新的日期或时间时,c
变量会丢失先前设置的数据:
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c = Calendar.getInstance(); // This code is causing the trouble
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
}
public void onDateSet (DatePicker view, int aasta, int kuu, int paev) {
c = Calendar.getInstance(); // This code is also causing the trouble
c.set(Calendar.YEAR, aasta);
c.set(Calendar.MONTH, kuu);
c.set(Calendar.DAY_OF_MONTH, paev);
}
现在,直接的解决方案是在您的 onCreate
生命周期方法 once:
中初始化 c
Calendar c;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meeldetuletus);
c = Calendar.getInstance() // Initialize 'c' here!
...
}
并删除回调方法中的冗余初始化:
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
}
public void onDateSet (DatePicker view, int aasta, int kuu, int paev) {
c.set(Calendar.YEAR, aasta);
c.set(Calendar.MONTH, kuu);
c.set(Calendar.DAY_OF_MONTH, paev);
}
现在,无论何时调用您的 startAlarm(c)
方法,它都会使用从 onDateSet
和 onTimeSet
或仅从其中一个设置的数据 - 基本上,更改不会通过从这些方法本身冗余地重新初始化 c
变量来覆盖在这些方法中创建的内容。
关于您不必要地实例化新的 Calendar
对象, 似乎是正确的。
此外,Calendar
class 是可怕的,应该避免。 Sun、Oracle 和 JCP 在指定 JSR 310 定义现代 java.time 时放弃了遗留的 date-time classes ]classes.
我没有详细研究您的所有代码,但乍一看,您似乎只需要从 UTC 1970 年第一时刻的纪元参考开始计算毫秒数(从UTC 零 hours-minutes-seconds)。我们可以很容易地只使用 java.time classes.
您的代码似乎忽略了时区这一关键问题。如果从您的代码中省略,则隐式应用 JVM 当前的默认时区。我建议最好是明确的。此外,JVM 的当前默认时区可以随时通过同一 JVM 中任何应用程序中的任何代码更改。
ZoneId z = ZoneId.systemDefault() ; // Or ZoneId.of( "Europe/Paris" )
LocalDate ld = LocalDate.of( 2022 , 1 , 23 ) ;
LocalTime lt = LocalTime.of( 15 , 30 ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
Instant instant = zdt.toInstant() ;
long millis = instant.toEpochMilli() ;
在我的应用程序中,用户可以选择发送通知的时间和日期。 但是,通知没有在正确的时间发出。如果我选择当天发送通知,则将在第二天随机发送。如果我选择在未来的某一天发出通知,它将在当天的随机时间发出。所以总而言之,它似乎忽略了时间并根据日期发送通知但是当我选择当天的通知时它甚至无法获得正确的日期。
当用户只能选择时间时,代码运行良好。在我添加了选择通知日期的功能后它就坏了。
MeeldetuletusActivity.java
public class MeeldetuletusActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
String timeText;
TextView textTime;
Button nupp_vali, nupp_katkesta, nupp_kuupaev, nupp_alarm;
Calendar c;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meeldetuletus);
nupp_vali = findViewById(R.id.button2);
nupp_katkesta = findViewById(R.id.nupp_katkesta);
nupp_kuupaev = findViewById(R.id.button);
nupp_alarm = findViewById(R.id.button3);
nupp_alarm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startAlarm(c);
}
});
nupp_vali.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DialogFragment timePicker = new TimePickerFragment();
timePicker.show(getSupportFragmentManager(), "time picker");
}
});
nupp_kuupaev.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DialogFragment kuupaevaValija = new KuupaevaFragment();
kuupaevaValija.show(getSupportFragmentManager(), "date picker");
}
});
nupp_katkesta.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
katkestaAlarm();
Toast.makeText(MeeldetuletusActivity.this, "Meeldetuletus unustatud", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
}
public void onDateSet (DatePicker view, int aasta, int kuu, int paev) {
c = Calendar.getInstance();
c.set(Calendar.YEAR, aasta);
c.set(Calendar.MONTH, kuu);
c.set(Calendar.DAY_OF_MONTH, paev);
}
private void startAlarm(Calendar c) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, NotificationPublisher.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0);
if (c.before(Calendar.getInstance())) {
c.add(Calendar.DATE, 1);
}
Objects.requireNonNull(alarmManager).setExact(AlarmManager.RTC_WAKEUP,
c.getTimeInMillis(), pendingIntent);
}
private void katkestaAlarm() {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent myIntent = new Intent(getApplicationContext(), NotificationPublisher.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), 1, myIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.cancel(pendingIntent);
}
}
KuupaevaFragment.java
public class KuupaevaFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar c = Calendar.getInstance();
int aasta = c.get(Calendar.YEAR);
int kuu = c.get(Calendar.MONTH);
int paev = c.get(Calendar.DAY_OF_MONTH);
return new DatePickerDialog(getActivity(), (DatePickerDialog.OnDateSetListener) getActivity(),
aasta, kuu, paev);
}
}
TimePickerFragment.java
public class TimePickerFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);
return new TimePickerDialog(getActivity(), (TimePickerDialog.OnTimeSetListener) getActivity(),
hour, minute, DateFormat.is24HourFormat(getActivity()));
}
}
NotificationPublisher.java
public class NotificationPublisher extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
NotificationHelper notificationHelper = new NotificationHelper(context);
NotificationCompat.Builder nb = notificationHelper.getChannelNotification();
notificationHelper.getManager().notify(1, nb.build());
Notification notification = nb.build();
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_SOUND;
}
}
NotificationHelper.java
class NotificationHelper extends ContextWrapper {
public static final String channelID = "channelID";
public static final String channelName = "Channel Name";
private NotificationManager notificationManager;
public NotificationHelper(Context base) {
super(base);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
}
@TargetApi(Build.VERSION_CODES.O)
private void createChannel() {
NotificationChannel channel = new NotificationChannel(channelID, channelName,
NotificationManager.IMPORTANCE_HIGH);
getManager().createNotificationChannel(channel);
}
public NotificationManager getManager() {
if (notificationManager == null) {
notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
}
return notificationManager;
}
public NotificationCompat.Builder getChannelNotification() {
return new NotificationCompat.Builder(getApplicationContext(), channelID)
.setContentTitle("Meeldetuletus")
.setContentText("Teie valitud aeg on käes")
.setSmallIcon(R.drawable.ic_baseline_notifications_active_24);
}
}
这很可能发生,因为在您的 onTimeSet
和 onDateSet
回调方法中,您正在初始化 c
变量 - 该变量应该代表警报发生的日期应该被解雇,因此每当您设置新的日期或时间时,c
变量会丢失先前设置的数据:
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c = Calendar.getInstance(); // This code is causing the trouble
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
}
public void onDateSet (DatePicker view, int aasta, int kuu, int paev) {
c = Calendar.getInstance(); // This code is also causing the trouble
c.set(Calendar.YEAR, aasta);
c.set(Calendar.MONTH, kuu);
c.set(Calendar.DAY_OF_MONTH, paev);
}
现在,直接的解决方案是在您的 onCreate
生命周期方法 once:
c
Calendar c;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meeldetuletus);
c = Calendar.getInstance() // Initialize 'c' here!
...
}
并删除回调方法中的冗余初始化:
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
}
public void onDateSet (DatePicker view, int aasta, int kuu, int paev) {
c.set(Calendar.YEAR, aasta);
c.set(Calendar.MONTH, kuu);
c.set(Calendar.DAY_OF_MONTH, paev);
}
现在,无论何时调用您的 startAlarm(c)
方法,它都会使用从 onDateSet
和 onTimeSet
或仅从其中一个设置的数据 - 基本上,更改不会通过从这些方法本身冗余地重新初始化 c
变量来覆盖在这些方法中创建的内容。
关于您不必要地实例化新的 Calendar
对象,
此外,Calendar
class 是可怕的,应该避免。 Sun、Oracle 和 JCP 在指定 JSR 310 定义现代 java.time 时放弃了遗留的 date-time classes ]classes.
我没有详细研究您的所有代码,但乍一看,您似乎只需要从 UTC 1970 年第一时刻的纪元参考开始计算毫秒数(从UTC 零 hours-minutes-seconds)。我们可以很容易地只使用 java.time classes.
您的代码似乎忽略了时区这一关键问题。如果从您的代码中省略,则隐式应用 JVM 当前的默认时区。我建议最好是明确的。此外,JVM 的当前默认时区可以随时通过同一 JVM 中任何应用程序中的任何代码更改。
ZoneId z = ZoneId.systemDefault() ; // Or ZoneId.of( "Europe/Paris" )
LocalDate ld = LocalDate.of( 2022 , 1 , 23 ) ;
LocalTime lt = LocalTime.of( 15 , 30 ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
Instant instant = zdt.toInstant() ;
long millis = instant.toEpochMilli() ;