閱讀686 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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支持一次性定時任務和循環定時任務,它的使用方式很簡單,這裏不多做介紹,隻給出一個簡單的示例:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);  
  2. Intent intent = new Intent(getApplicationContext(), TestActivity.class);  
  3. PendingIntent pendIntent = PendingIntent.getActivity(getApplicationContext(),  
  4.         0, intent, PendingIntent.FLAG_UPDATE_CURRENT);  
  5. //5秒後發送廣播,隻發送一次  
  6. int triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000;  
  7. 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

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public void set(int type, long triggerAtMillis, PendingIntent operation) {  
  2.     setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null);  
  3. }  
  4.   
  5. public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,  
  6.         PendingIntent operation, WorkSource workSource) {  
  7.     setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource);  
  8. }  
  9.   
  10. private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,  
  11.         PendingIntent operation, WorkSource workSource) {  
  12.     if (triggerAtMillis < 0) {  
  13.         /* NOTYET 
  14.         if (mAlwaysExact) { 
  15.             // Fatal error for KLP+ apps to use negative trigger times 
  16.             throw new IllegalArgumentException("Invalid alarm trigger time " 
  17.                     + triggerAtMillis); 
  18.         } 
  19.         */  
  20.         triggerAtMillis = 0;  
  21.     }  
  22.   
  23.     try {  
  24.         //定時任務實際上都有mService來完成,也就是說AlarmManager隻是一個空殼  
  25.         //從下麵的構造方法可以看出,這個mService是IAlarmManager類型的,而IAlarmManager是一個接口  
  26.         //如果大家了解AIDL就應該知道IAlarmManager應該是一個AIDL接口  
  27.         mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,  
  28.                 workSource);  
  29.     } catch (RemoteException ex) {  
  30.     }  
  31. }  
  32.   
  33. AlarmManager(IAlarmManager service, Context ctx) {  
  34.     mService = service;  
  35.   
  36.     final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;  
  37.     mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);  
  38. }  

說明:我對代碼進行了注釋,從注釋可以看出,現在我們需要去找到這個mService,其實我已經幫大家找到了,它就是AlarmManagerService,看下它的類的聲明:

class AlarmManagerService extends IAlarmManager.Stub

很顯然,AlarmManagerService的確實現了IAlarmManager接口,為什麼是顯然呢?因為按照AIDL的規範,IAlarmManager.Stub是按照如下這種方式聲明的:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public static abstract class Stub extends Binder implements IAlarmManager {  
  2.   
  3.     public static IAlarmManager asInterface(IBinder obj)  
  4.     ...  
  5. }  

可見這個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

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class SystemServer {  
  2.     private static final String TAG = "SystemServer";  
  3.   
  4.     public static final int FACTORY_TEST_OFF = 0;  
  5.     public static final int FACTORY_TEST_LOW_LEVEL = 1;  
  6.     public static final int FACTORY_TEST_HIGH_LEVEL = 2;  
  7.   
  8.     static Timer timer;  
  9.     static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000// 1hr  
  10.   
  11.     // The earliest supported time.  We pick one day into 1970, to  
  12.     // give any timezone code room without going into negative time.  
  13.     private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;  
  14.   
  15.     /** 
  16.      * Called to initialize native system services. 
  17.      * 初始化本地係統服務,jni方法 
  18.      */  
  19.     private static native void nativeInit();  
  20.   
  21.     //main方法,由底層調用  
  22.     public static void main(String[] args) {  
  23.         if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {  
  24.             // If a device's clock is before 1970 (before 0), a lot of  
  25.             // APIs crash dealing with negative numbers, notably  
  26.             // java.io.File#setLastModified, so instead we fake it and  
  27.             // hope that time from cell towers or NTP fixes it  
  28.             // shortly.  
  29.             Slog.w(TAG, "System clock is before 1970; setting to 1970.");  
  30.             SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);  
  31.         }  
  32.   
  33.         if (SamplingProfilerIntegration.isEnabled()) {  
  34.             SamplingProfilerIntegration.start();  
  35.             timer = new Timer();  
  36.             timer.schedule(new TimerTask() {  
  37.                 @Override  
  38.                 public void run() {  
  39.                     SamplingProfilerIntegration.writeSnapshot("system_server"null);  
  40.                 }  
  41.             }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);  
  42.         }  
  43.   
  44.         // Mmmmmm... more memory!  
  45.         dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();  
  46.   
  47.         // The system server has to run all of the time, so it needs to be  
  48.         // as efficient as possible with its memory usage.  
  49.         VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);  
  50.   
  51.         Environment.setUserRequired(true);  
  52.   
  53.         System.loadLibrary("android_servers");  
  54.   
  55.         Slog.i(TAG, "Entered the Android system server!");  
  56.   
  57.         // 初始化本地服務.  
  58.         nativeInit();  
  59.   
  60.         //這裏是關鍵,ServerThread被創建,同時其initAndLoop被調用  
  61.         ServerThread thr = new ServerThread();  
  62.         thr.initAndLoop();  
  63.     }  
  64. }  

