天天看點

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個小時才完成的,感謝大家閱讀本文,希望本文能給大家帶來一點幫助。

繼續閱讀