Android系统原生应用解析之桌面闹钟及相关原理应用之时钟任务的应用(一)

前段时间我一个朋友在面试回来问我:那个公司要5天之内完成一个项目,功能包括每天早上6点开始执行定时任务,大批量图片上传,大批量数据库同步。我心想,后两个功能还好说,可就是每天早上6点开始执行的这种定时任务如何搞定?


有了问题,自然要琢磨怎么解决,如果接触的知识面不够,或者没有系统的学习Android API,例如不知道AlarmManager,自然是不知道如何启动定时任务的,当时我也不知道这个的存在,突然心头一闪,那手机上的闹钟可不就是定时任务吗?


多亏了这心头一闪,知道从系统闹钟看看一个闹钟这种标准的定时任务是如何完成的,正好手中刚刚下载完完整的安卓源码,也编译通过了,在源码的目录/packages/中找到了DeskClock文件夹,一看便知是闹钟了。


为了不破坏原生系统的完整性,我将这个工程拷了出来,导入了Studio进行分析,看看如何启动一个定时任务(我自己心里是觉得应该不会有一个服务在后台一直跑着用来监控时间),导入Studio之后进行简单的环境配置编译,跑了起来:

不得不说原生应用还是很漂亮的,为了达到我们的研究目的,我们只选择一个闹钟是如何被创建以及是如何被响应的。


首先我们需要找到一个闹钟任务是如何被创建及打开的,我没有直接去找闹钟是如何创建的,我去找了闹钟是如何被打开的,因为在item上有个开关,我找到了那个开关:

这个开关位于com.android.deskclock.AlarmClockFragment内,AlarmClockFragment内含有一个Adapter内部类,在Adapter的getView方法中找到了这个小开关的触发事件:

		@Overridepublic View getView(int position, View convertView, ViewGroup parent) {...View v;if (convertView == null) {v = newView(mContext, getCursor(), parent);} else {v = convertView;}bindView(v, mContext, getCursor());return v;}...@Overridepublic void bindView(final View view, Context context, final Cursor cursor) {final Alarm alarm = new Alarm(cursor);Object tag = view.getTag();...final CompoundButton.OnCheckedChangeListener onOffListener = new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton compoundButton, boolean checked) {if (checked != alarm.enabled) {setDigitalTimeAlpha(itemHolder, checked);alarm.enabled = checked;asyncUpdateAlarm(alarm, alarm.enabled);}}};...itemHolder.onoff.setOnCheckedChangeListener(onOffListener);...}
