Android Alarm闹钟API使用心得

news2024/12/23 3:50:51

前言

有什么办法可以在不打开App的时候,也能够触发一些操作呢?比如说发送通知,解决这个需求的办法有很多种选择,比如说官方推荐的WorkManager API,可以在后台执行一次性、耗时、定时的任务,但WorkManager是严格遵循电池优化策略的,也就是并不精准,虽然你可以设置为加急任务,但也还是不能满足精准时间。

所以,想要在精准时间触发通知,就只能使用Alarm了。

前置准备

理清自己需要的闹钟类型,首先选择闹钟类型:

“经过的时间”闹钟:

从设定闹钟开始计时,经过特定的时间触发的闹钟,与时区、语言无关

实时闹钟:

基于世界协调时间(UTC),一般情况下,按照现实时间触发的闹钟,但该方法会受到用户改变系统时间时受到影响。

是否唤醒CPU

选择完闹钟类型后,还需确定闹钟是否能够唤醒设备,正常情况下,关闭屏幕后一段时间,CPU就会陷入“睡眠状态”,非唤醒闹钟会等待CPU“醒来”的时候才一起触发,唤醒闹钟则会直接唤醒CPU直接触发。

实现定时不重复闹钟

我们先来测试一个定时,能够唤醒CPU仅此一次的闹钟,来发送一条通知

如果target SDK为31以上,且没有被加入电池优化策略白名单,则还需要在manifest文件中添加精确闹钟权限,该权限会在安装时授予。

如果target SDK为33以上,发送通知需要通知权限,该权限需要在发送通知前主动向系统请求,不然发不了通知


    <!--通知权限-->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <!--闹钟权限-->
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

首先先创建一个广播接收器,这个广播接收器用来执行闹钟时间到的时候,我们需要执行的逻辑代码,例如发送一条通知(通知权限的请求本文不再书写,默认视为你已获得通知权限),本文使用的广播接收器是MyAlarmReceiver,闹钟时间到的时候,会发送一条通知,标题是My notification,内容为Hello World! 加一个随机数。

import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;

import java.util.Random;


public class MyAlarmReceiver extends BroadcastReceiver {

    String CHANNEL_LOCATION_ID = "myAlarm";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("TAG", "onReceive: NOTIFY_ALARM" );
        int count = new Random().nextInt(100);
        NotificationManager notificationManager = ContextCompat.getSystemService(context, NotificationManager.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel(CHANNEL_LOCATION_ID, CHANNEL_LOCATION_ID, importance);
            channel.setDescription("test");
            notificationManager.createNotificationChannel(channel);
        }
        //通知的普通点按操作
        Intent intentN = new Intent(context, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 202, intentN, PendingIntent.FLAG_IMMUTABLE);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context,CHANNEL_LOCATION_ID)
                .setSmallIcon(R.drawable.notification_icon_blue)//发送通知必须指定一个smallIcon,背景需透明
                .setContentTitle("My notification")
                .setContentText("Hello World!"+ count)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setContentIntent(pendingIntent);
        //发送通知,检查权限
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        NotificationManagerCompat.from(context).notify(count, builder.build());
    }
}

当然,别忘了在AndroidManifest.xml中注册我们新增的receiver

<application>
    ...
        <receiver android:name=".MyAlarmReceiver"
            android:exported="false"
            >
        </receiver>
</application>

 想要设置一个闹钟,就需要给系统的闹钟服务发送一个类似“预定”一样的意图,下面这段代码我设置在17点20分0秒的闹钟,时间到的时候,系统的闹钟服务就会发送一条广播到我们的广播接收器MyAlarmReceiver,根据接收到的广播进行对应的逻辑操作。

    private AlarmManager alarmManager;
    private PendingIntent pendingIntent;
    private String packageName;
    ...
    private void initAlarm(Context context){
        Intent intent = new Intent(context,MyAlarmReceiver.class);
        pendingIntent = PendingIntent.getBroadcast(
                context,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );
        alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    }

    private void setOneAlarm(){
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,17);
        calendar.set(Calendar.MINUTE,20);
        calendar.set(Calendar.SECOND,0);
        Log.i("TAG", "notify time: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime()));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Log.w("TAG", "alarm: must" );
         alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pendingIntent);
        }else{
            Log.w("TAG", "alarm: normal" );
            alarmManager.setExact(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pendingIntent);
        }
    }

