如何在安卓设备上按特定时间发送通知?

48
我想在特定时间给我的应用程序发送通知。比如每天早上7点,即使应用程序关闭也要发送通知。
我该怎么做呢?有什么教程吗? 请提供链接。
6个回答

38

首先,您需要使用BroadcastReceiver。由于广播接收器仅运行短时间

根据Android开发者博客所述,当处理广播时,应用程序会被分配一个固定的时间(目前为10秒)来完成其工作。如果在该时间内未完成,应用程序被视为行为不当,并立即将其进程置于后台状态以便在需要时释放内存。

在这里使用IntentService是更好的做法,以下是如何实现的示例。

这是广播接收器类。

public class MyReceiver extends BroadcastReceiver {
    public MyReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        Intent intent1 = new Intent(context, MyNewIntentService.class);
        context.startService(intent1);
    }
}

并将其在清单文件中注册。

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="false" >
</receiver>

这是意图服务类。

public class MyNewIntentService extends IntentService {
    private static final int NOTIFICATION_ID = 3;

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

    @Override
    protected void onHandleIntent(Intent intent) {
        Notification.Builder builder = new Notification.Builder(this);
            builder.setContentTitle("My Title");
            builder.setContentText("This is the Body");
            builder.setSmallIcon(R.drawable.whatever);
        Intent notifyIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 2, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        //to be able to launch your activity from the notification 
        builder.setContentIntent(pendingIntent);
        Notification notificationCompat = builder.build();
        NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
        managerCompat.notify(NOTIFICATION_ID, notificationCompat);
    }
}

并在清单文件中注册它。

<service
    android:name=".MyNewIntentService"
    android:exported="false" >
</service>

然后在您的活动中设置闹钟管理器以在特定时间启动广播接收器,并使用AlarmManager setRepeating方法重复它。以下示例将每天重复。

 Intent notifyIntent = new Intent(this,MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast
            (context, NOTIFICATION_REMINDER_NIGHT, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,  System.currentTimeMillis(),
            1000 * 60 * 60 * 24, pendingIntent);

希望这能对你有所帮助。


2
对我不起作用,尝试手动设置时间但仍然无法工作。 - Mitesh Jain
1
使用广播的原因是什么?我认为这并不是必要或有用的。你可以通过 PendingIntent 直接与你的 IntentService 进行通信。 - Zordid
它能在Pie版本中运行吗?似乎不能 :( - Narendra Singh
@NarendraSingh 这是因为你需要一个适用于Android O及更高版本的通知渠道。请参阅https://developer.android.com/training/notify-user/build-notification#Priority - LimitX
使用Google的基于警报系统的通知示例 https://github.com/googlecodelabs/android-kotlin-notifications - Sri Krishna
显示剩余2条评论

17
您可以使用AlarmManager在指定的时间设置闹钟。
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (!prefs.getBoolean("firstTime", false)) {

    Intent alarmIntent = new Intent(this, AlarmReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);

    AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 7);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 1);

    manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
            AlarmManager.INTERVAL_DAY, pendingIntent);

    SharedPreferences.Editor editor = prefs.edit();
    editor.putBoolean("firstTime", true);
    editor.apply();
}

我使用SharedPreferences来检查是否第一次运行该应用程序,如果是,则设置该警报,否则不执行任何操作,而不是每次启动应用程序时重置警报。
使用BroadcastReceiver监听警报触发事件

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // show toast
        Toast.makeText(context, "Alarm running", Toast.LENGTH_SHORT).show();
    }
}

使用另一个接收器来监听设备启动,以便您可以重置警报。

public class DeviceBootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            // on device boot compelete, reset the alarm
            Intent alarmIntent = new Intent(context, AlarmReceiver.class);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);

            AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis());
            calendar.set(Calendar.HOUR_OF_DAY, 7);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 1);

            manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                    AlarmManager.INTERVAL_DAY, pendingIntent);
        }
    }
}

将权限添加到清单文件中

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

然后注册您的接收器

<receiver android:name=".DeviceBootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
<receiver android:name=".AlarmReceiver" />

