Android 系统闹钟源码解析

前言

基于Android 系统原生闹钟, 系统代码路径:

packages/apps/DeskClock

带着问题看源码,不废话进正题

问题

如何写一个闹钟应用,需要考虑哪些。我从以下几点做个简单介绍:

  1. 闹钟实现原理,如何实现动作定时触发?
  2. 闹钟alarm如何设置,包含哪些元素?
  3. 如何管理闹钟设置-alarm?
  4. 如何管理一个闹钟事件-alarmInstance?
  5. 举例分析,如何实现一个闹钟在每周一,周三,早上8点响起?
  6. 开关机,时区时间改变,来电等冲突事件都做哪些处理?

针对以上问题,详细介绍如下。只介绍大致流程,处理细节请阅读代码,注释已十分详尽。

1.闹钟实现原理,如何实现动作定时触发?
        Intent stateChangeIntent = createStateChangeIntent(context, ALARM_MANAGER_TAG, instance, newState);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
            instance.hashCode(), stateChangeIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        if (Utils.isKitKatOrLater()) {  //这里需要做个判断,android 4.4之后接口有变化
            am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
        } else {
            am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
        }

通过上述方法,可在指定时间使pendingIntnet发送广播,然后程序实现BrodcastReceiver,接收此广播。这样就可在指定时间,作出动作。

2.闹钟alarm如何设置,包含哪些元素?
Android 系统闹钟源码解析_第1张图片
image

一个闹钟(alarm)包含:时间设置(几点几分),标签(名字),重复(是否单次使用),重复日期,是否振动,铃声设置;

其中重复日期设置(daysofweek),源码中的设计如下,通过一个整数表示所有情况:
1111111:通过移位与或运算来确定某天是否被选中:

Days of week code as a single int.
0x00(0000000): no day
0x01(0000001): Monday
0x02(0000010) :Tuesday
0x04(0000100): Wednesday
0x08(0001000): Thursday
0x10(0010000): Friday
0x20(0100000): Saturday
0x40(1000000): Sunday
0x7f(1111111) :Everyday

星期一,星期三被选中,就是0000101;

周一周三八点闹钟设置为:hour=8,minute=0, daysofweek=5,enabled=true;

3.如何管理闹钟alarm?

当然数据库,原生又实现了contentProvider,通过uri进行添加,删除,更新。体现了android的统一性。
细节自己看代码。

4.如何管理一个闹钟事件-alarmInstance?

每一个开启的闹钟(alarm),会生成一个闹钟事件(alarmInstance),这个闹钟事件(alarmInstance)保存闹钟状态。

闹钟事件内容

  1. alarmTime(闹钟触发时间,根据alarm设置结合当前时间计算而来)
  2. state( 闹钟事件状态),源码中一个闹钟事件分为7个状态;按时间顺序转变。

闹钟事件状态变化

举例:设置闹钟时间为8点整,则产生一个闹钟事件-alarmInstance 的alarmTime为8点;

  1. SILENT_STATE:(6:00点以前,没有任何提示)
    下一状态为:LOW_NOTIFICATION_STATE

  2. LOW_NOTIFICATION_STATE(6:00-7:30,通知栏有提示预定闹钟,通知可去除)
    下一状态为:HIGH_NOTIFICATION_STATE

  3. HIGH_NOTIFICATION_STATE(7:30-7:59,通知栏有提示预定闹钟,通知不可去除)
    下一状态为:FIRED_STATE

  4. FIRED_STATE(8:00,闹钟触发)
    下一状态为:SNOOZE_STATE(选择稍后再响) 或 DISMISSED_STATE(选择关闭)或 MISSED_STATE(未做任何操作,直到响声结束)

  5. SNOOZE_STATE(8:00--设置的暂停时间,闹钟暂停)
    下一状态为:FIRED_STATE

  6. MISSED_STATE(闹钟响起无任何操作,设置的闹钟响起时间过后,通知栏提示错过闹钟)
    下一状态为:DISMISSED_STATE

  7. DISMISSED_STATE(闹钟关闭)

如何管理闹钟事件(alarmInstance)状态:

结合上边第一条定时触发原理,写一个BroadcastReceiver类----AlarmStateManager,负责接收广播,并改变闹钟事件的状态

比如现在是7:50,设置的闹钟是8点;于是产生一条闹钟事件alarmInstance,根据规则判断当前状态为HIGH_NOTIFICATION_STATE,然后调用am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);使其在8:00发送广播,intent包含要alarmInstance设置的状态(FIRED_STATE),同时再次调用上述方法(alarmManager.set)发送广播包含要alarmInstance设置的(MISSED_STATE)

若闹钟响起时用户选择稍后响起,此时关闭当前闹钟,并将闹钟事件alarmInstance的状态设置为SNOOZE_STATE,调用(alarmManager.set),设置下次状态为FIRED_STATE。

若闹钟响起时用户选择关闭,则关闭当前闹钟,删除此闹钟事件alarmInstance。同时通过此闹钟事件对应的闹钟设置alarm,根据其重复日期,计算下次闹钟事件时间,并生成新的alarmInstance,设置其初始状态,并调用(alarmManager.set),使其在指定时间收到广播并修改对应状态;

若闹钟响起,用户不做任何操作,则到时会接收到之间预发送的广播,并将闹钟事件状态设置为MISSED_STATE。

alarmInstance也是用数据库保存,然后实现contentProvider,通过uri进行添加,删除,更新。

5.举例分析,如何实现一个闹钟在每周一,周三,早上8点响起?

假设现在是周一7点50:

1.增加闹钟设置alarm: hour=8,minute=0; repeat=true; daysofweek=5 (0000101); enabled=ture;

2.生成闹钟事件alarmInstance : alarmTime=今天8点, state=HIGH_NOTIFICATION_STATE( 当前状态);

3.通过alarmManager.set发送定时广播,pendingIntent中包含下次触发的状态为FIRED_STATE;

4.时间到,alarmStateManager收到广播播放铃声;此时选择关闭闹钟;此闹钟事件alarmInstance结束删除,然后根据对应alarm是计算得出下次闹钟时间是周三8点;于是生成新的闹钟事件alarmInstance : alarmTime=周三8点,state=SILENT_STATE;通过alarmManager.set发送定时广播,包含下次要触发的状态,循环······

6.开关机,时区或者时间改变,来电等冲突事件都做哪些处理?

重新开机后,之前闹钟事件发出的定时广播全部失效;所以要接受开机广播,然后根据alarm设置调整每个alarmInstance的状态,并重新发送定时广播;

其余就不在啰嗦啦,具体看系统代码去吧;

你可能感兴趣的:(Android 系统闹钟源码解析)