基于 Android7.1.1 源码,分析 AlarmManagerService 的启动流程,这里我们重点关注和分析 java 层的逻辑实现!
1 | private void startOtherServices() { |
首先会创建 AlarmManagerService 实例对象,然后会调用其 onStart 方法!
1 Constructor
1 | public AlarmManagerService(Context context) { |
AlarmManagerService 的构造器很简单,只是创建了一个 Constants 实例对象!
1.1 new Constants
Constants 用来保存 alarm manger 需要的一些常量数据,其内部有一些和属性值!
该类中所有的时间单位都是毫秒,这些常量的值和系统全局设置 Settings.Global 中的值保持同步,任何访问该类或该类中的字段都要持有 AlarmManagerService.mLock 锁!
首先,是一些 Settings.Global 中的 key 值,用于将数据保存到 Settings 中!1
2
3
4
5
6
7private static final String KEY_MIN_FUTURITY = "min_futurity";
private static final String KEY_MIN_INTERVAL = "min_interval";
private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
= "allow_while_idle_whitelist_duration";
private static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
接着是一些 default 值,用于初始化成员变量!1
2
3
4
5
6
7private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; // 默认的最小触发时间: 5s
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; // 默认最小时间间隔; 60s!
private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000; // 默认的监听超时时间:5s
最后是一些 alarm manager 会用到的一些成员变量:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
// alarm 重复执行的最小时间间隔!
public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL;
// Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;
// Minimum time between ALLOW_WHILE_IDLE alarms when system is idling.
public long ALLOW_WHILE_IDLE_LONG_TIME = DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME;
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
= DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
// Direct alarm listener callback timeout
// 闹钟监听回调超时时间,初始化为 5s;
public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
private ContentResolver mResolver; // 用于访问设置的全局数据库!
private final KeyValueListParser mParser = new KeyValueListParser(',');
private long mLastAllowWhileIdleWhitelistDuration = -1;
接着,去看看 Constants 的构造器!
1 | public Constants(Handler handler) { |
1.2 Constructor.updateAllowWhileIdleMinTimeLocked
这里调用了两个方法:
1 | public void updateAllowWhileIdleMinTimeLocked() { |
如果此时已经进入了 idle 状态,那么触发的最小时间间隔为:ALLOW_WHILE_IDLE_LONG_TIME,否则为 ALLOW_WHILE_IDLE_SHORT_TIME!
1.3 Constructor.updateAllowWhileIdleWhitelistDurationLocked
1 | public void updateAllowWhileIdleWhitelistDurationLocked() { |
继续来看,该方法用于设置 system 更新 idle whilde list 的时间间隔,目前是 10 × 1000 毫秒!
2 onStart
接着是重点 onStart 方法:
1 |
|
下面我们来具体分析下:
2.1 init -> android_server_AlarmManagerService_init
1 | private native long init(); |
可以看到,这是一个 native 方法,方法的定义在:android/frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp 中!
最终会通过 systemCall 进入 native 层,调用 android_server_AlarmManagerService_init 方法!
我们来看看 init 最终做了什么:1
2
3
4
5
6
7
8
9
10
11static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
{
//【2.1.1】初始化 alarm 驱动!
jlong ret = init_alarm_driver();
if (ret) {
return ret;
}
//【2.1.2】初始化定时器 fd!
return init_timerfd();
}
这里我们不过多关注!
2.2 setTimeZoneImpl
更新时区信息,把当前时区保存到内核中!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40void setTimeZoneImpl(String tz) {
if (TextUtils.isEmpty(tz)) {
return;
}
// 获取最新的时区信息!
TimeZone zone = TimeZone.getTimeZone(tz);
// 防止重复写入
boolean timeZoneWasChanged = false;
synchronized (this) {
// 通过系统属性获得已经保存的时区信息;
String current = SystemProperties.get(TIMEZONE_PROPERTY);
// 如果 current 不等于 zone.getID()。说明时区信息发生了变化!
if (current == null || !current.equals(zone.getID())) {
if (localLOGV) {
Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
}
timeZoneWasChanged = true;
// 更新系统属性,应用程序会读取该属性!
SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
}
// 更新内核时区信息
// 内核跟踪时间偏移为GMT以西的分钟数
int gmtOffset = zone.getOffset(System.currentTimeMillis());
setKernelTimezone(mNativeData, -(gmtOffset / 60000));
}
TimeZone.setDefault(null);
// 如果时区发生了变化,那就发送 Intent.ACTION_TIMEZONE_CHANGED 的广播!
if (timeZoneWasChanged) {
Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("time-zone", zone.getID());
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
这里的 setKernelTimezone 是一个 native 方法,这里我们先不看!
1 | private native int setKernelTimezone(long nativeData, int minuteswest); |
更新 kernel 时区的信息的原因是,kernel 在重启后,并不会保存时区信息!
2.3 ClockReceiver
我们来看看 ClockReceiver,是一个动态注册的 BroadcastReceiver,用于接收 Intent.ACTION_TIME_TICK 时间改变和 Intent.ACTION_DATE_CHANGED 日期改变的广播!
1 | class ClockReceiver extends BroadcastReceiver { |
可以看到:
- 当收到 Intent.ACTION_TIME_TICK 后,触发 scheduleTimeTickEvent 方法;
- 当收到 Intent.ACTION_DATE_CHANGED,触发 scheduleDateChangedEvent 方法:
2.3.1 ClockReceiver.scheduleTimeTickEvent
我们来看下 scheduleTimeTickEvent,该方法每一分钟,就会给所有接收 ACTION_TIME_TICK 广播的接收者发送广播,注意这里也包括自身!
1 | public void scheduleTimeTickEvent() { |
可以看出 scheduleTimeTickEvent 方法会设置一个 alarm,距离当前时间的下一秒,这个 alarm 会发送 Intent.ACTION_TIME_TICK 广播!
然后 ClockReceiver 会接收到这个广播,然后继续设置下一秒的,发送 Intent.ACTION_TIME_TICK 广播的 alarm,如此循环下去!
2.3.2 ClockReceiver.scheduleDateChangedEvent
我们来看下 scheduleDateChangedEvent:
1 | public void scheduleDateChangedEvent() { |
可以看出 scheduleDateChangedEvent 方法会设置一个 alarm,触发事件是下一天的 0 点整,这个 alarm 会发送 Intent.ACTION_DATE_CHANGED 广播!
然后 ClockReceiver 会接收到这个广播,然后继续设置下一天 0 点整的,发送 Intent.Intent.ACTION_DATE_CHANGED 广播的 alarm,如此循环下去!
我们可以看到这里涉及到了一个方法 setImpl,这个方法我们后续会分析,这里先简单认为其设置了一个 alarm!
2.4 InteractiveStateReceiver
InteractiveStateReceiver 用于监控熄屏亮屏的广播!
1 | class InteractiveStateReceiver extends BroadcastReceiver { |
监听的 Alarm:
- Intent.ACTION_SCREEN_OFF
- Intent.ACTION_SCREEN_ON
继续来看:
2.4.1 interactiveStateChangedLocked
这里说下参数 interactive,表示接收到的是否亮屏广播:
1 | Intent.ACTION_SCREEN_ON.equals(intent.getAction()) |
成员变量 mInteractive 用来保存上一次的熄屏亮屏状态!
接下来,我们来看看 interactiveStateChangedLocked 方法:
1 | void interactiveStateChangedLocked(boolean interactive) { |
3 onBootPhase
在启动的最后阶段,system ready 的时候,会调用 AlarmManagerService 的 onBootPhase 方法!
1 |
|
我们来去看看 Constants.start 方法:
3.1 Constants.start
1 | public void start(ContentResolver resolver) { |
当 Settings.Global.ALARM_MANAGER_CONSTANTS 的数据发生变化后,会触发 Constants 的回调:
1 |
|
3.2 Constants.updateConstants
更新 Constants 中的属性值!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29private void updateConstants() {
synchronized (mLock) {
//【1】读取 Settings 的 Global 数据库中的 alarm_manager_constants 项数据!
// 更新 Constants 内部变量!
try {
mParser.setString(Settings.Global.getString(mResolver,
Settings.Global.ALARM_MANAGER_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
Slog.e(TAG, "Bad device idle settings", e);
}
MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME,
DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME);
ALLOW_WHILE_IDLE_WHITELIST_DURATION = mParser.getLong(
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
DEFAULT_LISTENER_TIMEOUT);
updateAllowWhileIdleMinTimeLocked();
updateAllowWhileIdleWhitelistDurationLocked();
}
}
Alarm 是 AlarmManagerService 的一个内部类 Alarm,所有应用在设置 Alarm 的时候,都会在 setImplLocked 函数中将 Alarm 格式化为内部类 Alarm 的格式,定义截选如下:
private static class Alarm {
public int type;
public int count;
public long when;
public long repeatInterval;
public PendingIntent operation;
public int uid;
public int pid;
其中记录了逻辑闹钟的一些关键信息。
type域:记录着逻辑闹钟的闹钟类型,比如RTC_WAKEUP、ELAPSED_REALTIME_WAKEUP等;
count域:是个辅助域,它和repeatInterval域一起工作。当repeatInterval大于0时,这个域可被用于计算下一次重复激发alarm的时间。
when域:记录闹钟的激发时间。这个域和type域相关,详细情况见后文;
repeatInterval域:表示重复激发闹钟的时间间隔,如果闹钟只需激发一次,则此域为0,如果闹钟需要重复激发,此域为以毫秒为单位的时间间隔;
operation域:记录闹钟激发时应该执行的动作,详细情况见后文;
uid域:记录设置闹钟的进程的uid;
pid域:记录设置闹钟的进程的pid。