如果在打开应用程序时也需要发送通知怎么办? - Si8
感谢您的回复。SharedPreference在第二天会如何工作?因为布尔值将变为false。 - Si8
不客气。SharedPreferences 只在应用程序第一次启动时工作以设置闹钟,然后将布尔值设置为 true,即应用程序已经启动过,以避免反复重置闹钟。 - SaNtoRiaN
我会测试一下。谢谢。 - Si8
1
如果您的意图(intents)神奇地消失了,您可能需要设置一个标志(flag):请参见此答案。此外,如果您的Serializable对象没有被包装在Bundle中,则可能会变成null:请参见此答案 - Benoit Duffez
显示剩余7条评论

16

由于后台服务限制,接受答案中的解决方案在安卓8.0(API级别26)及以上版本中将无法正常工作(https://developer.android.com/about/versions/oreo/background.html#services),并且当应用程序在后台运行时会导致以下异常:

java.lang.IllegalStateException: Not allowed to start service Intent xxx: app is in background

可能的解决方案之一是使用JobIntentService

  1. 将您的Service扩展自JobIntentService,而不是IntentService,并使用onHandleWork方法替换onHandleIntent

  2. AndroidManifest.xml中为您的Service添加android:permission="android.permission.BIND_JOB_SERVICE"


使用已接受答案中给出的代码时,该方法无法正常工作,请根据您的指示进行修改。它没有调用“BroadCastReceiver”。我该如何解决这个问题? - JAMSHAID
不看你的代码很难说。如果你的接收器有问题,在启动服务之前就会发生,无论你是使用我的建议还是接收者的答案。你在清单中注册了接收器吗? - Vasily Kabunov
1
为什么不完全省略服务?设置通知应该足够快,可以在广播接收器中完成。 - Eir

6

这是我针对Android 10测试过并且适用于之前所有版本的解决方案

MainActivity.class

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    ....
    reminderNotification();

}

public void reminderNotification()
{
    NotificationUtils _notificationUtils = new NotificationUtils(this);
    long _currentTime = System.currentTimeMillis();
    long tenSeconds = 1000 * 10;
    long _triggerReminder = _currentTime + tenSeconds; //triggers a reminder after 10 seconds.
    _notificationUtils.setReminder(_triggerReminder);
}

NotificationUtils.class

public class NotificationUtils extends ContextWrapper
{

    private NotificationManager _notificationManager;
    private Context _context;

    public NotificationUtils(Context base)
    {
        super(base);
        _context = base;
        createChannel();
    }

    public NotificationCompat.Builder setNotification(String title, String body)
    {
        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.noti_icon)
                .setContentTitle(title)
                .setContentText(body)
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);
    }

    private void createChannel()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, TIMELINE_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
            getManager().createNotificationChannel(channel);
        }
    }

    public NotificationManager getManager()
    {
        if(_notificationManager == null)
        {
            _notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        }

        return _notificationManager;
    }

    public void setReminder(long timeInMillis)
    {
        Intent _intent = new Intent(_context, ReminderBroadcast.class);
        PendingIntent _pendingIntent = PendingIntent.getBroadcast(_context, 0, _intent, 0);

        AlarmManager _alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

        _alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, _pendingIntent);
    }

}

ReminderBroadcast.class

public class ReminderBroadcast extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        NotificationUtils _notificationUtils = new NotificationUtils(context);
        NotificationCompat.Builder _builder = _notificationUtils.setNotification("Testing", "Testing notification system");
        _notificationUtils.getManager().notify(101, _builder.build());
    }
}

AndroidManifest.xml

<application>
    ...
    <receiver android:name=".custom.ReminderBroadcast"/>
</application>

NB: CHANNEL_IDTIMELINE_CHANNEL_NAME已经在另一个类中创建。

例如,

CHANNEL_ID = "通知渠道";

TIMELINE_CHANNEL_NAME = "时间线通知";

如果对我的代码有任何误解或错误,请不要犹豫,留言评论。我会尽快回复。


无法在我的系统通知区域中看到通知。没有崩溃。没有通知。 - Sri Krishna
是否有可能为当前的AndroidX平台和Android 13重新编写答案? - Mahmoud Ragab
抱歉回复晚了,@SriKrishna和其他人。目前,我已经全身心投入到使用Flutter进行移动开发,不再进行原生开发。感谢你们的评论。 - undefined

2
  • 从系统获取闹钟服务。
  • 创建一个挂起的Intent,传入广播接收器类名。
  • 创建一个日历对象,并将其时间设置为上午8点。
  • 检查当前时间是否晚于8点。如果是,则添加另一天。
  • 调用AlarmManager类的set repeating方法。