接著看ServerThread的initAndLoop方法,該方法中,主線程Looper會被創建,各種Binder服務會被創建。該方法太長,我進行了截斷,隻展出我們所關心的代碼。

code:ServerThread#initAndLoop

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public void initAndLoop() {  
  2.     EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,  
  3.         SystemClock.uptimeMillis());  
  4.     //主線程Looper被創建  
  5.     Looper.prepareMainLooper();  
  6.   
  7.     android.os.Process.setThreadPriority(  
  8.             android.os.Process.THREAD_PRIORITY_FOREGROUND);  
  9.   
  10.     BinderInternal.disableBackgroundScheduling(true);  
  11.     android.os.Process.setCanSelfBackground(false);  
  12.     ...此處省略  
  13.     //下麵是各種Binder服務,從名字我們應該能夠大致看出它們所對應的Manager  
  14.     Installer installer = null;  
  15.     AccountManagerService accountManager = null;  
  16.     ContentService contentService = null;  
  17.     LightsService lights = null;  
  18.     PowerManagerService power = null;  
  19.     DisplayManagerService display = null;  
  20.     BatteryService battery = null;  
  21.     VibratorService vibrator = null;  
  22.     AlarmManagerService alarm = null;  
  23.     MountService mountService = null;  
  24.     NetworkManagementService networkManagement = null;  
  25.     NetworkStatsService networkStats = null;  
  26.     NetworkPolicyManagerService networkPolicy = null;  
  27.     ConnectivityService connectivity = null;  
  28.     WifiP2pService wifiP2p = null;  
  29.     WifiService wifi = null;  
  30.     NsdService serviceDiscovery= null;  
  31.     IPackageManager pm = null;  
  32.     Context context = null;  
  33.     WindowManagerService wm = null;  
  34.     BluetoothManagerService bluetooth = null;  
  35.     DockObserver dock = null;  
  36.     UsbService usb = null;  
  37.     SerialService serial = null;  
  38.     TwilightService twilight = null;  
  39.     UiModeManagerService uiMode = null;  
  40.     RecognitionManagerService recognition = null;  
  41.     NetworkTimeUpdateService networkTimeUpdater = null;  
  42.     CommonTimeManagementService commonTimeMgmtService = null;  
  43.     InputManagerService inputManager = null;  
  44.     TelephonyRegistry telephonyRegistry = null;  
  45.     ConsumerIrService consumerIr = null;  
  46.     ...此處省略  
  47.     Slog.i(TAG, "Alarm Manager");  
  48.     //這裏AlarmManager對應的Binder服務被創建  
  49.     alarm = new AlarmManagerService(context);  
  50.     //將AlarmManagerService加入ServiceManager中統一管理  
  51.     ServiceManager.addService(Context.ALARM_SERVICE, alarm);  
  52.   
  53.     Slog.i(TAG, "Init Watchdog");  
  54.     Watchdog.getInstance().init(context, battery, power, alarm,  
  55.             ActivityManagerService.self());  
  56.     Watchdog.getInstance().addThread(wmHandler, "WindowManager thread");  
  57.   
  58.     Slog.i(TAG, "Input Manager");  
  59.     inputManager = new InputManagerService(context, wmHandler);  
  60.   
  61.     Slog.i(TAG, "Window Manager");  
  62.     //這裏WindowManager所對應的Binder服務被創建  
  63.     wm = WindowManagerService.main(context, power, display, inputManager,  
  64.             wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,  
  65.             !firstBoot, onlyCore);  
  66.     //將WindowManagerService加入ServiceManager中統一管理  
  67.     ServiceManager.addService(Context.WINDOW_SERVICE, wm);  
  68.     ServiceManager.addService(Context.INPUT_SERVICE, inputManager);  
  69.   
  70.     ActivityManagerService.self().setWindowManager(wm);  
  71.     ...此處省略  
  72. }  

