转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/18448997
本次给大家分析的是android中alarm的机制以及它和binder的交互,所用源码为最新的android4.4。因为alarm的功能都是通过binder来完成的,所以,介绍alarm之前必须要先介绍下它是如何调用binder来完成定时功能的。由于内容较多,本文会比较长,在文章结构安排上是这样的:首先简单介绍如何使用alarm并给出其工作原理,接着分析alarm和timer以及handler在完成定时任务上的差别,然后分析alarm与binder的交互,最后分析alarm机制的源码。
alarm是android提供的用于完成闹钟式定时任务的类,系统通过alarmmanager来管理所有的alarm,alarm支持一次性定时任务和循环定时任务,它的使用方式很简单,这里不多做介绍,只给出一个简单的示例:
[java] view
plaincopy
alarmmanager alarmmgr = (alarmmanager) getsystemservice(context.alarm_service);
intent intent = new intent(getapplicationcontext(), testactivity.class);
pendingintent pendintent = pendingintent.getactivity(getapplicationcontext(),
0, intent, pendingintent.flag_update_current);
//5秒后发送广播,只发送一次
int triggerattime = systemclock.elapsedrealtime() + 5 * 1000;
alarmmgr.set(alarmmanager.elapsed_realtime, triggerattime, pendintent);
相同点:
三者都可以完成定时任务,都支持一次性定时和循环定时(注:handler可以间接支持循环定时任务)
不同点:
handler和timer在定时上是类似的,二者在系统休眠的情况下无法正常工作,定时任务不会按时触发。alarm在系统休眠的情况下可以正常工作,并且还可以决定是否唤醒系统,同时alarm在自身不启动的情况下仍能正常收到定时任务提醒,但是当系统重启或者应用被杀死的情况下,alarm定时任务会被取消。另外,从android4.4开始,alarm事件默认采用非精准方式,即定时任务可能会有小范围的提前或延后,当然我们可以强制采用精准方式,而在此之前,alarm事件都是精准方式。
alarm由alarmmanager来管理,从使用方式来看,alarmmanager很简单,我们只要得到了alarmmanager的对象,就可以调用set方法来设定定时任务了,而如何得到alarmmanager对象呢?也很简单,alarmmanager alarmmgr = (alarmmanager) getsystemservice(context.alarm_service);下面我们去看看alarmmanager的set方法,当然alarmmanager还有setrepeating方法,但是二者是类似的。为了更好地理解下面的内容,需要你了解aidl,如果你还不了解,请参看android跨进程通信(ipc):使用aidl。
code:alarmmanager#set
public void set(int type, long triggeratmillis, pendingintent operation) {
setimpl(type, triggeratmillis, legacyexactlength(), 0, operation, null);
}
public void set(int type, long triggeratmillis, long windowmillis, long intervalmillis,
pendingintent operation, worksource worksource) {
setimpl(type, triggeratmillis, windowmillis, intervalmillis, operation, worksource);
private void setimpl(int type, long triggeratmillis, long windowmillis, long intervalmillis,
if (triggeratmillis < 0) {
/* notyet
if (malwaysexact) {
// fatal error for klp+ apps to use negative trigger times
throw new illegalargumentexception("invalid alarm trigger time "
+ triggeratmillis);
}
*/
triggeratmillis = 0;
}
try {
//定时任务实际上都有mservice来完成,也就是说alarmmanager只是一个空壳
//从下面的构造方法可以看出,这个mservice是ialarmmanager类型的,而ialarmmanager是一个接口
//如果大家了解aidl就应该知道ialarmmanager应该是一个aidl接口
mservice.set(type, triggeratmillis, windowmillis, intervalmillis, operation,
worksource);
} catch (remoteexception ex) {
alarmmanager(ialarmmanager service, context ctx) {
mservice = service;
final int sdkversion = ctx.getapplicationinfo().targetsdkversion;
malwaysexact = (sdkversion < build.version_codes.kitkat);
说明:我对代码进行了注释,从注释可以看出,现在我们需要去找到这个mservice,其实我已经帮大家找到了,它就是alarmmanagerservice,看下它的类的声明:
class alarmmanagerservice extends ialarmmanager.stub
很显然,alarmmanagerservice的确实现了ialarmmanager接口,为什么是显然呢?因为按照aidl的规范,ialarmmanager.stub是按照如下这种方式声明的:
public static abstract class stub extends binder implements ialarmmanager {
public static ialarmmanager asinterface(ibinder obj)
...
可见这个stub类就是一个普通的binder,只不过它实现了ialarmmanager接口。它还有一个静态方法asinterface,这个方法很有用,通过它,我们就可以将ibinder对象转换成ialarmmanager的实例,进而通过实例来调用其方法。什么是binder?这个还真不好说,但是我们要知道binder在android系统中有大量的应用,大部分manager都通过binder来实现(包括alarmmanager),而service和aidl也是通过binder来实现调用的。至于binder和ibinder的关系,很简单,就是binder实现了ibinder接口。由于alarmmanagerservice继承了ialarmmanager.stub,所以alarmmanagerservice也相当于实现了ialarmmanager接口,所以很显然,alarmmanagerservice就是alarmmanager中用于和其交互的mservice。不过,还没有完,因为上面的结论不是我瞎猜的,是有代码层面的依据的,下面我将带领大家一起去探索寻找mservice的过程,通过这个过程,我们会对binder机制有更加深刻的认识。
首先dalvik虚拟机会在systemserver中创建一个叫做serverthread的线程并调用它的initandloop方法,在initandloop方法中会创建主线程looper和初始化各种manager所对应的binder服务,我们所常见的binder服务如windowmanagerservice、alarmmanagerservice、powermanagerservice等均在这里创建并加入到servicemanager中进行统一管理。而我们通过getsystemservice方式来得到各种manager的工作主要是在contextimpl中完成的,不过layoutinflater、windowmanager以及searchmanager除外。通过contextimpl我们可以知道各种manager和binder服务的一一对应关系,比如alarmmanager对应alarmmanagerservice、windowmanager对应windowmanagerservice。
上面只是结论,为了真正搞清楚各种manager所对应的binder服务,下面将要看一系列代码,首先看systemserver的代码:
code:systemserver
public class systemserver {
private static final string tag = "systemserver";
public static final int factory_test_off = 0;
public static final int factory_test_low_level = 1;
public static final int factory_test_high_level = 2;
static timer timer;
static final long snapshot_interval = 60 * 60 * 1000; // 1hr
// the earliest supported time. we pick one day into 1970, to
// give any timezone code room without going into negative time.
private static final long earliest_supported_time = 86400 * 1000;
/**
* called to initialize native system services.
* 初始化本地系统服务,jni方法
*/
private static native void nativeinit();
//main方法,由底层调用
public static void main(string[] args) {
if (system.currenttimemillis() < earliest_supported_time) {
// if a device's clock is before 1970 (before 0), a lot of
// apis crash dealing with negative numbers, notably
// java.io.file#setlastmodified, so instead we fake it and
// hope that time from cell towers or ntp fixes it
// shortly.
slog.w(tag, "system clock is before 1970; setting to 1970.");
systemclock.setcurrenttimemillis(earliest_supported_time);
}
if (samplingprofilerintegration.isenabled()) {
samplingprofilerintegration.start();
timer = new timer();
timer.schedule(new timertask() {
@override
public void run() {
samplingprofilerintegration.writesnapshot("system_server", null);
}
}, snapshot_interval, snapshot_interval);
// mmmmmm... more memory!
dalvik.system.vmruntime.getruntime().cleargrowthlimit();
// the system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
vmruntime.getruntime().settargetheaputilization(0.8f);
environment.setuserrequired(true);
system.loadlibrary("android_servers");
slog.i(tag, "entered the android system server!");
// 初始化本地服务.
nativeinit();
//这里是关键,serverthread被创建,同时其initandloop被调用
serverthread thr = new serverthread();
thr.initandloop();
接着看serverthread的initandloop方法,该方法中,主线程looper会被创建,各种binder服务会被创建。该方法太长,我进行了截断,只展出我们所关心的代码。
code:serverthread#initandloop
public void initandloop() {
eventlog.writeevent(eventlogtags.boot_progress_system_run,
systemclock.uptimemillis());
//主线程looper被创建
looper.preparemainlooper();
android.os.process.setthreadpriority(
android.os.process.thread_priority_foreground);
binderinternal.disablebackgroundscheduling(true);
android.os.process.setcanselfbackground(false);
...此处省略
//下面是各种binder服务,从名字我们应该能够大致看出它们所对应的manager
installer installer = null;
accountmanagerservice accountmanager = null;
contentservice contentservice = null;
lightsservice lights = null;
powermanagerservice power = null;
displaymanagerservice display = null;
batteryservice battery = null;
vibratorservice vibrator = null;
alarmmanagerservice alarm = null;
mountservice mountservice = null;
networkmanagementservice networkmanagement = null;
networkstatsservice networkstats = null;
networkpolicymanagerservice networkpolicy = null;
connectivityservice connectivity = null;
wifip2pservice wifip2p = null;
wifiservice wifi = null;
nsdservice servicediscovery= null;
ipackagemanager pm = null;
context context = null;
windowmanagerservice wm = null;
bluetoothmanagerservice bluetooth = null;
dockobserver dock = null;
usbservice usb = null;
serialservice serial = null;
twilightservice twilight = null;
uimodemanagerservice uimode = null;
recognitionmanagerservice recognition = null;
networktimeupdateservice networktimeupdater = null;
commontimemanagementservice commontimemgmtservice = null;
inputmanagerservice inputmanager = null;
telephonyregistry telephonyregistry = null;
consumerirservice consumerir = null;
slog.i(tag, "alarm manager");
//这里alarmmanager对应的binder服务被创建
alarm = new alarmmanagerservice(context);
//将alarmmanagerservice加入servicemanager中统一管理
servicemanager.addservice(context.alarm_service, alarm);
slog.i(tag, "init watchdog");
watchdog.getinstance().init(context, battery, power, alarm,
activitymanagerservice.self());
watchdog.getinstance().addthread(wmhandler, "windowmanager thread");
slog.i(tag, "input manager");
inputmanager = new inputmanagerservice(context, wmhandler);
slog.i(tag, "window manager");
//这里windowmanager所对应的binder服务被创建
wm = windowmanagerservice.main(context, power, display, inputmanager,
wmhandler, factorytest != systemserver.factory_test_low_level,
!firstboot, onlycore);
//将windowmanagerservice加入servicemanager中统一管理
servicemanager.addservice(context.window_service, wm);
servicemanager.addservice(context.input_service, inputmanager);
activitymanagerservice.self().setwindowmanager(wm);
说明:针对上述代码,我要说明一下,首先其创建的各种binder服务其实并不是真正的服务,说它们是binder比较恰当,因为它们的确继承自binder而不是service;另一点就是servicemanager其实也仅仅是个壳子,真正的工作是通过其binder服务servicemanagernative来完成的,servicemanager提供的工厂方法addservice和getservice均在servicemanagernative中通过代理来实现。
到此为止,我们已经知道各种binder服务的创建过程,下面我们要看一下manager是如何和其binder服务关联上的,再回到getsystemservice方法。首先我们要知道activity的继承关系,如下图所示:
再看如下代码,观察下它们中的getsystemservice方法是如何实现的
code:各种getsystemservice方法
//#context
public abstract object getsystemservice(string name);
//#contextwrapper
@override
public object getsystemservice(string name) {
return mbase.getsystemservice(name);
//#contextthemewrapper
@override
if (layout_inflater_service.equals(name)) {
if (minflater == null) {
minflater = layoutinflater.from(mbase).cloneincontext(this);
return minflater;
//#activity
if (getbasecontext() == null) {
throw new illegalstateexception(
"system services not available to activities before oncreate()");
if (window_service.equals(name)) {
return mwindowmanager;
} else if (search_service.equals(name)) {
ensuresearchmanager();
return msearchmanager;
return super.getsystemservice(name);
说明:通过上述代码可以看出layoutinflater、windowmanager以及searchmanager的处理比较特殊,直接在方法中返回对象,剩下的所有manager将通过mbase.getsystemservice(name)返回,现在问题转移到mbase上面,mbase是什么呢?我已经查清楚了,activity的mbase就是contextimpl对象,何以见得?请看下面分析
不知道大家对我写的另外一篇源码分析是否有印象:android源码分析-activity的启动过程,在这篇文章中我指出:activity的最终启动过程由activitythread中的performlaunchactivity方法来完成,在performlaunchactivity中,activity的mbase将被赋值为contextimpl对象,下面通过代码来说明:
code:mbase的赋值过程
private activity performlaunchactivity(activityclientrecord r, intent customintent) {
if (activity != null) {
//这里的appcontext就是contextimpl对象
context appcontext = createbasecontextforactivity(r, activity);
charsequence title = r.activityinfo.loadlabel(appcontext.getpackagemanager());
configuration config = new configuration(mcompatconfiguration);
if (debug_configuration) slog.v(tag, "launching activity "
+ r.activityinfo.name + " with config " + config);
//通过activity的attach方法将contextimpl对象赋值给mbase
activity.attach(appcontext, this, getinstrumentation(), r.token,
r.ident, app, r.intent, r.activityinfo, title, r.parent,
r.embeddedid, r.lastnonconfigurationinstances, config);
...
private context createbasecontextforactivity(activityclientrecord r,
final activity activity) {
//很显然,此方法返回的就是contextimpl对象
contextimpl appcontext = new contextimpl();
appcontext.init(r.packageinfo, r.token, this);
appcontext.setoutercontext(activity);
context basecontext = appcontext;
return basecontext;
final void attach(context context, activitythread athread,
instrumentation instr, ibinder token, int ident,
application application, intent intent, activityinfo info,
charsequence title, activity parent, string id,
nonconfigurationinstances lastnonconfigurationinstances,
configuration config) {
//将context赋值给mbase,这里的context就是performlaunchactivity中的appcontext,即contextimpl对象
attachbasecontext(context);
mfragments.attachactivity(this, mcontainer, null);
mwindow = policymanager.makenewwindow(this);
mwindow.setcallback(this);
@override protected void attachbasecontext(context newbase) {
super.attachbasecontext(newbase);
//这里很显然,对mbase进行赋值
mbase = newbase;
说明:看了上面的代码,我们已经知道,mbase的确是contextimpl对象。上面我提到:除了layoutinflater、windowmanager以及searchmanager,剩下的所有manager将通过mbase.getsystemservice(name)返回,那么现在,我们去看下contextimpl中的getsystemservice方法。
code:contextimpl#getsystemservice
class contextimpl extends context {
@override
public object getsystemservice(string name) {
//首先从system_service_map根据服务名得到一个fetcher对象
//其中system_service_map是一个hashmap,然后再通过fetcher去取service
servicefetcher fetcher = system_service_map.get(name);
return fetcher == null ? null : fetcher.getservice(this);
说明:看了contextimpl的getsystemservice方法,发现失望了,还没有找到真正的实现,看来还要去看这个fetcher是怎么回事,下面请看代码:
code:服务注册过程和fetcher
//一个哈希表,用来根据服务名存储对应服务的servicefetcher(可以理解为通过servicefetcher可以得到服务)
private static final hashmap<string, servicefetcher> system_service_map =
new hashmap<string, servicefetcher>();
//注册服务,将服务的fetcher存到哈希表中
private static void registerservice(string servicename, servicefetcher fetcher) {
if (!(fetcher instanceof staticservicefetcher)) {
fetcher.mcontextcacheindex = snextpercontextservicecacheindex++;
system_service_map.put(servicename, fetcher);
//静态代码块,注册各种服务
//也就是说,contextimpl这个类被加载的时候就会把如下的各种服务的fetcher加入到哈希表中
//这样我们通过getsystemservice就可以得到一个服务的fetcher,再通过fetcher去得到服务的对象
static {
registerservice(accessibility_service, new servicefetcher() {
public object getservice(contextimpl ctx) {
return accessibilitymanager.getinstance(ctx);
}});
registerservice(captioning_service, new servicefetcher() {
return new captioningmanager(ctx);
registerservice(account_service, new servicefetcher() {
public object createservice(contextimpl ctx) {
ibinder b = servicemanager.getservice(account_service);
iaccountmanager service = iaccountmanager.stub.asinterface(b);
return new accountmanager(ctx, service);
registerservice(activity_service, new servicefetcher() {
return new activitymanager(ctx.getoutercontext(), ctx.mmainthread.gethandler());
//这里是alarm服务的注册
registerservice(alarm_service, new servicefetcher() {
/**还记得alarm_service吗?
* alarm = new alarmmanagerservice(context);
* 将alarmmanagerservice加入servicemanager中统一管理
* servicemanager.addservice(context.alarm_service, alarm);
*/
//通过servicemanager的getservice得到alarm服务,很显然,下面的b就是alarmmanagerservice对象
ibinder b = servicemanager.getservice(alarm_service);
//还记得alarmmanager中的mservice吗?就是这里的service,很显然它是一个binder服务
//分析到这里,事实已经得出:alarmmanager所对应的binder服务就是alarmmanagerservice
ialarmmanager service = ialarmmanager.stub.asinterface(b);
return new alarmmanager(service, ctx);
registerservice(audio_service, new servicefetcher() {
return new audiomanager(ctx);
...省略:下面还有许多服务
说明:通过上述代码的分析,相信大家已经很明确manager是如何和binder服务一一对应的,然后manager的各种功能将会交由binder服务来完成。尽管我只详细分析了alarmmanager和alarmmanagerservice的对应过程,但是其它manager的对应过程是几乎完全一样的。好了,到了这里,我们已经把manager和binder服务的对应过程进行了深入地分析,下面开始我们的最后一个主题:alarm机制的源码分析。
通过上面的一系列分析,我们知道alarmmanager的所有功能都是通过alarmmanagerservice来完成的,在分析源码之前,我先来描述下alarm的工作原理:从android4.4开始,alarm默认为非精准模式,除非显示指定采用精准模式。在非精准模式下,alarm是批量提醒的,每个alarm根据其触发时间和最大触发时间的不同会被加入到不同的batch中,同一个batch的不同alarm是同时发生的,这样就无法实现精准闹钟,官方的解释是批量处理可以减少设备被唤醒次数以及节约电量,不过针对精准闹钟,官方预留的方法是setexact和setwindow,二者都是通过将时间窗口定义为0来实现精准闹钟的,因为时间窗口为0,意味着触发时间和最大触发时间是一样的,因为典型的情况下:最大触发时间=
触发时间 + 时间窗口。同时所有的batch是按开始时间升序排列的,在一个batch内部,不同的闹钟也是按触发时间升序排列的,所以闹钟的唤醒顺序是按照batch的排序依次触发的,而同一个batch中的alarm是同时触发的,可以用下面这个示意图来描述:
上图是示意图,系统中可以有多个batch,每个batch中可以有多个alarm。下面我们分析一下alarmmanagerservice中的代码。其入口方法为set,set又调用了setimpllocked,所以我们直接看setimpllocked。
code:alarmmanagerservice#setimpllocked
private void setimpllocked(int type, long when, long whenelapsed, long maxwhen, long interval,
pendingintent operation, boolean isstandalone, boolean dovalidate,
worksource worksource) {
/**创建一个alarm,其中各参数的含义如下:
* type 闹钟类型 elapsed_realtime、rtc、rtc_wakeup等
* when 触发时间 utc类型,绝对时间,通过system.currenttimemillis()得到
* whenelapsed 相对触发时间,自开机算起,含休眠,通过systemclock.elapsedrealtime()得到
* maxwhen 最大触发时间
* interval 触发间隔,针对循环闹钟有效
* operation 闹钟触发时的行为,pendingintent类型
alarm a = new alarm(type, when, whenelapsed, maxwhen, interval, operation, worksource);
//根据pendingintent删除之前已有的同一个闹钟
removelocked(operation);
boolean reschedule;
//尝试将alarm加入到合适的batch中,如果alarm是独立的或者无法找到合适的batch去容纳此alarm,返回-1
int whichbatch = (isstandalone) ? -1 : attemptcoalescelocked(whenelapsed, maxwhen);
if (whichbatch < 0) {
//没有合适的batch去容纳alarm,则新建一个batch
batch batch = new batch(a);
batch.standalone = isstandalone;
//将batch加入malarmbatches中,并对malarmbatches进行排序:按开始时间升序排列
reschedule = addbatchlocked(malarmbatches, batch);
} else {
//如果找到合适了batch去容纳此alarm,则将其加入到batch中
batch batch = malarmbatches.get(whichbatch);
//如果当前alarm的加入引起了batch开始时间和结束时间的改变,则reschedule为true
reschedule = batch.add(a);
if (reschedule) {
//由于batch的起始时间发生了改变,所以需要从列表中删除此batch并重新加入、重新对batch列表进行排序
malarmbatches.remove(whichbatch);
addbatchlocked(malarmbatches, batch);
if (debug_validate) {
if (dovalidate && !validateconsistencylocked()) {
slog.v(tag, "tipping-point operation: type=" + type + " when=" + when
+ " when(hex)=" + long.tohexstring(when)
+ " whenelapsed=" + whenelapsed + " maxwhen=" + maxwhen
+ " interval=" + interval + " op=" + operation
+ " standalone=" + isstandalone);
rebatchallalarmslocked(false);
reschedule = true;
if (reschedule) {
reschedulekernelalarmslocked();
说明:通过上述代码可以看出,当我们创建一个alarm的时候,仅仅是将这个alarm加入到某个batch中,系统中有一个batch列表,专门用于存储所有的alarm。可是仅仅把alarm加入到batch中还不行,系统还必须提供一个类似于looper的东西一直去遍历这个列表,一旦它发现有些alarm的时间已经到达就要把它取出来去执行。事实上,alarmmanagerservice中的确有一个类似于looper的东西去干这个事情,只不过它是个线程,叫做alarmthread。下面看它的代码:
code:alarmmanagerservice#alarmthread
private class alarmthread extends thread
{
public alarmthread()
{
super("alarmmanager");
public void run()
//当前时间触发的alarm列表
arraylist<alarm> triggerlist = new arraylist<alarm>();
while (true)
{
//jni方法,顾名思义,阻塞式方法,当有alarm的时候会被唤醒
int result = waitforalarm(mdescriptor);
triggerlist.clear();
if ((result & time_changed_mask) != 0) {
if (debug_batch) {
slog.v(tag, "time changed notification from kernel; rebatching");
remove(mtimeticksender);
//将所有的alarm重新排序
rebatchallalarms();
mclockreceiver.scheduletimetickevent();
intent intent = new intent(intent.action_time_changed);
intent.addflags(intent.flag_receiver_replace_pending
| intent.flag_receiver_registered_only_before_boot);
mcontext.sendbroadcastasuser(intent, userhandle.all);
}
synchronized (mlock) {
final long nowrtc = system.currenttimemillis();
final long nowelapsed = systemclock.elapsedrealtime();
if (locallogv) slog.v(
tag, "checking for alarms... rtc=" + nowrtc
+ ", elapsed=" + nowelapsed);
if (wakeup_stats) {
if ((result & is_wakeup_mask) != 0) {
long newearliest = nowrtc - recent_wakeup_period;
int n = 0;
for (wakeupevent event : mrecentwakeups) {
if (event.when > newearliest) break;
n++; // number of now-stale entries at the list head
}
for (int i = 0; i < n; i++) {
mrecentwakeups.remove();
recordwakeupalarms(malarmbatches, nowelapsed, nowrtc);
}
//这个方法会把batch列表中的第一个batch取出来然后加到触发列表中
//当然,前提是此batch的开始时间不大于当前时间
//同时,如果是循环闹钟,则会对下次任务进行再次定时
triggeralarmslocked(triggerlist, nowelapsed, nowrtc);
reschedulekernelalarmslocked();
// 遍历触发列表,发送pendingintent
for (int i=0; i<triggerlist.size(); i++) {
alarm alarm = triggerlist.get(i);
try {
if (locallogv) slog.v(tag, "sending alarm " + alarm);
//这里pendingintent会被send,结果就是我们的定时任务被执行了
alarm.operation.send(mcontext, 0,
mbackgroundintent.putextra(
intent.extra_alarm_count, alarm.count),
mresultreceiver, mhandler);
// we have an active broadcast so stay awake.
if (mbroadcastrefcount == 0) {
setwakelockworksource(alarm.operation, alarm.worksource);
mwakelock.acquire();
final inflight inflight = new inflight(alarmmanagerservice.this,
alarm.operation, alarm.worksource);
minflight.add(inflight);
mbroadcastrefcount++;
final broadcaststats bs = inflight.mbroadcaststats;
bs.count++;
if (bs.nesting == 0) {
bs.nesting = 1;
bs.starttime = nowelapsed;
} else {
bs.nesting++;
final filterstats fs = inflight.mfilterstats;
fs.count++;
if (fs.nesting == 0) {
fs.nesting = 1;
fs.starttime = nowelapsed;
fs.nesting++;
if (alarm.type == elapsed_realtime_wakeup
|| alarm.type == rtc_wakeup) {
bs.numwakeup++;
fs.numwakeup++;
//针对能唤醒设备的闹钟,这里会做一些唤醒设备的事情
activitymanagernative.notewakeupalarm(
alarm.operation);
} catch (pendingintent.canceledexception e) {
if (alarm.repeatinterval > 0) {
// this intentsender is no longer valid, but this
// is a repeating alarm, so toss the hoser.
remove(alarm.operation);
} catch (runtimeexception e) {
slog.w(tag, "failure sending alarm.", e);
说明:上述代码中,alarmthread会一直循环的跑着,一旦有新的alarm触发,它就会取出一个batch然后逐个发送pendingintent,具体alarm的触发是由底层来完成的,我没法再继续分析下去。还有就是alarm中有一些细节,我没有进行很具体的分析,实际上很简单,大家一看就懂。到此为止,alarm机制的主要流程也分析完了。
本文没有详细介绍如何使用alarm,因为很简单,看一下官方文档或者网上搜一下,到处都是。关于alarm,有一点需要强调一下:当手机重启或者应用被杀死的时候,alarm会被删除,因此,如果想通过alarm来完成长久定时任务是不可靠的,如果非要完成长久定时任务,可以这样:将应用的所有alarm信息存到数据库中,每次应用启动的时候都重新注册alarm并更新alarm的触发时间,通过这种方式就不存在alarm丢失的情况了。本文很长,耗时8个小时才完成的,感谢大家阅读本文,希望本文能给大家带来一点帮助。