我在github上看到这个开源项目。但它有一个问题,就是如果屏幕关闭,就不能准确地通知设定的时间表。我想重新编程,让它在用户设定的确切时间内发出通知,即使设备的屏幕是关闭的。
问题。"如果设备屏幕关闭/Doze/睡眠模式,安卓项目在5分钟后通知。"
注:目标SDK版本23-33
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Schedule alarm notification
private void scheduleNotification(Notification notification, long delay) {
Intent notificationIntent = new Intent(this, MyNotificationPublisher.class);
notificationIntent.putExtra(MyNotificationPublisher.NOTIFICATION_ID, 1);
notificationIntent.putExtra(MyNotificationPublisher.NOTIFICATION, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
assert alarmManager != null;
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, delay, pendingIntent);
Log.d(TAG, "scheduleNotification: Notification set successfully!");
//Build notification
private Notification getNotification(String content) {
//on notification click open MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, default_notification_channel_id);
builder.setContentTitle("ToDo Reminder");
builder.setContentText(content);
builder.setContentIntent(pendingIntent);
builder.setAutoCancel(true);
builder.setSmallIcon(R.drawable.ic_stat_name);
builder.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND);
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
return builder.build();
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(System.currentTimeMillis());
//Set custom date
dateText.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onClick(View v) {
final DatePickerDialog datePickerDialog = new DatePickerDialog(MainActivity.this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
String newMonth = getMonth(monthOfYear + 1);
dateText.setText(dayOfMonth + " " + newMonth);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, monthOfYear);
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
Log.d(TAG, "onDateSet: Date has been set successfully");
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
datePickerDialog.show();
datePickerDialog.getDatePicker().setMinDate(date);
//Set custom time
timeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TimePickerDialog timePickerDialog = new TimePickerDialog(MainActivity.this,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
String time;
String minTime = String.format("%02d", minute); //prefix zero to minute if its single digit
//Method to postfix AM/PM
if (hourOfDay >= 0 && hourOfDay < 12) {
time = hourOfDay + " : " + minTime + " AM";
} else {
if (hourOfDay != 12) {
hourOfDay = hourOfDay - 12;
time = hourOfDay + " : " + minTime + " PM";
timeText.setText(time);
cal.set(Calendar.HOUR, hourOfDay);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, 0);
Log.d(TAG, "onTimeSet: Time has been set successfully");
}, cal.get(Calendar.HOUR), cal.get(MINUTE), false);
timePickerDialog.show();
dialogBuilder.setTitle("Lets add new task!");
dialogBuilder.setPositiveButton("Done", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String title = editTitle.getText().toString();
String date = dateText.getText().toString();
String time = timeText.getText().toString();
if (title.length() != 0) {
try {
insertDataToDb(title, date, time);
scheduleNotification(getNotification(title), cal.getTimeInMillis());
} catch (Exception e) {
e.printStackTrace();
} else {
toastMsg("Oops, Cannot set an empty ToDo!!!");
dialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//pass
dialog.cancel();
MyNotificationPublisher.java
public class MyNotificationPublisher extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification-id";
public static String NOTIFICATION = "notification";
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = intent.getParcelableExtra(NOTIFICATION);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel notificationChannel = new NotificationChannel(MainActivity.NOTIFICATION_CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", importance);
assert notificationManager != null;
notificationManager.createNotificationChannel(notificationChannel);
int id = intent.getIntExtra(NOTIFICATION_ID, 0);
assert notificationManager != null;
notificationManager.notify(id, notification);
舱单文件的内部
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/LightTheme">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>