好了,前置准备我们都做完了 ,只要触发setOneAlarm()方法就设定了一个闹钟,系统的闹钟服务会在17点20分0秒发送一条广播,触发MyAlarmReceiver类中的onReceive()方法,就能发送一条通知了。

但是

你的手机如果是三星或谷歌的pixel,以上方法就已经足够了。如果你的手机是国产定制化过的系统

例如小米的MIUI,华为,VIVO,OPPO等手机的话,我们还需要获取由定制系统接管的权限,拿小米的MIUI举例,这个权限叫做 自启动权限 没有这个权限的情况下不一定能触发这个闹钟(大部分时间都无法触发)

打开App的应用设置页面我们就能看到这个权限,其他系统也基本同理,不在应用信息中就在手机管家中

 把这个自启动开关打开,再去设定闹钟,就能触发一个定时闹钟了。

实现重复闹钟且自动取消

重复闹钟的实现很简单,只需要设定闹钟的时候使用setRepeating方法,就能指定第一次闹钟的时间,以及重复的间隔。但想要自动取消该怎么办呢?

想要取消闹钟,就需要调用闹钟服务的cancel()方法,且传递一个一样的pendingIntent

其实实现方法很简单,只需要再定一个取消的闹钟就行了。

给我们的Receiver区分一下不同的闹钟做什么事,根据intent中的"enable"值来区分是发送通知还是取消闹钟。

为1的时候,就发送通知,为0的时候就取消闹钟。

import android.Manifest;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;

import java.util.Random;


public class MyAlarmReceiver extends BroadcastReceiver {

    public static final String NOTIFY_ALARM = "tdsss.myalarmnotify1.MyAlarmReceiver";
    public static final String CANCEL_ALARM = "tdsss.myalarmnotify1.cacelAlarm";

    String CHANNEL_LOCATION_ID = "myAlarm";

    @Override
    public void onReceive(Context context, Intent intent) {
        int isEnable = intent.getIntExtra("enable",-1);
        Log.e("TAG", "alarm onReceive: " );
        switch (isEnable){
            case 1:
                Log.e("TAG", "onReceive: NOTIFY_ALARM" );
                int count = new Random().nextInt(100);
                NotificationManager notificationManager = ContextCompat.getSystemService(context, NotificationManager.class);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    int importance = NotificationManager.IMPORTANCE_DEFAULT;
                    NotificationChannel channel = new NotificationChannel(CHANNEL_LOCATION_ID, CHANNEL_LOCATION_ID, importance);
                    channel.setDescription("test");
                    notificationManager.createNotificationChannel(channel);
                }
                //通知的普通点按操作
                Intent intentN = new Intent(context, MainActivity.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(context, 202, intentN, PendingIntent.FLAG_IMMUTABLE);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context,CHANNEL_LOCATION_ID)
                .setSmallIcon(R.drawable.notification_icon_blue)//发送通知必须指定一个smallIcon,背景需透明
                .setContentTitle("My notification")
                .setContentText("Hello World!"+ count)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setContentIntent(pendingIntent);
                //发送通知,检查权限
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                NotificationManagerCompat.from(context).notify(count, builder.build());
                break;
            case 0:
                Log.e("TAG", "onReceive: CANCEL_ALARM" );
                Intent cancel = new Intent(context,MyAlarmReceiver.class);
                cancel.setAction(MyAlarmReceiver.NOTIFY_ALARM);
                intent.putExtra("enable",1);
                PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(
                        context,
                        0,
                        cancel,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
                );
                AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                alarmManager.cancel(cancelPendingIntent);
                break;
            default:
                Log.e("TAG", "onReceive: " );
                break;
        }
    }
}

    private void setRepeatAlarmAndCancel(){
        alarmManager.cancel(pendingIntent);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,17);
        calendar.set(Calendar.MINUTE,30);
        calendar.set(Calendar.SECOND,0);
        Log.e("TAG", "notify time: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime()));
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),1000*60*1,pendingIntent);
        //cancel alarm
        Calendar cancelTime = Calendar.getInstance();
        cancelTime.set(Calendar.HOUR_OF_DAY,17);
        cancelTime.set(Calendar.MINUTE,35);
        cancelTime.set(Calendar.SECOND,0);
        Log.e("TAG", "cancel time: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cancelTime.getTime()));
        Intent cancelIntent = new Intent(getContext(),MyAlarmReceiver.class);
        cancelIntent.setAction(MyAlarmReceiver.CANCEL_ALARM);
        cancelIntent.putExtra("enable",0);
        PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(getContext(),2,cancelIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Log.e("TAG", "alarm: must" );
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,cancelTime.getTimeInMillis(),cancelPendingIntent);
        }else{
            Log.e("TAG", "alarm: normal" );
            alarmManager.setExact(AlarmManager.RTC_WAKEUP,cancelTime.getTimeInMillis(),cancelPendingIntent);
        }
    }