說明:針對上述代碼,我要說明一下,首先其創建的各種Binder服務其實並不是真正的服務,說它們是Binder比較恰當,因為它們的確繼承自Binder而不是Service;另一點就是ServiceManager其實也僅僅是個殼子,真正的工作是通過其Binder服務ServiceManagerNative來完成的,ServiceManager提供的工廠方法addService和getService均在ServiceManagerNative中通過代理來實現。

到此為止,我們已經知道各種Binder服務的創建過程,下麵我們要看一下Manager是如何和其Binder服務關聯上的,再回到getSystemService方法。首先我們要知道Activity的繼承關係,如下圖所示:


 再看如下代碼,觀察下它們中的getSystemService方法是如何實現的

code:各種getSystemService方法

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //#Context  
  2. public abstract Object getSystemService(String name);  
  3.   
  4. //#ContextWrapper  
  5. @Override  
  6. public Object getSystemService(String name) {  
  7.     return mBase.getSystemService(name);  
  8. }  
  9.   
  10. //#ContextThemeWrapper    
  11. @Override   
  12. public Object getSystemService(String name) {  
  13.     if (LAYOUT_INFLATER_SERVICE.equals(name)) {  
  14.         if (mInflater == null) {  
  15.             mInflater = LayoutInflater.from(mBase).cloneInContext(this);  
  16.         }  
  17.         return mInflater;  
  18.     }  
  19.     return mBase.getSystemService(name);  
  20. }  
  21.   
  22. //#Activity  
  23. @Override  
  24. public Object getSystemService(String name) {  
  25.     if (getBaseContext() == null) {  
  26.         throw new IllegalStateException(  
  27.                 "System services not available to Activities before onCreate()");  
  28.     }  
  29.   
  30.     if (WINDOW_SERVICE.equals(name)) {  
  31.         return mWindowManager;  
  32.     } else if (SEARCH_SERVICE.equals(name)) {  
  33.         ensureSearchManager();  
  34.         return mSearchManager;  
  35.     }  
  36.     return super.getSystemService(name);  
  37. }  

