Android源碼分析-Alarm機製與Binder的交互
轉載請注明出處:https://blog.csdn.net/singwhatiwanna/article/details/18448997
前言
本次給大家分析的是Android中Alarm的機製以及它和Binder的交互,所用源碼為最新的Android4.4。因為Alarm的功能都是通過Binder來完成的,所以,介紹Alarm之前必須要先介紹下它是如何調用Binder來完成定時功能的。由於內容較多,本文會比較長,在文章結構安排上是這樣的:首先簡單介紹如何使用Alarm並給出其工作原理,接著分析Alarm和Timer以及Handler在完成定時任務上的差別,然後分析Alarm與Binder的交互,最後分析Alarm機製的源碼。
什麼是Alarm
Alarm是android提供的用於完成鬧鍾式定時任務的類,係統通過AlarmManager來管理所有的Alarm,Alarm支持一次性定時任務和循環定時任務,它的使用方式很簡單,這裏不多做介紹,隻給出一個簡單的示例:
- 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);
Alarm和Timer以及Handler在定時任務上的區別
相同點:
三者都可以完成定時任務,都支持一次性定時和循環定時(注:Handler可以間接支持循環定時任務)
不同點:
Handler和Timer在定時上是類似的,二者在係統休眠的情況下無法正常工作,定時任務不會按時觸發。Alarm在係統休眠的情況下可以正常工作,並且還可以決定是否喚醒係統,同時Alarm在自身不啟動的情況下仍能正常收到定時任務提醒,但是當係統重啟或者應用被殺死的情況下,Alarm定時任務會被取消。另外,從Android4.4開始,Alarm事件默認采用非精準方式,即定時任務可能會有小範圍的提前或延後,當然我們可以強製采用精準方式,而在此之前,Alarm事件都是精準方式。
Alarm與Binder的交互
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,
- PendingIntent operation, WorkSource workSource) {
- 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機製有更加深刻的認識。
各種Manager和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
- public Object getSystemService(String name) {
- if (LAYOUT_INFLATER_SERVICE.equals(name)) {
- if (mInflater == null) {
- mInflater = LayoutInflater.from(mBase).cloneInContext(this);
- }
- return mInflater;
- }
- return mBase.getSystemService(name);
- }
- //#Activity
- @Override
- public Object getSystemService(String name) {
- 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對象,何以見得?請看下麵分析
ContextImpl:Activity的mBase
不知道大家對我寫的另外一篇源碼分析是否有印象: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() {
- public Object getService(ContextImpl ctx) {
- 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() {
- public Object createService(ContextImpl ctx) {
- return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
- }});
- //這裏是Alarm服務的注冊
- registerService(ALARM_SERVICE, new ServiceFetcher() {
- public Object createService(ContextImpl ctx) {
- /**還記得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() {
- public Object createService(ContextImpl ctx) {
- return new AudioManager(ctx);
- }});
- ...省略:下麵還有許多服務
- }
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);
最後更新:2017-04-03 12:55:33
上一篇:
使用反射獲取字節碼文件中的方法
下一篇:
Winform Panel按鈕位置
Missing javax.transaction.jta artifact
asp.net性能的技巧
Magento - GRID FILTER FOR COLUMNS WITH COMPLEX VALUES
ListView嵌套在TabActivity裏數據不更新解決辦法
Linux內核--usb子係統的分析
ls -l文件類型
《Web前端開發精品課——HTML5 Canvas開發詳解》——第一部分 Canvas基礎
“精靈學院”課堂筆記2——針對企業的DevOps改進和實踐(下)
持續集成(CI)的作用-Wise2C容器技術的開展
《Log4j 2官方文檔》 Configuration Syntax
相關內容