设定完以后,系统就会在17点30分0秒时,发送5条通知,然后自动取消不再重复,还有更多的扩展用法就自己摸索啦!本文只是简单的使用一下~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/882739.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Maven教程】(一)入门介绍篇:Maven基础概念与其他构建工具:理解构建过程与Maven的多重作用,以及与敏捷开发的关系 ~

Maven入门介绍篇 1️⃣ 基础概念1.1 构建1.2 maven对构建的支持1.3 Maven的其他作用 2️⃣ 其他构建工具2.1 IDE2.2 Make2.3 Ant2.4 Jenkins 3️⃣ Maven与敏捷开发&#x1f33e; 总结 1️⃣ 基础概念 "Maven"可以翻译为 “知识的积累者” 或 “专家”。这个词源于波…

【第三阶段】kotlin语言空合并操作符

1.空操作符&#xff1f;&#xff1a; xxx?:“如果是null执行” 如果xxx是null&#xff0c;就执行?:后面的逻辑&#xff0c;如果不是null就执行&#xff1f;&#xff1a;前面的逻辑&#xff0c;后面的不在执行 fun main() {var name:String?"kotlin" namenullvar …

【数据结构OJ题】反转链表

原题链接&#xff1a;https://leetcode.cn/problems/reverse-linked-list/description/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 方法一&#xff1a;三指针翻转法 使用三个结构体指针n1&#xff0c;n2&#xff0c;n3&#xff0c;原地修改结点…

移动端直播相关技术总结

一、直播APP原理 二、直播APP架构 三、直播APP实现流程 四、流媒体开发 流媒体模块架构 流媒体相关基础知识 帧&#xff1a;每一帧代表一幅静止的图像 GOP&#xff1a;Group of Pictures&#xff0c;画面组&#xff0c;一个GOP就是一组连续的画面&#xff0c;很多帧的集合 码率…

腾讯Perfdog支持Windows PC端体验性能测试

一、背景 最近在做抖音的小玩法&#xff0c;其基于unity引擎&#xff0c;然后挂载到直播伴侣。以及Perfdog近期也支持了Windows的测试&#xff0c;所以做一个体验测试。 二、如何做 查看PC端的支持&#xff0c;目前是beat版本 选择或搜索自己需要的对应的程序&#xff0c;如…

Allegro172版本如何在联机模式下使用skill文件操作指导

Allegro172版本如何在Symphony模式下使用skill文件操作指导 在用Allegeo172版本进行PCB设计的时候,有时会用到Symphony模式进行多人实时在线协同设计,如下图 这个模式设计起来可以提升设计速度,但是也有一个缺陷,就是不能使用skill文件 如下图,当调用skill的时候就会出现…

Aspera替代方案:探索这些安全且可靠的文件传输工具

科技的发展日新月异&#xff0c;文件的传输方式也在不断地更新换代。传统的邮件附件、FTP等方式已经难以满足人们对于传输速度和安全性的需求了。近年来&#xff0c;一些新兴的文件传输工具受到了人们的关注&#xff0c;其中除了知名的Aspera之外&#xff0c;还有许多可靠安全的…

全志H616交叉编译,orangepi-zero2

文章目录 交叉编译是什么为什么需要交叉编译&#xff1f; 宿主机和目标机所需工具解压编译工具临时有效&#xff0c;配置环境变量&#xff08;切换终端无效&#xff09;永久有效&#xff0c;配置环境变量大功告成开始测试拷入文件测试结束 交叉编译是什么 交叉编译&#xff1a…

《Learning Combinatorial Optimization Algorithms over Graphs》阅读笔记

一.文章概述 本文提出将强化学习和图嵌入的组合以端到端地自动为图上组合优化问题设计贪心启发式算法&#xff0c;以避免设计传统算法所需要的大量专业知识和试错。学得的贪心策略行为类似增量构造解决方案的元算法&#xff0c;动作由解决方案当前状态上的图嵌入网络确定。作者…