說明:通過上述代碼可以看出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的賦值過程

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  2.     ...  
  3.     if (activity != null) {  
  4.         //這裏的appContext就是ContextImpl對象  
  5.         Context appContext = createBaseContextForActivity(r, activity);  
  6.           
  7.         CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());  
  8.         Configuration config = new Configuration(mCompatConfiguration);  
  9.         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "  
  10.                 + r.activityInfo.name + " with config " + config);  
  11.         //通過Activity的attach方法將ContextImpl對象賦值給mBase  
  12.         activity.attach(appContext, this, getInstrumentation(), r.token,  
  13.                 r.ident, app, r.intent, r.activityInfo, title, r.parent,  
  14.                 r.embeddedID, r.lastNonConfigurationInstances, config);  
  15.         ...  
  16.     }  
  17.     ...  
  18. }  
  19.   
  20. private Context createBaseContextForActivity(ActivityClientRecord r,  
  21.         final Activity activity) {  
  22.     //很顯然,此方法返回的就是ContextImpl對象  
  23.     ContextImpl appContext = new ContextImpl();  
  24.     appContext.init(r.packageInfo, r.token, this);  
  25.     appContext.setOuterContext(activity);  
  26.     Context baseContext = appContext;  
  27.     ...  
  28.     return baseContext;  
  29. }  
  30.   
  31. final void attach(Context context, ActivityThread aThread,  
  32.         Instrumentation instr, IBinder token, int ident,  
  33.         Application application, Intent intent, ActivityInfo info,  
  34.         CharSequence title, Activity parent, String id,  
  35.         NonConfigurationInstances lastNonConfigurationInstances,  
  36.         Configuration config) {  
  37.     //將context賦值給mBase,這裏的context就是performLaunchActivity中的appContext,即ContextImpl對象  
  38.     attachBaseContext(context);  
  39.   
  40.     mFragments.attachActivity(this, mContainer, null);  
  41.       
  42.     mWindow = PolicyManager.makeNewWindow(this);  
  43.     mWindow.setCallback(this);  
  44.     ...  
  45. }  
  46.   
  47. @Override protected void attachBaseContext(Context newBase) {  
  48.     super.attachBaseContext(newBase);  
  49.     //這裏很顯然,對mBase進行賦值  
  50.     mBase = newBase;  
  51. }  

說明:看了上麵的代碼,我們已經知道,mBase的確是ContextImpl對象。上麵我提到:除了LayoutInflater、WindowManager以及SearchManager,剩下的所有Manager將通過mBase.getSystemService(name)返回,那麼現在,我們去看下ContextImpl中的getSystemService方法。

code:ContextImpl#getSystemService

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class ContextImpl extends Context {  
  2.     ...  
  3.     @Override  
  4.     public Object getSystemService(String name) {  
  5.         //首先從SYSTEM_SERVICE_MAP根據服務名得到一個fetcher對象  
  6.         //其中SYSTEM_SERVICE_MAP是一個HashMap,然後再通過fetcher去取service  
  7.         ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);  
  8.         return fetcher == null ? null : fetcher.getService(this);  
  9.     }  
  10.     ...  
  11. }  

說明:看了ContextImpl的getSystemService方法,發現失望了,還沒有找到真正的實現,看來還要去看這個fetcher是怎麼回事,下麵請看代碼:

