您的位置 首页 java

七爪源码:Android——每天在特定时间重复通知

七爪源码:Android——每天在特定时间重复通知

在 android 中,安排工作/作业在特定时间运行是很棘手的,因为有很多选项,如 WorkManager、JobScheduler、 Alarm Manager + BroadcastReceivers 以及因为打盹模式。

在此博客中,我将向您展示我在已发布的应用程序 QuoteLab 中使用的实现,以使用 alarm Manager + BroadcastReceivers 在特定时间发送每日提醒通知。

为什么使用 AlarmManager + BroadcastReceivers?

由于 WorkManager 不保证执行时间,据我所知,AlarmManager 是在特定时间触发某些事件的最佳选择,并且在处理打盹模式时也是更安全的选择。

我们将如何去做?

我们的任务比较简单,我们要做的就是

  1. 创建ReminderManager 负责启动和停止提醒。
  2. ReminderManager 将使用 AlarmManager 安排警报
  3. AlarmManager 将在预定时间发生时触发 AlarmReceiver。
  4. AlarmReceiver 将发送通知并可选择重新安排提醒。

1. ReminderManager

让我们创建一个具有以下两个功能的对象……

 object RemindersManager {
  const val REMINDER_NOTIFICATION_REQUEST_CODE = 123
  fun startReminder(
        context: Context,
        reminderTime: String = "08:00",
        reminderId: Int = REMINDER_NOTIFICATION_REQUEST_CODE
    ) {}

    fun stopReminder(
        context: Context,
        reminderId: Int = REMINDER_NOTIFICATION_REQUEST_CODE
    ) {}
}  

提示 :如果您使用 DI,您可以创建一个类而不是对象,并在构造函数中注入 applicationContext。

然后 startReminder 函数会像……

 fun startReminder(
        context: Context,
        reminderTime: String = "08:00",
        reminderId: Int = REMINDER_NOTIFICATION_REQUEST_CODE
    ) {
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

        val (hours, min) = reminderTime.split(":").map { it.toInt() }
        val  Intent  =
            Intent(context.applicationContext, AlarmReceiver::class.java).let { intent ->
                PendingIntent.getBroadcast(
                    context.applicationContext,
                    reminderId,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT
                )
            }

        val calendar: Calendar = Calendar. getInstance (Locale.ENGLISH).apply {
            set(Calendar.HOUR_OF_DAY, hours)
            set(Calendar.MINUTE, min)
        }

        // If the trigger time you specify is in the past, the alarm triggers immediately.
        // if soo just add one day to required calendar
        // Note: i'm also adding one min cuz if the user clicked on the notification as soon as
        // he receive it it will reschedule the alarm to fire another notification immediately
        if (Calendar.getInstance(Locale.ENGLISH)
                .apply { add(Calendar.MINUTE, 1) }.timeInMillis - calendar.timeInMillis > 0
        ) {
            calendar.add(Calendar.DATE, 1)
        }

        alarmManager.setAlarmClock(
                AlarmManager.AlarmClockInfo(calendar.timeInMillis, intent),
                intent
        )
    }  
  • 在这里,我们使用remindId 来识别警报,以防我们以后想停止它(例如:如果用户禁用了设置中的通知)
  • 我们使用提醒时间来安排下一个选定时间的警报,但是如果您想安排它,例如五天后,您可以发送一个日历并将其传递给 AlarmManager。
  • 我们将在我们的预定闹钟中增加一天,否则如果您尝试设置的时间早于当前时间,它将立即触发闹钟。

注意 :面向 SDK 级别 31 或更高级别的应用需要请求 SCHEDULE_EXACT_ALARM 权限才能使用此 API。 用户和系统可以通过设置中的特殊应用访问屏幕撤销此权限。

而 stopReminder 功能将是……

 fun stopReminder(
    context: Context,
    reminderId: Int = REMINDER_NOTIFICATION_REQUEST_CODE
) {
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val intent = Intent(context, AlarmReceiver::class.java).let { intent ->
        PendingIntent.getBroadcast(
            context,
            reminderId,
            intent,
            0
        )
    }
    alarmManager.cancel(intent)
}  

2.报警接收器

在此类中,onReceive 将按计划时间触发,我们将发送通知并选择性地重新安排警报。

 class AlarmReceiver : BroadcastReceiver() {

    /**
     * sends notification when receives alarm
     * and then reschedule the reminder again
     * */
    override fun onReceive(context: Context, intent: Intent) {
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager

        notificationManager.sendReminderNotification(
            applicationContext = context,
            channelId = context.getString(R.string.reminders_notification_channel_id)
        )
        // Remove this line if you don't want to reschedule the reminder
        RemindersManager.startReminder(context.applicationContext)
    }
}

fun NotificationManager.sendReminderNotification(
    applicationContext: Context,
    channelId: String,
) {
    val contentIntent = Intent(applicationContext, MainActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(
        applicationContext,
        1,
        contentIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
    val builder = NotificationCompat.Builder(applicationContext, channelId)
        .setContentTitle(applicationContext.getString(R.string.title_notification_reminder))
        .setContentText(applicationContext.getString(R.string.description_notification_reminder))
        .setSmallIcon(R.drawable.ic_logo)
        .setStyle(
            NotificationCompat.BigTextStyle()
                .bigText(applicationContext.getString(R.string.description_notification_reminder))
        )
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)

    notify(NOTIFICATION_ID, builder.build())
}

const val NOTIFICATION_ID = 1  

然后我们需要在 manifest .xml 文件中注册它。

 <receiver
    android:name="your.package.AlarmReceiver"
    android:enabled="true" />  

最后我们将创建通知通道并启动提醒…

 override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    createNotificationsChannels()
    RemindersManager.startReminder(this)
}

 private  fun createNotificationsChannels() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            getString(R.string.reminders_notification_channel_id),
            getString(R.string.reminders_notification_channel_name),
            NotificationManager.IMPORTANCE_HIGH
        )
        ContextCompat.getSystemService(this, NotificationManager::class.java)
            ?.createNotificationChannel(channel)
    }
}  

提示 :在 Application 类中创建通知通道更有意义。

3. 引导接收器

当用户关闭他的手机时,您之前安排的所有警报都会消失,因此要重新启动它们,我们可以使用 BootReceiver 将在用户启动手机时触发。

 class BootReceiver : BroadcastReceiver() {
    /*
    * restart reminders alarms when user's device reboots
    * */
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == "android.intent.action.BOOT_COMPLETED") {
            RemindersManager.startReminder(context)
        }
    }
}  

当然,在 manifest.xml 文件中声明它。

 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><application
   .../>
    <receiver
        android:name="your.package.BootReceiver"
        android:enabled="true"
        android:exported="false">

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

    </receiver>
</application>  

都搞定了,试试看。

而已。 我希望你喜欢这个博客

文章来源:智云一二三科技

文章标题:七爪源码:Android——每天在特定时间重复通知

文章地址:https://www.zhihuclub.com/188834.shtml

关于作者: 智云科技

热门文章

网站地图