『清安无别事-赠书01期』|〖测试设计思想〗

目录 &#x1f9e1; 内容简介 &#x1f49b; 作者简介 &#x1f496; 本书内容|目录 &#x1f497; 读后感想 &#x1f49d; 参与方式 购书传送门&#xff1a;测试设计思想购书传送门&#xff1a;测试设计思想购书传送门&#xff1a;测试设计思想 &#x1f9e1; 内容简介 …

Win10提醒事项如何打开?电脑上如何添加日程提醒?

有不少上班族表示自己在日常办公时&#xff0c;经常会忘记一些重要的事情&#xff0c;例如领导安排给自己的任务、会议安排、项目截止日期等。为了避免自己忘记工作事项&#xff0c;很多人都想要在电脑上设置提醒事项或添加日程提醒。那么Win10提醒事项如何打开呢&#xff1f;P…

LCS最大公共子序列 与 LIS最大递增子序列

LCS Largest Common Subsequence 最大公共子序列 /* Input s1 s2//两个字符串Output length//长度 ans//具体字母 */ #include<iostream> using namespace std; int main() {string s1,s2;cin>>s1>>s2;int dp[100][100]{0};//dp[i][j]表示s1取前i位&#x…

软件工程概述-架构师(三)

软件工程概述&#xff08;老版&#xff09; 软件开发生命周期&#xff1a; 软件定义时期&#xff1a;包括 可行性研究和详细需求分析过程&#xff0c;任务是软件工程必需完成的目标&#xff0c;具有可行问题分析、可行性研究、需求分析等。软件开发时期&#xff1a;软件的 设…

css浮动(为什么要清除浮动?清除浮动有哪几种方式?)

为什么要清除浮动&#xff1f; 清除浮动主要是为了清除浮动元素造成的影响&#xff0c;使浮动元素不会影响其后元素的布局 防止父元素高度塌陷&#xff1a;当元素浮动后&#xff0c;它会脱离一个标准文档流&#xff0c;不再占用原先的布局空间。如果一个父元素内只有浮动元素&a…

SpringBoot中优雅的实现隐私数据脱敏(提供Gitee源码)

前言&#xff1a;在实际项目开发中&#xff0c;可能会对一些用户的隐私信息进行脱敏操作&#xff0c;传统的方式很多都是用replace方法进行手动替换&#xff0c;这样会由很多冗余的代码并且后续也不好维护&#xff0c;本期就讲解一下如何在SpringBoot中优雅的通过序列化的方式去…

07 线程学习

一 qt线程角色 子线程完成与UI线程无关的工作,并且能够保持与UI线程通信 二 qt中线程 在QT中,对于 线程操作也是提供类(QThread)来进行封装,然后再学习该类的API接口 参数用途Header:#include qmake:QT += coreInherits:QObject //继承于QObject从官方文档可以看出,如果一…

每天一道leetcode:797. 所有可能的路径(图论中等深度优先遍历)

今日份题目&#xff1a; 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特定顺序&#xff09; graph[i] 是一个从节点 i 可以访问的所有节点的列表&#xff08;即从节点 i 到节…

[论文笔记]Glancing Transformer for Non-Autoregressive Neural Machine Translation

引言 这是论文Glancing Transformer for Non-Autoregressive Neural Machine Translation的笔记。 传统的非自回归文本生成速度较慢,因为需要给定之前的token来预测下一个token。但自回归模型虽然效率高,但性能没那么好。 这篇论文提出了Glancing Transformer,可以只需要一…

Verdi_traceX and autotrace

Verdi_traceX and autotrace Trace X From nWave/nTrace of from the Teporal Flow View. Show Paths on Flow ViewShow Paths on nWave 若Waveform中有X态&#xff0c;鼠标右键会有Trace X的选项&#xff1b; 会自动打开Temporal Flow View窗口&#xff0c;展示对应路径&am…

我的创作纪念日 — 嵌入式up(2023.8.15)

机缘 在2022年8月15日&#xff0c;开始了CSDN文章的编写&#xff0c;最初只想做一个知识的积累&#xff0c;毕竟对一个电子发烧友来说&#xff0c;很多东西都是迭代很快&#xff0c;且玩的次数不会很多&#xff0c;有些问题和一些解决方法&#xff0c;最好记录下来&#xff0c;…