code:服務注冊過程和fetcher

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //一個哈希表,用來根據服務名存儲對應服務的ServiceFetcher(可以理解為通過ServiceFetcher可以得到服務)  
  2. private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =  
  3.         new HashMap<String, ServiceFetcher>();  
  4.   
  5. //注冊服務,將服務的fetcher存到哈希表中  
  6. private static void registerService(String serviceName, ServiceFetcher fetcher) {  
  7.     if (!(fetcher instanceof StaticServiceFetcher)) {  
  8.         fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;  
  9.     }  
  10.     SYSTEM_SERVICE_MAP.put(serviceName, fetcher);  
  11. }  
  12. //靜態代碼塊,注冊各種服務  
  13. //也就是說,ContextImpl這個類被加載的時候就會把如下的各種服務的fetcher加入到哈希表中  
  14. //這樣我們通過getSystemService就可以得到一個服務的fetcher,再通過fetcher去得到服務的對象  
  15. static {  
  16.     registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {  
  17.             public Object getService(ContextImpl ctx) {  
  18.                 return AccessibilityManager.getInstance(ctx);  
  19.             }});  
  20.   
  21.     registerService(CAPTIONING_SERVICE, new ServiceFetcher() {  
  22.             public Object getService(ContextImpl ctx) {  
  23.                 return new CaptioningManager(ctx);  
  24.             }});  
  25.   
  26.     registerService(ACCOUNT_SERVICE, new ServiceFetcher() {  
  27.             public Object createService(ContextImpl ctx) {  
  28.                 IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);  
  29.                 IAccountManager service = IAccountManager.Stub.asInterface(b);  
  30.                 return new AccountManager(ctx, service);  
  31.             }});  
  32.   
  33.     registerService(ACTIVITY_SERVICE, new ServiceFetcher() {  
  34.             public Object createService(ContextImpl ctx) {  
  35.                 return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());  
  36.             }});  
  37.   
  38.     //這裏是Alarm服務的注冊  
  39.     registerService(ALARM_SERVICE, new ServiceFetcher() {  
  40.             public Object createService(ContextImpl ctx) {  
  41.                 /**還記得ALARM_SERVICE嗎? 
  42.                  * alarm = new AlarmManagerService(context); 
  43.                  * 將AlarmManagerService加入ServiceManager中統一管理 
  44.                  * ServiceManager.addService(Context.ALARM_SERVICE, alarm); 
  45.                  */  
  46.                 //通過ServiceManager的getService得到Alarm服務,很顯然,下麵的b就是AlarmManagerService對象  
  47.                 IBinder b = ServiceManager.getService(ALARM_SERVICE);  
  48.                 //還記得AlarmManager中的mService嗎?就是這裏的service,很顯然它是一個Binder服務  
  49.                 //分析到這裏,事實已經得出:AlarmManager所對應的Binder服務就是AlarmManagerService  
  50.                 IAlarmManager service = IAlarmManager.Stub.asInterface(b);  
  51.                 return new AlarmManager(service, ctx);  
  52.             }});  
  53.   
  54.     registerService(AUDIO_SERVICE, new ServiceFetcher() {  
  55.             public Object createService(ContextImpl ctx) {  
  56.                 return new AudioManager(ctx);  
  57.             }});  
  58.     ...省略:下麵還有許多服務  
  59. }  
說明:通過上述代碼的分析,相信大家已經很明確Manager是如何和Binder服務一一對應的,然後Manager的各種功能將會交由Binder服務來完成。盡管我隻詳細分析了AlarmManager和AlarmManagerService的對應過程,但是其它Manager的對應過程是幾乎完全一樣的。好了,到了這裏,我們已經把Manager和Binder服務的對應過程進行了深入地分析,下麵開始我們的最後一個主題:Alarm機製的源碼分析。

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

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval,  
  2.         PendingIntent operation, boolean isStandalone, boolean doValidate,  
  3.         WorkSource workSource) {  
  4.     /**創建一個alarm,其中各參數的含義如下: 
  5.      * type 鬧鍾類型 ELAPSED_REALTIME、RTC、RTC_WAKEUP等 
  6.      * when 觸發時間 UTC類型,絕對時間,通過System.currentTimeMillis()得到 
  7.      * whenElapsed 相對觸發時間,自開機算起,含休眠,通過SystemClock.elapsedRealtime()得到 
  8.      * maxWhen 最大觸發時間 
  9.      * interval 觸發間隔,針對循環鬧鍾有效 
  10.      * operation 鬧鍾觸發時的行為,PendingIntent類型 
  11.      */  
  12.     Alarm a = new Alarm(type, when, whenElapsed, maxWhen, interval, operation, workSource);  
  13.     //根據PendingIntent刪除之前已有的同一個鬧鍾  
  14.     removeLocked(operation);  
  15.   
  16.     boolean reschedule;  
  17.     //嚐試將alarm加入到合適的batch中,如果alarm是獨立的或者無法找到合適的batch去容納此alarm,返回-1  
  18.     int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);  
  19.     if (whichBatch < 0) {  
  20.         //沒有合適的batch去容納alarm,則新建一個batch  
  21.         Batch batch = new Batch(a);  
  22.         batch.standalone = isStandalone;  
  23.         //將batch加入mAlarmBatches中,並對mAlarmBatches進行排序:按開始時間升序排列  
  24.         reschedule = addBatchLocked(mAlarmBatches, batch);  
  25.     } else {  
  26.         //如果找到合適了batch去容納此alarm,則將其加入到batch中  
  27.         Batch batch = mAlarmBatches.get(whichBatch);  
  28.         //如果當前alarm的加入引起了batch開始時間和結束時間的改變,則reschedule為true  
  29.         reschedule = batch.add(a);  
  30.         if (reschedule) {  
  31.             //由於batch的起始時間發生了改變,所以需要從列表中刪除此batch並重新加入、重新對batch列表進行排序  
  32.             mAlarmBatches.remove(whichBatch);  
  33.             addBatchLocked(mAlarmBatches, batch);  
  34.         }  
  35.     }  
  36.   
  37.     if (DEBUG_VALIDATE) {  
  38.         if (doValidate && !validateConsistencyLocked()) {  
  39.             Slog.v(TAG, "Tipping-point operation: type=" + type + " when=" + when  
  40.                     + " when(hex)=" + Long.toHexString(when)  
  41.                     + " whenElapsed=" + whenElapsed + " maxWhen=" + maxWhen  
  42.                     + " interval=" + interval + " op=" + operation  
  43.                     + " standalone=" + isStandalone);  
  44.             rebatchAllAlarmsLocked(false);  
  45.             reschedule = true;  
  46.         }  
  47.     }  
  48.   
  49.     if (reschedule) {  
  50.         rescheduleKernelAlarmsLocked();  
  51.     }  
  52. }  

