天天看点

Android源码分析-Alarm机制与Binder的交互

转载请注明出处: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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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是按照如下这种方式声明的:

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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的继承关系,如下图所示:

Android源码分析-Alarm机制与Binder的交互

 再看如下代码,观察下它们中的getsystemservice方法是如何实现的

code:各种getsystemservice方法

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

//#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的赋值过程

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

//一个哈希表,用来根据服务名存储对应服务的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是同时触发的,可以用下面这个示意图来描述:

Android源码分析-Alarm机制与Binder的交互

上图是示意图,系统中可以有多个batch,每个batch中可以有多个alarm。下面我们分析一下alarmmanagerservice中的代码。其入口方法为set,set又调用了setimpllocked,所以我们直接看setimpllocked。

code:alarmmanagerservice#setimpllocked

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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

Android源码分析-Alarm机制与Binder的交互
Android源码分析-Alarm机制与Binder的交互

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个小时才完成的,感谢大家阅读本文,希望本文能给大家带来一点帮助。

继续阅读