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

简介: 前段时间我一个朋友在面试回来问我:那个公司要5天之内完成一个项目,功能包括每天早上6点开始执行定时任务,大批量图片上传,大批量数据库同步。我心想,后两个功能还好说,可就是每天早上6点开始执行的这种定时任务如何搞定? 有了问题,自然要琢磨怎么解决,如果接触的知识面不够,或者没有系统的学习...

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


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


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


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

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


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

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

		@Override
		public 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;
		}

		...

		@Override
		public 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() {
				@Override
				public 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>() {
				@Override
				protected AlarmInstance doInBackground(Void... parameters) {
					ContentResolver cr = context.getContentResolver();

					// Dismiss all old instances
					AlarmStateManager.deleteAllInstances(context, alarm.id);

					// Update alarm
					Alarm.updateAlarm(cr, alarm);
					if (alarm.enabled) {
						return setupAlarmInstance(context, alarm);
					}

					return null;
				}

				@Override
				protected 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 manager
			AlarmStateManager.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 optimization
			if (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的各种时钟任务应用。








目录
相关文章
|
11天前
|
存储 缓存 NoSQL
深入解析Redis:一种快速、高效的键值存储系统
**Redis** 是一款高性能的键值存储系统,以其内存数据、高效数据结构、持久化机制和丰富的功能在现代应用中占有一席之地。支持字符串、哈希、列表、集合和有序集合等多种数据结构,适用于缓存、计数、分布式锁和消息队列等场景。安装Redis涉及下载、编译和配置`redis.conf`。基本操作包括键值对的设置与获取,以及哈希、列表、集合和有序集合的操作。高级特性涵盖发布/订阅、事务处理和Lua脚本。优化策略包括选择合适数据结构、配置缓存和使用Pipeline。注意安全、监控和备份策略,以确保系统稳定和数据安全。
53 1
|
16天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
17天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
49 1
|
17天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
17天前
|
开发框架 安全 Android开发
探索安卓系统的新趋势:智能家居应用的蓬勃发展
随着智能家居概念的兴起,安卓系统在智能家居应用领域的应用日益广泛。本文将探讨安卓系统在智能家居应用开发方面的最新趋势和创新,以及其对用户生活的影响。
13 2
|
18天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
1天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
2 0
|
1天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
4 0
|
3天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
5天前
|
搜索推荐 开发工具 Android开发
安卓即时应用(Instant Apps)开发指南
【4月更文挑战第14天】Android Instant Apps让用户体验部分应用功能而无需完整下载。开发者需将应用拆分成模块,基于已上线的基础应用构建。使用Android Studio的Instant Apps Feature Library定义模块特性,优化代码与资源以减小模块大小,同步管理即时应用和基础应用的版本。经过测试,可发布至Google Play Console,提升用户便利性,创造新获客机会。

推荐镜像

更多