說明:通過上述代碼可以看出,當我們創建一個alarm的時候,僅僅是將這個alarm加入到某個batch中,係統中有一個batch列表,專門用於存儲所有的alarm。可是僅僅把alarm加入到batch中還不行,係統還必須提供一個類似於Looper的東西一直去遍曆這個列表,一旦它發現有些alarm的時間已經到達就要把它取出來去執行。事實上,AlarmManagerService中的確有一個類似於Looper的東西去幹這個事情,隻不過它是個線程,叫做AlarmThread。下麵看它的代碼:

code:AlarmManagerService#AlarmThread

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. private class AlarmThread extends Thread  
  2. {  
  3.     public AlarmThread()  
  4.     {  
  5.         super("AlarmManager");  
  6.     }  
  7.       
  8.     public void run()  
  9.     {  
  10.         //當前時間觸發的alarm列表  
  11.         ArrayList<Alarm> triggerList = new ArrayList<Alarm>();  
  12.   
  13.         while (true)  
  14.         {  
  15.             //jni方法,顧名思義,阻塞式方法,當有alarm的時候會被喚醒  
  16.             int result = waitForAlarm(mDescriptor);  
  17.   
  18.             triggerList.clear();  
  19.   
  20.             if ((result & TIME_CHANGED_MASK) != 0) {  
  21.                 if (DEBUG_BATCH) {  
  22.                     Slog.v(TAG, "Time changed notification from kernel; rebatching");  
  23.                 }  
  24.                 remove(mTimeTickSender);  
  25.                 //將所有的alarm重新排序  
  26.                 rebatchAllAlarms();  
  27.                 mClockReceiver.scheduleTimeTickEvent();  
  28.                 Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);  
  29.                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING  
  30.                         | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  
  31.                 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);  
  32.             }  
  33.               
  34.             synchronized (mLock) {  
  35.                 final long nowRTC = System.currentTimeMillis();  
  36.                 final long nowELAPSED = SystemClock.elapsedRealtime();  
  37.                 if (localLOGV) Slog.v(  
  38.                     TAG, "Checking for alarms... rtc=" + nowRTC  
  39.                     + ", elapsed=" + nowELAPSED);  最後更新:2017-04-03 12:55:33

      上一篇:go 使用反射獲取字節碼文件中的方法
      下一篇:go Winform Panel按鈕位置