相应的示例代码:

alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);   
alarmIntent = new Intent(context of current file, AlarmReceiver1.class); 
AlarmReceiver1 = broadcast receiver

    pendingIntent = PendingIntent.getBroadcast(  Menu.this, 0, alarmIntent, 
PendingIntent.FLAG_UPDATE_CURRENT);
    alarmIntent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
    alarmManager.cancel(pendingIntent);

    Calendar alarmStartTime = Calendar.getInstance();
    Calendar now = Calendar.getInstance();
    alarmStartTime.set(Calendar.HOUR_OF_DAY, 8);
    alarmStartTime.set(Calendar.MINUTE, 00);
    alarmStartTime.set(Calendar.SECOND, 0);
    if (now.after(alarmStartTime)) {
        Log.d("Hey","Added a day");
        alarmStartTime.add(Calendar.DATE, 1);
    }

     alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 
alarmStartTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
     Log.d("Alarm","Alarms set for everyday 8 am.");

来到广播接收器类。您需要在清单中注册您的广播接收器。这将使您接收时钟事件。 重写此广播接收器的onReceive方法,并在其中创建通知,或者创建一个独立的通知构建服务,在那里构建和显示您的通知。
清单代码片段:

广播接收器代码片段:
public class AlarmReceiver1 extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
  Intent service1 = new Intent(context, NotificationService1.class);
service1.setData((Uri.parse("custom://"+System.currentTimeMillis())));
          context.startService(service1);
}

通知构建服务代码片段:

public class NotificationService1 extends IntentService{

private NotificationManager notificationManager;
private PendingIntent pendingIntent;
private static int NOTIFICATION_ID = 1;
Notification notification;
@Override
protected void onHandleIntent(Intent intent) {
Context context = this.getApplicationContext();
       notificationManager = 
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent mIntent = new Intent(this, Activity to be opened after clicking on the 
notif);
        Bundle bundle = new Bundle(); 
        bundle.putString("test", "test");
        mIntent.putExtras(bundle);
        pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 
PendingIntent.FLAG_UPDATE_CURRENT);     

        Resources res = this.getResources();
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
        notification = new NotificationCompat.Builder(this)
                    .setContentIntent(pendingIntent)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher))
                    .setTicker("ticker value")
                    .setAutoCancel(true)
                    .setPriority(8)
                    .setSound(soundUri)
                    .setContentTitle("Notif title")
                    .setContentText("Text").build();
        notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS;
        notification.defaults |= Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE;
        notification.ledARGB = 0xFFFFA500;
        notification.ledOnMS = 800;
        notification.ledOffMS = 1000;
        notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(NOTIFICATION_ID, notification);
        Log.i("notif","Notifications sent.");

}

}

嘿,我有一个通知问题,希望你能帮忙。https://stackoverflow.com/questions/76949162/scheduled-notification-not-appearing-andriod-13 - Mahmoud Ragab

1
使用NotifyMe Android库进行简单通知。非常容易地设置延迟或时间,以便在您想要弹出通知时弹出。通知将通过系统重新启动弹出。
使用Jitpack.io下载库。将以下内容添加到您的应用程序的build.gradle文件中。
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

将以下代码添加到您项目的build.gradle文件中的dependencies中。

dependencies {
        implementation 'com.github.jakebonk:NotifyMe:1.0.1'
}

创建一个NotifyMe Builder对象
NotifyMe.Builder notifyMe = new NotifyMe.Builder(getApplicationContext());

然后设置你想要的字段。
notifyMe.title(String title);
notifyMe.content(String content);
notifyMe.color(Int red,Int green,Int blue,Int alpha);//Color of notification header
notifyMe.led_color(Int red,Int green,Int blue,Int alpha);//Color of LED when 
notification pops up
notifyMe.time(Calendar time);//The time to popup notification
notifyMe.delay(Int delay);//Delay in ms
notifyMe.large_icon(Int resource);//Icon resource by ID
notifyMe.rrule("FREQ=MINUTELY;INTERVAL=5;COUNT=2")//RRULE for frequency of 
//notification
notifyMe.addAction(Intent intent,String text); //The action will call the intent when 
//pressed

在设置好所需的所有字段后,只需调用build()方法即可!
notifyMe.build();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接