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