版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、AlarmManager研究android 4.0目錄(?)-概述AlarmManagerAlarmManager的成員函數(shù)AlarmManagerService邏輯鬧鐘主要行為設(shè)置alarm重復(fù)性alarm取消alarm設(shè)置系統(tǒng)時(shí)間和時(shí)區(qū)運(yùn)作細(xì)節(jié)AlarmThread和Alarm的激發(fā)AlarmThread中的runwaitForAlarmtriggerAlarmsLocked進(jìn)一步處理喚醒鬧鐘說(shuō)說(shuō)AlarmManagerService中的mBroadcastRefCountAlarmManager研究侯 亮 1.概述
2、160; 在Android系統(tǒng)中,鬧鐘和喚醒功能都是由Alarm Manager Service控制并管理的。我們所熟悉的RTC鬧鐘以及定時(shí)器都和它有莫大的關(guān)系。為了便于稱呼,我常常也把這個(gè)service簡(jiǎn)稱為ALMS。 另外,ALMS還提供了一個(gè)AlarmManager輔助類。在實(shí)際的代碼中,應(yīng)用程序一般都是通過(guò)這個(gè)輔助類來(lái)和ALMS打交道的。就代碼而言,輔助類只不過(guò)是把一些邏輯語(yǔ)義傳遞給ALMS服務(wù)端而已,具體怎么做則完全要看ALMS的實(shí)現(xiàn)代碼了。
3、 ALMS的實(shí)現(xiàn)代碼并不算太復(fù)雜,主要只是在管理“邏輯鬧鐘”。它把邏輯鬧鐘分成幾個(gè)大類,分別記錄在不同的列表中。然后ALMS會(huì)在一個(gè)專門的線程中循環(huán)等待鬧鐘的激發(fā),一旦時(shí)機(jī)到了,就“回調(diào)”邏輯鬧鐘對(duì)應(yīng)的動(dòng)作。 以上只是一些概要性的介紹,下面我們來(lái)看具體的技術(shù)細(xì)節(jié)。 2.AlarmManager 前文我們已經(jīng)說(shuō)過(guò),ALMS只是服務(wù)端的東西。它必須向外提供具體的接口,才能被外界使用。在A
4、ndroid平臺(tái)中,ALMS的外部接口為IAlarmManager。其定義位于frameworksbasecorejavaandroidappIAlarmManager.aidl腳本中,定義截選如下: interface IAlarmManager void set(int type, long triggerAtTime, in PendingIntent operation); void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation); void setIn
5、exactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation); void setTime(long millis); void setTimeZone(String zone); void remove(in PendingIntent operation); 在一般情況下,service的使用者會(huì)通過(guò)Service Manager Service接口,先拿到它感興趣的service對(duì)應(yīng)的代理I接口
6、,然后再調(diào)用I接口的成員函數(shù)向service發(fā)出請(qǐng)求。所以按理說(shuō),我們也應(yīng)該先拿到一個(gè)IAlarmManager接口,然后再使用它。可是,對(duì)Alarm Manager Service來(lái)說(shuō),情況略有不同,其最常見(jiàn)的調(diào)用方式如下:manager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);其中,getSystemService()返回的不再是IAlarmManager接口,而是AlarmManager對(duì)象。
7、我們參考AlarmManager.java文件,可以看到AlarmManager類中聚合了一個(gè)IAlarmManager接口,private final IAlarmManager mService;也就是說(shuō)在執(zhí)行實(shí)際動(dòng)作時(shí),AlarmManager只不過(guò)是把外界的請(qǐng)求轉(zhuǎn)發(fā)給內(nèi)部聚合的IAlarmManager接口而已。 2.1 AlarmManager的成員函數(shù) AlarmManager的成員函數(shù)有:AlarmManager(IAlarmManager service)public void se
8、t(int type, long triggerAtTime, PendingIntent operation)public void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation)public void setInexactRepeating(int type, long triggerAtTime, long interval, PendingIntent operation)public void cancel(PendingIntent operation)public
9、 void setTime(long millis)public void setTimeZone(String timeZone)即1個(gè)構(gòu)造函數(shù),6個(gè)功能函數(shù)?;旧贤耆虸AlarmManager的成員函數(shù)一一對(duì)應(yīng)。 另外,AlarmManager類中會(huì)以不同的公共常量來(lái)表示多種不同的邏輯鬧鐘,在Android 4.0的原生代碼中有4種邏輯鬧鐘: 1) RTC_WAKEUP 2) RTC 3) ELAPSED_REALTIME_W
10、AKEUP 4) ELAPSED_REALTIME 應(yīng)用側(cè)通過(guò)調(diào)用AlarmManager對(duì)象的成員函數(shù),可以把語(yǔ)義傳遞到AlarmManagerService,并由它進(jìn)行實(shí)際的處理。 3.AlarmManagerService ALMS的重頭戲在AlarmManagerService中,這個(gè)類繼承于IAlarmManager.Stub,所以是個(gè)binder實(shí)體。它包含的重要成員如下:其中,mRtcWakeu
11、pAlarms等4個(gè)ArrayList<Alarm>數(shù)組分別對(duì)應(yīng)著前文所說(shuō)的4種“邏輯鬧鐘”。為了便于理解,我們可以想象在底層有4個(gè)“實(shí)體鬧鐘”,注意,是4個(gè),不是4類。上面每一類“邏輯鬧鐘”都會(huì)對(duì)應(yīng)一個(gè)“實(shí)體鬧鐘”,而邏輯鬧鐘則可以有若干個(gè),它們被存儲(chǔ)在ArrayList中,示意圖如下: 當(dāng)然,這里所說(shuō)的“實(shí)體鬧鐘”只是個(gè)概念而已,其具體實(shí)現(xiàn)和底層驅(qū)動(dòng)有關(guān),在frameworks層不必過(guò)多關(guān)心。 Frameworks層應(yīng)該關(guān)心的是那幾個(gè)ArrayList<A
12、larm>。這里的Alarm對(duì)應(yīng)著邏輯鬧鐘。 3.1 邏輯鬧鐘 Alarm是AlarmManagerService的一個(gè)內(nèi)嵌類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 p
13、id; . . . . . .其中記錄了邏輯鬧鐘的一些關(guān)鍵信息。 type域:記錄著邏輯鬧鐘的鬧鐘類型,比如RTC_WAKEUP、ELAPSED_REALTIME_WAKEUP等;count域:是個(gè)輔助域,它和repeatInterval域一起工作。當(dāng)repeatInterval大于0時(shí),這個(gè)域可被用于計(jì)算下一次重復(fù)激發(fā)alarm的時(shí)間,詳細(xì)情況見(jiàn)后文;when域:記錄鬧鐘的激發(fā)時(shí)間。這個(gè)域和type域相關(guān),詳細(xì)情況見(jiàn)后文;repeatInterval域:表示重復(fù)激發(fā)鬧鐘的時(shí)間間隔,如果鬧鐘只需激發(fā)一次,則此域?yàn)?,如果鬧鐘需要重復(fù)激發(fā),此域?yàn)橐院撩霝閱挝坏臅r(shí)間間隔;operation域:記錄
14、鬧鐘激發(fā)時(shí)應(yīng)該執(zhí)行的動(dòng)作,詳細(xì)情況見(jiàn)后文;uid域:記錄設(shè)置鬧鐘的進(jìn)程的uid;pid域:記錄設(shè)置鬧鐘的進(jìn)程的pid。 總體來(lái)說(shuō)還是比較簡(jiǎn)單的,我們先補(bǔ)充說(shuō)明一下其中的count域。這個(gè)域是針對(duì)重復(fù)性鬧鐘的一個(gè)輔助域。重復(fù)性鬧鐘的實(shí)現(xiàn)機(jī)理是,如果當(dāng)前時(shí)刻已經(jīng)超過(guò)鬧鐘的激發(fā)時(shí)刻,那么ALMS會(huì)先從邏輯鬧鐘數(shù)組中摘取下Alarm節(jié)點(diǎn),并執(zhí)行鬧鐘對(duì)應(yīng)的邏輯動(dòng)作,然后進(jìn)一步比較“當(dāng)前時(shí)刻”和“Alarm理應(yīng)激發(fā)的理想時(shí)刻”之間的時(shí)間跨度,從而計(jì)算出Alarm的“下一次理應(yīng)激發(fā)的理想時(shí)刻”,并將這個(gè)激發(fā)時(shí)間記
15、入Alarm節(jié)點(diǎn),接著將該節(jié)點(diǎn)重新排入邏輯鬧鐘列表。這一點(diǎn)和普通Alarm不太一樣,普通Alarm節(jié)點(diǎn)摘下后就不再還回邏輯鬧鐘列表了。 “當(dāng)前時(shí)刻”和“理應(yīng)激發(fā)時(shí)刻”之間的時(shí)間跨度會(huì)隨實(shí)際的運(yùn)作情況而變動(dòng)。我們分兩步來(lái)說(shuō)明“下一次理應(yīng)激發(fā)時(shí)刻”的計(jì)算公式: 1) count = (時(shí)間跨度 / repeatInterval ) + 1 ; 2) “下一次理應(yīng)激發(fā)時(shí)刻” = “上一次理應(yīng)激發(fā)時(shí)刻”+ count * rep
16、eatInterval ; 我們畫一張示意圖,其中綠色的可激發(fā)時(shí)刻表示“上一次理應(yīng)激發(fā)時(shí)刻”,我們假定“當(dāng)前時(shí)刻”分別為now_1處或now_2處,可以看到會(huì)計(jì)算出不同的“下一次理應(yīng)激發(fā)時(shí)刻”,這里用桔紅色表示。 可以看到,如果當(dāng)前時(shí)刻為now_1,那么它和“上一次理應(yīng)激發(fā)時(shí)刻”之間的“時(shí)間跨度”是小于一個(gè)repeatInterval的,所以count數(shù)為1。而如果當(dāng)前時(shí)刻為now
17、_2,那么“時(shí)間跨度”與repeatInterval的商取整后為2,所以count數(shù)為3。另外,圖中那兩個(gè)虛線箭頭對(duì)應(yīng)的可激發(fā)時(shí)刻,只是用來(lái)做刻度的東西。 3.2 主要行為 接下來(lái)我們來(lái)看ALMS中的主要行為,這些行為和AlarmManager輔助類提供的成員函數(shù)相對(duì)應(yīng)。 3.2.1 設(shè)置alarm 外界能接觸的設(shè)置alarm的函數(shù)是set():public void se
18、t(int type, long triggerAtTime, PendingIntent operation)type:表示要設(shè)置的alarm類型。如前文所述,有4個(gè)alarm類型。 triggerAtTime:表示alarm“理應(yīng)激發(fā)”的時(shí)間。 operation:指明了alarm鬧鈴激發(fā)時(shí)需要執(zhí)行的動(dòng)作,比如執(zhí)行某種廣播通告。 設(shè)置alarm的動(dòng)作會(huì)牽扯到一個(gè)發(fā)起者。簡(jiǎn)單地說(shuō),發(fā)起者會(huì)向Alarm Manager Service發(fā)出一個(gè)設(shè)置alarm的請(qǐng)求,而且在請(qǐng)求里注明
19、了到時(shí)間后需要執(zhí)行的動(dòng)作。由于“待執(zhí)行的動(dòng)作”一般都不會(huì)馬上執(zhí)行,所以要表達(dá)成PendingIntent的形式。(PendingIntent的詳情可參考其他文章) 另外,triggerAtTime參數(shù)的意義也會(huì)隨type參數(shù)的不同而不同。簡(jiǎn)單地說(shuō),如果type是和RTC相關(guān)的話,那么triggerAtTime的值應(yīng)該是標(biāo)準(zhǔn)時(shí)間,即從1970 年 1 月 1 日午夜開(kāi)始所經(jīng)過(guò)的毫秒數(shù)。而如果type是其他類型的話,那么triggerAtTime的值應(yīng)該是從本次開(kāi)機(jī)開(kāi)始算起的毫秒數(shù)。
20、0;3.2.2 重復(fù)性alarm 另一個(gè)設(shè)置alarm的函數(shù)是setRepeating():public void setRepeating(int type, long triggerAtTime, long interval,PendingIntent operation)其參數(shù)基本上和set()函數(shù)差不多,只是多了一個(gè)“時(shí)間間隔”參數(shù)。事實(shí)上,在Alarm Manager Service一側(cè),set()函數(shù)內(nèi)部也是在調(diào)用setRepeating()的,只不過(guò)會(huì)把interval設(shè)成
21、了0。 setRepeating()的實(shí)現(xiàn)函數(shù)如下:public void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) if (operation = null) Slog.w(TAG, "set/setRepeating ignored because there is no intent"); return; synchronize
22、d (mLock) Alarm alarm = new Alarm(); alarm.type = type; alarm.when = triggerAtTime; alarm.repeatInterval = interval; alarm.operation = operation; / Remove this alarm if already scheduled. removeLocked(operation); if (localLOGV) Slog.v(TAG, "set: " + alarm); int index = addAlarmLocked(alarm
23、); if (index = 0) setLocked(alarm); 代碼很簡(jiǎn)單,會(huì)創(chuàng)建一個(gè)邏輯鬧鐘Alarm,而后調(diào)用addAlarmLocked()將邏輯鬧鐘添加到內(nèi)部邏輯鬧鐘數(shù)組的某個(gè)合適位置。private int addAlarmLocked(Alarm alarm) ArrayList<Alarm> alarmList = getAlarmList(alarm.type); int index = Collections.binarySearch(alarmList, a
24、larm, mIncreasingTimeOrder); if (index < 0) index = 0 - index - 1; if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index); alarmList.add(index, alarm); . . . . . . return index; 邏輯鬧鐘列表是依據(jù)alarm的激發(fā)時(shí)間進(jìn)行排序的,越早激發(fā)的alarm,越
25、靠近第0位。所以,addAlarmLocked()在添加新邏輯鬧鐘時(shí),需要先用二分查找法快速找到列表中合適的位置,然后再把Alarm對(duì)象插入此處。 如果所插入的位置正好是第0位,就說(shuō)明此時(shí)新插入的這個(gè)邏輯鬧鐘將會(huì)是本類alarm中最先被激發(fā)的alarm,而正如我們前文所述,每一類邏輯鬧鐘會(huì)對(duì)應(yīng)同一個(gè)“實(shí)體鬧鐘”,此處我們?cè)诘?位設(shè)置了新的激發(fā)時(shí)間,明確表示我們以前對(duì)“實(shí)體鬧鐘”設(shè)置的激發(fā)時(shí)間已經(jīng)不準(zhǔn)確了,所以setRepeating()中必須重新調(diào)整一下“實(shí)體鬧鐘”的激發(fā)時(shí)間,于是有了下面的句子:if (index = 0) setLocked(alarm);
26、 setLocked()內(nèi)部會(huì)調(diào)用native函數(shù)set():private native void set(int fd, int type, long seconds, long nanoseconds);重新設(shè)置“實(shí)體鬧鐘”的激發(fā)時(shí)間。這個(gè)函數(shù)內(nèi)部會(huì)調(diào)用ioctl()和底層打交道。具體代碼可參考frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp文件:static void android_server_AlarmManager
27、Service_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds) struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); if (result < 0) ALOGE("Unable to set alarm to %lld.%09lld: %sn", secon
28、ds, nanoseconds, strerror(errno); 我們知道,PendingIntent只是frameworks一層的概念,和底層驅(qū)動(dòng)是沒(méi)有關(guān)系的。所以向底層設(shè)置alarm時(shí)只需要type信息以及激發(fā)時(shí)間信息就可以了。 3.2.3 取消alarm 用戶端是調(diào)用AlarmManager對(duì)象的cancel()函數(shù)來(lái)取消alarm的。這個(gè)函數(shù)內(nèi)部其實(shí)是調(diào)用IA
29、larmManager的remove()函數(shù)。所以我們只來(lái)看AlarmManagerService的remove()就可以了。public void remove(PendingIntent operation) if (operation = null) return; synchronized (mLock) removeLocked(operation); 注意,在取消alarm時(shí),是以一個(gè)PendingIntent對(duì)象作為參數(shù)的。
30、這個(gè)PendingIntent對(duì)象正是當(dāng)初設(shè)置alarm時(shí),所傳入的那個(gè)operation參數(shù)。我們不能隨便創(chuàng)建一個(gè)新的PendingIntent對(duì)象來(lái)調(diào)用remove()函數(shù),否則remove()是不會(huì)起作用的。PendingIntent的運(yùn)作細(xì)節(jié)不在本文論述范圍之內(nèi),此處我們只需粗淺地知道,PendingIntent對(duì)象在AMS(Activity Manager Service)端會(huì)對(duì)應(yīng)一個(gè)PendingIntentRecord實(shí)體,而ALMS在遍歷邏輯鬧鐘列表時(shí),是根據(jù)是否指代相同PendingIntentRecord實(shí)體來(lái)判斷PendingIntent的相符情況的。如果我們隨便創(chuàng)建一個(gè)
31、PendingIntent對(duì)象并傳入remove()函數(shù)的話,那么在ALMS端勢(shì)必找不到相符的PendingIntent對(duì)象,所以remove()必然無(wú)效。 remove()中調(diào)用的removeLocked()如下:public void removeLocked(PendingIntent operation) removeLocked(mRtcWakeupAlarms, operation); removeLocked(mRtcAlarms, operation); removeLo
32、cked(mElapsedRealtimeWakeupAlarms, operation); removeLocked(mElapsedRealtimeAlarms, operation);簡(jiǎn)單地說(shuō)就是,把4個(gè)邏輯鬧鐘數(shù)組都遍歷一遍,刪除其中所有和operation相符的Alarm節(jié)點(diǎn)。removeLocked()的實(shí)現(xiàn)代碼如下:private void removeLocked(ArrayList<Alarm> alarmList, PendingIntent operation) if (alarmList.size() <= 0) return; / iterator
33、over the list removing any it where the intent match Iterator<Alarm> it = alarmList.iterator(); while (it.hasNext() Alarm alarm = it.next(); if (alarm.operation.equals(operation) it.remove(); 請(qǐng)注意,所謂的取消alarm,只是刪除了對(duì)應(yīng)的邏輯Alarm節(jié)點(diǎn)而已,并不會(huì)和底層驅(qū)動(dòng)再打什么
34、交道。也就是說(shuō),是不存在針對(duì)底層“實(shí)體鬧鐘”的刪除動(dòng)作的。所以,底層“實(shí)體鬧鐘”在到時(shí)之時(shí),還是會(huì)被“激發(fā)”出來(lái)的,只不過(guò)此時(shí)在frameworks層,會(huì)因?yàn)檎也坏椒弦蟮摹斑壿嬼[鐘”而不做進(jìn)一步的激發(fā)動(dòng)作。 3.2.4 設(shè)置系統(tǒng)時(shí)間和時(shí)區(qū) AlarmManager還提供設(shè)置系統(tǒng)時(shí)間的功能,設(shè)置者需要具有android.permission.SET_TIME權(quán)限。public void setTime(long millis) mContext.enforceCallingO
35、rSelfPermission("android.permission.SET_TIME", "setTime"); SystemClock.setCurrentTimeMillis(millis); 另外,還具有設(shè)置時(shí)區(qū)的功能:public void setTimeZone(String tz)相應(yīng)地,設(shè)置者需要具有android.permission.SET_TIME_ZONE權(quán)限。 3.3 運(yùn)作細(xì)節(jié)3.3.1
36、 AlarmThread和Alarm的激發(fā) AlarmManagerService內(nèi)部是如何感知底層激發(fā)alarm的呢?首先,AlarmManagerService有一個(gè)表示線程的mWaitThread成員:private final AlarmThread mWaitThread = new AlarmThread();在AlarmManagerService構(gòu)造之初,就會(huì)啟動(dòng)這個(gè)專門的“等待線程”。public AlarmManagerService(Context context) mCont
37、ext = context; mDescriptor = init(); . . . . . . . . . . . . if (mDescriptor != -1) mWaitThread.start(); / 啟動(dòng)線程! else Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); AlarmManagerService的構(gòu)造函數(shù)一開(kāi)始就會(huì)調(diào)用一個(gè)init()函數(shù),該函數(shù)是個(gè)native函數(shù),它的內(nèi)部會(huì)打開(kāi)alarm驅(qū)動(dòng),并返回驅(qū)動(dòng)文件句柄。只要能夠順利打開(kāi)alarm驅(qū)動(dòng),ALM
38、S就可以走到mWaitThread.start()一句,于是“等待線程”就啟動(dòng)了。 3.3.1.1 AlarmThread中的run() AlarmThread本身是AlarmManagerService中一個(gè)繼承于Thread的內(nèi)嵌類:private class AlarmThread extends Thread其最核心的run()函數(shù)的主要?jiǎng)幼髁鞒虉D如下: 我們分別來(lái)闡述上圖中的關(guān)鍵步驟。 3.3.1.2 waitForAlarm()
39、160; 首先,從上文的流程圖中可以看到,AlarmThread線程是在一個(gè)while(true)循環(huán)里不斷調(diào)用waitForAlarm()函數(shù)來(lái)等待底層alarm激發(fā)動(dòng)作的。waitForAlarm()是一個(gè)native函數(shù):private native int waitForAlarm(int fd);其對(duì)應(yīng)的C+層函數(shù)是android_server_AlarmManagerService_waitForAlarm():【com_android_server_AlarmManagerService.cpp】static
40、 jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) int result = 0; do result = ioctl(fd, ANDROID_ALARM_WAIT); while (result < 0 && errno = EINTR); if (result < 0) ALOGE("Unable to wait on alarm: %sn", strerror(errno); return 0; return res
41、ult; 當(dāng)AlarmThread調(diào)用到ioctl()一句時(shí),線程會(huì)阻塞住,直到底層激發(fā)alarm。而且所激發(fā)的alarm的類型會(huì)記錄到ioctl()的返回值中。這個(gè)返回值對(duì)外界來(lái)說(shuō)非常重要,外界用它來(lái)判斷該遍歷哪個(gè)邏輯鬧鐘列表。 3.3.1.3 triggerAlarmsLocked() 一旦等到底層驅(qū)動(dòng)的激發(fā)動(dòng)作,AlarmThread會(huì)開(kāi)始遍歷相應(yīng)的邏輯鬧鐘列表:ArrayList<Alarm&
42、gt; triggerList = new ArrayList<Alarm>();. . . . . .final long nowRTC = System.currentTimeMillis();final long nowELAPSED = SystemClock.elapsedRealtime();. . . . . .if (result & RTC_WAKEUP_MASK) != 0) triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);if (result & RTC_MASK) != 0
43、) triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);if (result & ELAPSED_REALTIME_WAKEUP_MASK) != 0) triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);if (result & ELAPSED_REALTIME_MASK) != 0) triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
44、160; 可以看到,AlarmThread先創(chuàng)建了一個(gè)臨時(shí)的數(shù)組列表triggerList,然后根據(jù)result的值對(duì)相應(yīng)的alarm數(shù)組列表調(diào)用triggerAlarmsLocked(),一旦發(fā)現(xiàn)alarm數(shù)組列表中有某個(gè)alarm符合激發(fā)條件,就把它移到triggerList中。這樣,4條alarm數(shù)組列表中需要激發(fā)的alarm就匯總到triggerList數(shù)組列表中了。 接下來(lái),只需遍歷
45、一遍triggerList就可以了:Iterator<Alarm> it = triggerList.iterator();while (it.hasNext() Alarm alarm = it.next(); . . . . . . alarm.operation.send(mContext, 0, mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count), mResultReceiver, mHandler); / we have an active broadcast so stay awake. i
46、f (mBroadcastRefCount = 0) setWakelockWorkSource(alarm.operation); mWakeLock.acquire(); mInFlight.add(alarm.operation); mBroadcastRefCount+; mTriggeredUids.add(new Integer(alarm.uid); BroadcastStats bs = getStatsLocked(alarm.operation); if (bs.nesting = 0) bs.startTime = nowELAPSED; else bs.nesting+
47、; if (alarm.type = AlarmManager.ELAPSED_REALTIME_WAKEUP | alarm.type = AlarmManager.RTC_WAKEUP) bs.numWakeup+; ActivityManagerNative.noteWakeupAlarm(alarm.operation); 在上面的while循環(huán)中,每遍歷到一個(gè)Alarm對(duì)象,就執(zhí)行它的alarm.operation.send()函數(shù)。我們知道,alarm中記錄的operation就是當(dāng)初設(shè)置它時(shí)傳來(lái)的那個(gè)PendingIntent對(duì)象,現(xiàn)在開(kāi)始執(zhí)行PendingIntent的send
48、()操作啦。 PendingIntent的send()函數(shù)代碼是:public void send(Context context, int code, Intent intent, OnFinished onFinished, Handler handler) throws CanceledException send(context, code, intent, onFinished, handler, null);調(diào)用了下面的send()函數(shù):public void send(Co
49、ntext context, int code, Intent intent, OnFinished onFinished, Handler handler, String requiredPermission) throws CanceledException try String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver() : null; int res = mTarget.send(code, intent, resolvedType, onFinished
50、 != null ? new FinishedDispatcher(this, onFinished, handler) : null, requiredPermission); if (res < 0) throw new CanceledException(); catch (RemoteException e) throw new CanceledException(e); mTarget是個(gè)IPendingIntent代理接口,它對(duì)應(yīng)AMS(Activity Manager Service)中的某個(gè)PendingIntentRecord實(shí)體。需要說(shuō)明的是,PendingInten
51、t的重要信息都是在AMS的PendingIntentRecord以及PendingIntentRecord.Key對(duì)象中管理的。AMS中有一張哈希表專門用于記錄所有可用的PendingIntentRecord對(duì)象。 相較起來(lái),在創(chuàng)建PendingIntent對(duì)象時(shí)傳入的intent數(shù)組,其重要性并不太明顯。這種intent數(shù)組主要用于一次性啟動(dòng)多個(gè)activity,如果你只是希望啟動(dòng)一個(gè)activity或一個(gè)service,那么這個(gè)intent的內(nèi)容有可能在最終執(zhí)行PendingIntent的sen
52、d()動(dòng)作時(shí),被新傳入的intent內(nèi)容替換掉。 AMS中關(guān)于PendingIntentRecord哈希表的示意圖如下:AMS是整個(gè)Android平臺(tái)中最復(fù)雜的一個(gè)核心service了,所以我們不在這里做過(guò)多的闡述,有興趣的讀者可以參考其他相關(guān)文檔。 3.3.1.4 進(jìn)一步處理“喚醒鬧鐘” 在AlarmThread.run()函數(shù)中while循環(huán)的最后,會(huì)進(jìn)一步判斷,當(dāng)前激發(fā)的ala
53、rm是不是“喚醒鬧鐘”。如果鬧鐘類型為RTC_WAKEUP或ELAPSED_REALTIME_WAKEUP,那它就屬于“喚醒鬧鐘”,此時(shí)需要通知一下AMS:if (alarm.type = AlarmManager.ELAPSED_REALTIME_WAKEUP | alarm.type = AlarmManager.RTC_WAKEUP) bs.numWakeup+; ActivityManagerNative.noteWakeupAlarm(alarm.operation);這兩種alarm就是我們常說(shuō)的0型和2型鬧鐘,它們和我們手機(jī)的續(xù)航時(shí)間息息相關(guān)。
54、 AMS里的noteWakeupAlarm()比較簡(jiǎn)單,只是在調(diào)用BatteryStatsService服務(wù)的相關(guān)動(dòng)作,但是卻會(huì)導(dǎo)致機(jī)器的喚醒:public void noteWakeupAlarm(IIntentSender sender) if (!(sender instanceof PendingIntentRecord) return; BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); synchronized (
55、stats) if (mBatteryStatsService.isOnBattery() mBatteryStatsService.enforceCallingPermission(); PendingIntentRecord rec = (PendingIntentRecord)sender; int MY_UID = Binder.getCallingUid(); int uid = rec.uid = MY_UID ? Process.SYSTEM_UID : rec.uid; BatteryStatsImpl.Uid.Pkg pkg = stats.getPackageStatsLo
56、cked(uid, rec.key.packageName); pkg.incWakeupsLocked(); 好了,說(shuō)了這么多,我們還是畫一張AlarmThread示意圖作為總結(jié): 3.3.2 說(shuō)說(shuō)AlarmManagerService中的mBroadcastRefCount 下面我們說(shuō)說(shuō)AlarmManagerService中的mBroadcastRefCount,之所以要說(shuō)它,僅僅是因?yàn)槲以谛薷腁larmManagerService代碼的時(shí)候,吃過(guò)它的虧。 我們先回顧一下處理triggerList列表的代碼,如下:Iterator<Alarm> it = trig
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 水產(chǎn)養(yǎng)殖行業(yè)助理工作總結(jié)
- 美容行業(yè)人力資源管理經(jīng)驗(yàn)總結(jié)
- 樓梯扶手銷售工作總結(jié)
- 潛水行業(yè)潛水技巧培訓(xùn)回顧
- 糧食行業(yè)話務(wù)員工作總結(jié)
- 2024年河南省商丘市公開(kāi)招聘警務(wù)輔助人員輔警筆試自考題1卷含答案
- 2024年四川省涼山自治州公開(kāi)招聘警務(wù)輔助人員輔警筆試自考題1卷含答案
- 2022年山東省煙臺(tái)市公開(kāi)招聘警務(wù)輔助人員輔警筆試自考題1卷含答案
- 農(nóng)村先進(jìn)個(gè)人發(fā)言稿
- PEP小學(xué)生讀書心得體會(huì)作文選
- 2024版Amazon店鋪代運(yùn)營(yíng)與品牌授權(quán)及維權(quán)服務(wù)合同3篇
- 環(huán)境因素控制措施
- 采購(gòu)合同范例壁布
- 2024年下學(xué)期學(xué)校德育工作總結(jié)
- 公司員工出差車輛免責(zé)協(xié)議書
- 2024年陜西榆林市神木市公共服務(wù)輔助人員招聘775人歷年管理單位遴選500模擬題附帶答案詳解
- 安全生產(chǎn)事故案例分析
- 《電化學(xué)儲(chǔ)能系統(tǒng)艙大件運(yùn)輸特殊要求》
- 2025年采購(gòu)部工作計(jì)劃
- 期末檢測(cè)卷(一)(試卷)-2024-2025學(xué)年外研版(三起)英語(yǔ)六年級(jí)上冊(cè)(含答案含聽(tīng)力原文無(wú)音頻)
- 《防范于心反詐于行》中小學(xué)防范電信網(wǎng)絡(luò)詐騙知識(shí)宣傳課件
評(píng)論
0/150
提交評(píng)論