Android Alarm闹钟API使用心得

2023-11-13

前言

有什么办法可以在不打开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条通知,然后自动取消不再重复,还有更多的扩展用法就自己摸索啦!本文只是简单的使用一下~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android Alarm闹钟API使用心得 的相关文章

随机推荐

  • Swagger注解详解

    目录 1 Api 2 APiOperation 3 ApiImplicitParams 4 ApiResponses 5 ApiModel 6 ApiModelProperty 这里是说明常用注解的含义和基本用法 也就是说已经对swagge
  • 使用反射对单例模式进行攻击的讨论

    我们都知道在单例模式中 对构造函数进行私有化private修饰 保证了类不能使用new进行对象的实例化 但是如果使用反射获取构造函数 在进行实例化就会导致private失效 作者用中文作为类名 请读者勿怪 纯属喜好 工作中是不允许的哦 ja
  • 为什么我们要考虑线性规划的对偶问题?

    文章转自 https www zhihu com question 26658861 版权归原作者
  • 更改计算机bios密码怎么办,计算机BIOS通用密码的修改

    电脑信息的保密一直是一个重要的话题 众所周知 在BIOS设置菜单中 有两个密码设置栏目 Supervisor Password 超级用户密码 和User Password 一般用户密码 可以说是电脑资料保密的第一道防线 这两组密码搭配BIO
  • 2023届秋招,我重新认清了自己

    仅记录个人经历 充满主观感受 甚至纯属虚构 仅供参考 杠就是你对 本想毕业再写 但是考虑到等毕业了 24秋招的提前批就快开始了 大概就来不及了 正好现在有点时间 陆陆续续的写了出来 个人情况 学历 苏北二本 加南京某211 现在二本三本合并
  • 【转载】PMOS,NMOS

    NMOS保证截止 发生在G电位小于等于S PMOS保证截止 发生在G电位大于等于S 寄生二极管是因为基底与Source连接形成的PN结
  • JQuery获取多个name相同select/input框的value值

    JavaWeb jsp页面 使用c forEach 循环多个select下拉框 name相同获取被选中的值 jsp
  • 虚拟机连接不上网络,解决办法

    虚拟机连接不上网络解决思路 简单的介绍了VM虚拟机常用的三种网络连接方式 一般用NAT方式虚拟机就很容易上网的 所以一般没有特殊要求推荐用NAT方式 1 桥接 就是把虚拟机通过VMnet0桥接到主机的本地连接 现在虚拟机是通过VMnet0与
  • NestedScrollView RecycleView 嵌套 滑动冲突

    NestedScrollView RecycleView 嵌套 滑动冲突 场景描述 效果演示 实现思路 问题和优化 优化 参考文档 场景描述 使用NestedScrollView 内嵌RecycleView时 当用户上滑时 NestedSc
  • 扫雷(初阶)

    细节决定成败 目录 打印菜单 创建棋盘并打印 埋雷 排雷 判断输赢 扫雷游戏规则 当棋盘内除去有地雷的格子 全被排除那么就会胜利 反之当玩家排到一颗雷就会被炸西 根据三子棋的写法 扫雷程序也是需要分为几个文件 声明函数的文件 定义函数的文件
  • Samba+ldap认证(LDAP搭建)

    Samba ldap认证 题目 LDAP 一 关闭Selinux跟防火墙 二 安装ldap 三 设置slapd密码 四 修改配置文件 五 启动服务ldap服务并导入基本Schema 六 导入基础数据库和用户组 七 导入用户 八 修改配置文件
  • Qt的内存管理机制

    当我们在使用Qt时不可避免得需要接触到内存的分配和使用 即使是在使用Python Golang这种带有自动垃圾回收器 GC 的语言时我们仍然需要对Qt的内存管理机制有所了解 以更加清楚的认识Qt对象的生命周期并在适当的时机加以控制或者避免进
  • go语言连接mysql数据库,并验证连通性

    go语言连接mysql数据库 并验证连通性 package main import database sql sql Open加载包 github com go sql driver mysql 没用到包里的内容但是需要加载一下这个包 lo
  • 2023年MathorCup 高校数学建模挑战赛-A 题 量子计算机在信用评分卡组合优化中的应用-思路详解(模型代码答案)

    一 题目简析 运筹优化类题目 不同于目标规划 该题限制了必须使用量子退火算法QUBO来进行建模与求解 本身题目并不难 但是该模型较生僻 给出的参考文献需要耗费大量时间去钻研 建议擅长运筹类题目且建模能力强的队伍选择 二 逐问思路分享 问题
  • 连续子序列最大最小值差额最大化

    本文为最近做过的一道编程笔试题 代码实现方式多种多样 此处本人提供的代码可以获得正确解 仅供大家参考 目录 一 题目描述 二 实现代码程序 三 测试结果截图 一 题目描述 题目描述 Mike在一家律师事务所工作 他的老板Harvey分配给他
  • Git vscode-git ssh新建和验证

    在push本地仓库遇到的SSH公钥认证问题 Warning Permanently added github com xx xxx xxx xxx ECDSA to the list of known hosts git github co
  • 牛客网基础语法1~10题

    牛客网基础语法1 10题 前言 今天是咱们第一期刷牛客网上的题目 目标 掌握基础编程 打牢基础知识 有一定的编程思想 鸡汤 读不在三更五鼓 功只怕一曝十寒 先干为敬 大家随意 第一题 include
  • tensorflow报错raise RuntimeError('The Session graph is empty.  Add operations to the ' RuntimeError:

    tensorflow报错raise RuntimeError The Session graph is empty Add operations to the RuntimeError The Session graph is empty
  • 3.5安装ideay、快捷键

    注册账号 网址 https www jetbrains com 绑定激活码 网址 https www jetbrains com store redeem Alt Enter 快速补全 Ctrl shift O 清理导包 去除无用的包 Ct
  • Android Alarm闹钟API使用心得

    前言 有什么办法可以在不打开App的时候 也能够触发一些操作呢 比如说发送通知 解决这个需求的办法有很多种选择 比如说官方推荐的WorkManager API 可以在后台执行一次性 耗时 定时的任务 但WorkManager是严格遵循电池优