onOffListener引用的对象便是闹钟开关的实现逻辑了,它调用了asyncUpdateAlarm方法:

		private void asyncUpdateAlarm(final Alarm alarm, final boolean popToast) {final Context context = AlarmClockFragment.this.getActivity().getApplicationContext();final AsyncTask<Void, Void, AlarmInstance> updateTask = new AsyncTask<Void, Void, AlarmInstance>() {@Overrideprotected AlarmInstance doInBackground(Void... parameters) {ContentResolver cr = context.getContentResolver();// Dismiss all old instancesAlarmStateManager.deleteAllInstances(context, alarm.id);// Update alarmAlarm.updateAlarm(cr, alarm);if (alarm.enabled) {return setupAlarmInstance(context, alarm);}return null;}@Overrideprotected void onPostExecute(AlarmInstance instance) {if (popToast && instance != null) {AlarmUtils.popAlarmSetToast(context, instance.getAlarmTime().getTimeInMillis());}}};updateTask.execute();}
它内部执行了一个异步任务,任务的核心调用的是setupAlarmInstance:

		private static AlarmInstance setupAlarmInstance(Context context, Alarm alarm) {ContentResolver cr = context.getContentResolver();AlarmInstance newInstance = alarm.createInstanceAfter(Calendar.getInstance());newInstance = AlarmInstance.addInstance(cr, newInstance);// Register instance to state managerAlarmStateManager.registerInstance(context, newInstance, true);return newInstance;}
这里的意思是,将闹钟数据添加到ContentProvider中,以便将数据共享给其它应用。接下来调用了AlarmStateManager.registerInstance:

		public static void registerInstance(Context context, AlarmInstance instance,boolean updateNextAlarm) {...// The caller prefers to handle updateNextAlarm for optimizationif (updateNextAlarm) {updateNextAlarm(context);}}
这段代码中本来有很长的一段代码,用来判断闹钟的各个时间段的执行情况,为了避免干扰我们的主流程,对代码进行了删减处理,我们从上一段代码可知,这里的updateNextAlarm值为true,进入到updateNextAlarm:

    public static void updateNextAlarm(Context context) {...AlarmNotifications.registerNextAlarmWithAlarmManager(context, nextAlarm);}...public static void registerNextAlarmWithAlarmManager(Context context, AlarmInstance instance)  {// Sets a surrogate alarm with alarm manager that provides the AlarmClockInfo for the// alarm that is going to fire next. The operation is constructed such that it is ignored// by AlarmStateManager.AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);int flags = instance == null ? PendingIntent.FLAG_NO_CREATE : 0;PendingIntent operation = PendingIntent.getBroadcast(context, 0 /* requestCode */,AlarmStateManager.createIndicatorIntent(context), flags);if (instance != null) {long alarmTime = instance.getAlarmTime().getTimeInMillis();// Create an intent that can be used to show or edit details of the next alarm.PendingIntent viewIntent = PendingIntent.getActivity(context, instance.hashCode(),createViewAlarmIntent(context, instance), PendingIntent.FLAG_UPDATE_CURRENT);AlarmManager.AlarmClockInfo info =new AlarmManager.AlarmClockInfo(alarmTime, viewIntent);alarmManager.setAlarmClock(info, operation);} else if (operation != null) {alarmManager.cancel(operation);}}
updateNextAlarm方法中通过调用AlarmNotifications类中的registerNextAlarmWithAlarmManager方法将下一次的闹铃注册到AlarmManager,歪果仁的命名清晰易懂啊!registerNextAlarmWithAlarmManager的方法内部则是我们真正需要看到的,首先是获取到了系统中的AlarmManager对象,接着创建了一个PendingIntent对象operation,这个对象用来执行当闹钟时间到的时候,需要调用的广播类,我们看看AlarmStateManager.createIndicatorIntent(context)方法内部是如何实现的:

    /*** Creates an intent that can be used to set an AlarmManager alarm to set the next alarm* indicators.*/public static Intent createIndicatorIntent(Context context) {return new Intent(context, AlarmStateManager.class).setAction(INDICATOR_ACTION);}public final class AlarmStateManager extends BroadcastReceiver {...}
内部则是简单的new了一个Intent,这个显式的意图指定的是AlarmStateManager,而AlarmStateManager则继承的是BroadcastReceiver,这时,我们很明白,当时钟任务触发的时候会调用我们这个AlarmStateManager的广播,其实AlarmStateManager这个类的内部是有很多代码了,这里被我删减了,以便看代码清晰。


回到上一段方法中继续往下走,又看到在创建PendingIntent的对象viewIntent,这个对象则是用来当时钟任务启动时显示的界面,我们在使用闹钟的时候会弹出一个界面让我们关掉,那与我们交互的Activity就是这里被设定的,createViewAlarmIntent方法内部创建的是一个显式的Activity,有兴趣的可以进去看看。


一切设定好之后,再通过AlarmManager的方法setAlarmClock将我们的时钟任务注册到系统,系统会在我们设定的时间到达之后调用相关的Intent对象。

除了可以使用setAlarmClock方法注册一个时钟任务之外,我们还可以通过cancel方法将这个任务取消。

好,这就是闹钟的基本实现原理。接下里详细描述一下AlarmManager的各种时钟任务应用。








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

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

相关文章

配送交付时间轻量级预估实践

1. 背景 可能很多同学都不知道&#xff0c;从打开美团App点一份外卖开始&#xff0c;然后在半小时内就可以从骑手小哥手中拿到温热的饭菜&#xff0c;这中间涉及的环节有多么复杂。而美团配送技术团队的核心任务&#xff0c;就是将每天来自祖国各地的数千万份订单&#xff0c;迅…

论文浅尝 | 面向 cQA 的跨语言问题检索方法

论文笔记整理&#xff1a;谭亦鸣&#xff0c;东南大学博士生&#xff0c;研究方向为知识库问答。来源&#xff1a;WWW2019链接&#xff1a;https://dl.acm.org/citation.cfm?doid3308558.3313502本文提出了一种面向cQA的跨语言问题检索方法&#xff0c;旨在对于给定的问题检索…

揭秘Python并发编程——协程

原文链接&#xff1a;https://baijiahao.baidu.com/s?id1649450510185145678&wfrspider&forpc Python并发编程一直是进阶当中不可跨越的一道坎&#xff0c;其中包括进程、线程、协程&#xff0c;今天我们就来聊一聊协程。协程的定义很简单&#xff0c;从头到尾只有一条…

LeetCode 804. 唯一摩尔斯密码词(哈希+set)

文章目录1. 题目2. 解题1. 题目 国际摩尔斯密码定义一种标准编码方式&#xff0c;将每个字母对应于一个由一系列点和短线组成的字符串&#xff0c; 比如: “a” 对应 “.-”, “b” 对应 “-…”, “c” 对应 “-.-.”, 等等。 为了方便&#xff0c;所有26个英文字母对应摩尔…

计算机基础晦涩难懂?那你是没看他的图解文章!

这年头&#xff0c;写计算机基础的人好之又少&#xff0c;能把枯燥无味的计算机基础写的通俗易懂的人更是少&#xff0c;而就有一位叫「小林coding」的公众号横空出世&#xff0c;真的是一股清流&#xff0c;他酷爱「图解」计算机基础文章&#xff0c;真正做到了图解&#xff0…

Litho在美团动态化方案MTFlexbox中的实践

1. MTFlexbox MTFlexbox是美团内部应用的非常成熟的一种跨平台动态化解决方案&#xff0c;它遵循了CSS3中提出的Flexbox规范来抹平多平台的差异。MTFlexbox适用于重展示、轻交互的业务场景&#xff0c;与现有HTML、React Native、Weex等跨平台方案相比&#xff0c;MTFlexbox具备…

服务器部署docker

服务器部署docker docker简介## 可以把docker是一个容器&#xff0c;可以让开发者将自己的项目部署到这个容器中&#xff0c;最常用的场景是将自己的后端项目部署到服务器的时候会将其打入docker镜像中&#xff0c;可以理解为一个开销更小的虚拟机。 docker好处## 可以轻易地让…

LeetCode 461. 汉明距离(异或^ 与)

文章目录1. 题目2. 解题1. 题目 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 给出两个整数 x 和 y&#xff0c;计算它们之间的汉明距离。 注意&#xff1a; 0 ≤ x, y < 2^31.示例:输入: x 1, y 4输出: 2解释: 1 (0 0 0 1) 4 (0 1 0 0)…

论文浅尝 | NumNet: 一种带有数学推理的机器阅读理解模型

论文笔记整理&#xff1a;吴林娟。来源&#xff1a;EMNLP2019论文链接&#xff1a;https://arxiv.org/pdf/1910.06701.pdf开放源码&#xff1a;https://github.com/ranqiu92/NumNet概述本文提出了一个将数学推理融入机器阅读理解的模型——NumNet&#xff0c;其中利用数字感知的…

2021,你好哇!发个小红包可好~

小伙伴们&#xff0c;大家新年快乐哇&#xff5e;凡是过往皆序章&#xff0c;所有将来皆可盼。2020磨难太多&#xff0c;就祝大家2021多吃不胖叭&#xff01;原创即正义&#xff01;2020年对卖萌屋来说是非常不平凡的一年。这一年的时间里&#xff0c;卖萌屋坚守内容创作的初心…

降低软件复杂性一般原则和方法

一、前言 斯坦福教授、Tcl语言发明者John Ousterhout 的著作《A Philosophy of Software Design》[1]&#xff0c;自出版以来&#xff0c;好评如潮。按照IT图书出版的惯例&#xff0c;如果冠名为“实践”&#xff0c;书中内容关注的是某项技术的细节和技巧&#xff1b;冠名为“…

LeetCode 657. 机器人能否返回原点

文章目录1. 题目2. 解题1. 题目 在二维平面上&#xff0c;有一个机器人从原点 (0, 0) 开始。给出它的移动顺序&#xff0c;判断这个机器人在完成移动后是否在 (0, 0) 处结束。 移动顺序由字符串表示。字符 move[i] 表示其第 i 次移动。机器人的有效动作有 R&#xff08;右&am…

论文浅尝 | 基于图匹配神经网络的跨语言知识图对齐 (ACL 2019)

本文转载自公众号&#xff1a;PaperWeekly。作者&#xff1a;王文博&#xff0c;哈尔滨工程大学硕士生&#xff0c;研究方向为知识图谱、表示学习。动机在本篇文章之前&#xff0c;跨语言知识图谱对齐研究仅依赖于从单语知识图谱结构信息中获得的实体嵌入向量。并且大多数研究将…

谷歌大改Transformer注意力,速度、内存利用率都提上去了-新的 Transformer 架构——Performer

原文地址&#xff1a;https://www.jiqizhixin.com/articles/2020-10-28-10 Transformer 有着巨大的内存和算力需求&#xff0c;因为它构造了一个注意力矩阵&#xff0c;需求与输入呈平方关系。谷歌大脑 Krzysztof Choromanski 等人最近提出的 Performer 模型因为随机正正交特性…

一文搞懂 PyTorch 内部机制

文 | ArchWalker译者序&#xff1a;这篇博文是一篇非常新的介绍PyTorch内部机制的文章&#xff0c;作者Edward Z Yang来自于Stanford大学&#xff0c;是PyTorch的核心开发者之一。文章中介绍了如何阅读PyTorch源码和扩展PyTorch的技巧。目前讲PyTorch底层的文章不多&#xff0c…

美团外卖商家端视频探索之旅

背景 美团外卖至今已迅猛发展了六年&#xff0c;随着外卖业务量级与日俱增&#xff0c;单一的文字和图片已无法满足商家的需求&#xff0c;商家迫切需要更丰富的商品描述手段吸引用户&#xff0c;增加流量&#xff0c;进而提高下单转化率和下单量。商品视频的引入&#xff0c;在…

Android官方开发文档Training系列课程中文版:构建第一款安卓应用之工程创建

这节课你将会学到&#xff1a; - 使用Android Studio创建一个工程。 - 使用命令行工具创建一个工程。 你也应该阅读&#xff1a; - 安装SDK - 管理工程 安卓项目包含了包括源代码在内的所有文件。Android SDK工具使我们很轻松的创建一个包含一系列默认目录和文件的的安卓工…

LeetCode 1051. 高度检查器

文章目录1. 题目2. 解题1. 题目 学校在拍年度纪念照时&#xff0c;一般要求学生按照 非递减 的高度顺序排列。 请你返回至少有多少个学生没有站在正确位置数量。该人数指的是&#xff1a;能让所有学生以 非递减 高度排列的必要移动人数。 示例&#xff1a;输入&#xff1a;[…

论文浅尝 | 基于动态记忆的原型网络进行元学习以实现少样本事件探测

本文转载自公众号&#xff1a;浙大KG。 论文题目&#xff1a;Meta-Learning with Dynamic-Memory-Based Prototypical Network for Few-Shot Event Detection本文作者&#xff1a;邓淑敏&#xff0c;浙江大学在读博士&#xff0c;研究方向为低资源条件下知识图谱自动化构建关键…

字节跳动破局联邦学习:开源Fedlearner框架,广告投放增效209%

本文介绍了字节跳动联邦学习技术团队自研的联邦学习平台 Fedlearner &#xff0c;分享了该平台的技术实现与落地应用体验&#xff0c;向读者展示了字节跳动如何联邦学习落地难题的突破点。作为分布式的机器学习范式&#xff0c;联邦学习能够有效解决数据孤岛问题&#xff0c;让…