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

Alarm是android提供的用于完成闹钟式定时任务的类,系统通过AlarmManager来管理所有的Alarm,Alarm支持一次性定时任务和循环定时任务,它的使用方式很简单,这里不多做介绍,只给出一个简单的示例:

[java]  view plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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 plain copy 在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);  
  40.   
  41.                 if (WAKEUP_STATS) {  
  42.                     if ((result & IS_WAKEUP_MASK) != 0) {  
  43.                         long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;  
  44.                         int n = 0;  
  45.                         for (WakeupEvent event : mRecentWakeups) {  
  46.                             if (event.when > newEarliest) break;  
  47.                             n++; // number of now-stale entries at the list head  
  48.                         }  
  49.                         for (int i = 0; i < n; i++) {  
  50.                             mRecentWakeups.remove();  
  51.                         }  
  52.   
  53.                         recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);  
  54.                     }  
  55.                 }  
  56.                 //这个方法会把batch列表中的第一个batch取出来然后加到触发列表中  
  57.                 //当然,前提是此batch的开始时间不大于当前时间  
  58.                 //同时,如果是循环闹钟,则会对下次任务进行再次定时  
  59.                 triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);  
  60.                 rescheduleKernelAlarmsLocked();  
  61.   
  62.                 // 遍历触发列表,发送PendingIntent  
  63.                 for (int i=0; i<triggerList.size(); i++) {  
  64.                     Alarm alarm = triggerList.get(i);  
  65.                     try {  
  66.                         if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);  
  67.                         //这里PendingIntent会被send,结果就是我们的定时任务被执行了  
  68.                         alarm.operation.send(mContext, 0,  
  69.                                 mBackgroundIntent.putExtra(  
  70.                                         Intent.EXTRA_ALARM_COUNT, alarm.count),  
  71.                                 mResultReceiver, mHandler);  
  72.                           
  73.                         // we have an active broadcast so stay awake.  
  74.                         if (mBroadcastRefCount == 0) {  
  75.                             setWakelockWorkSource(alarm.operation, alarm.workSource);  
  76.                             mWakeLock.acquire();  
  77.                         }  
  78.                         final InFlight inflight = new InFlight(AlarmManagerService.this,  
  79.                                 alarm.operation, alarm.workSource);  
  80.                         mInFlight.add(inflight);  
  81.                         mBroadcastRefCount++;  
  82.   
  83.                         final BroadcastStats bs = inflight.mBroadcastStats;  
  84.                         bs.count++;  
  85.                         if (bs.nesting == 0) {  
  86.                             bs.nesting = 1;  
  87.                             bs.startTime = nowELAPSED;  
  88.                         } else {  
  89.                             bs.nesting++;  
  90.                         }  
  91.                         final FilterStats fs = inflight.mFilterStats;  
  92.                         fs.count++;  
  93.                         if (fs.nesting == 0) {  
  94.                             fs.nesting = 1;  
  95.                             fs.startTime = nowELAPSED;  
  96.                         } else {  
  97.                             fs.nesting++;  
  98.                         }  
  99.                         if (alarm.type == ELAPSED_REALTIME_WAKEUP  
  100.                                 || alarm.type == RTC_WAKEUP) {  
  101.                             bs.numWakeup++;  
  102.                             fs.numWakeup++;  
  103.                             //针对能唤醒设备的闹钟,这里会做一些唤醒设备的事情  
  104.                             ActivityManagerNative.noteWakeupAlarm(  
  105.                                     alarm.operation);  
  106.                         }  
  107.                     } catch (PendingIntent.CanceledException e) {  
  108.                         if (alarm.repeatInterval > 0) {  
  109.                             // This IntentSender is no longer valid, but this  
  110.                             // is a repeating alarm, so toss the hoser.  
  111.                             remove(alarm.operation);  
  112.                         }  
  113.                     } catch (RuntimeException e) {  
  114.                         Slog.w(TAG, "Failure sending alarm.", e);  
  115.                     }  
  116.                 }  
  117.             }  
  118.         }  
  119.     }  
  120. }  
说明:上述代码中,AlarmThread会一直循环的跑着,一旦有新的alarm触发,它就会取出一个batch然后逐个发送PendingIntent,具体alarm的触发是由底层来完成的,我没法再继续分析下去。还有就是Alarm中有一些细节,我没有进行很具体的分析,实际上很简单,大家一看就懂。到此为止,Alarm机制的主要流程也分析完了。

总结

本文没有详细介绍如何使用Alarm,因为很简单,看一下官方文档或者网上搜一下,到处都是。关于Alarm,有一点需要强调一下:当手机重启或者应用被杀死的时候,Alarm会被删除,因此,如果想通过Alarm来完成长久定时任务是不可靠的,如果非要完成长久定时任务,可以这样:将应用的所有Alarm信息存到数据库中,每次应用启动的时候都重新注册Alarm并更新Alarm的触发时间,通过这种方式就不存在Alarm丢失的情况了。本文很长,耗时8个小时才完成的,感谢大家阅读本文,希望本文能给大家带来一点帮助。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值