Android源码分析-Alarm机制与Binder的交互
转自http://www.2cto.com/kf/201401/273797.html
前言
本次给大家分析的是Android中Alarm的机制以及它和Binder的交互,所用源码为最新的Android4.4。因为Alarm的功能都是通过Binder来完成的,所以,介绍Alarm之前必须要先介绍下它是如何调用Binder来完成定时功能的。由于内容较多,本文会比较长,在文章结构安排上是这样的:首先简单介绍如何使用Alarm并给出其工作原理,接着分析Alarm和Timer以及Handler在完成定时任务上的差别,然后分析Alarm与Binder的交互,最后分析Alarm机制的源码。
什么是Alarm
Alarm是android提供的用于完成闹钟式定时任务的类,系统通过AlarmManager来管理所有的Alarm,Alarm支持一次性定时任务和循环定时任务,它的使用方式很简单,这里不多做介绍,只给出一个简单的示例:
1 2 3 4 5 6 7 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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是按照如下这种方式声明的:
1 2 3 4 5 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 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方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | //#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的赋值过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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
1 2 3 4 5 6 7 8 9 10 11 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | //一个哈希表,用来根据服务名存储对应服务的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); }}); ...省略:下面还有许多服务 }</string,></string,> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | private class AlarmThread extends Thread { public AlarmThread() { super (AlarmManager); } public void run() { //当前时间触发的alarm列表 ArrayList triggerList = new ArrayList(); 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(); active= "" alarm= "" alarm.repeatinterval= "" alarm.type= "=" an= "" awake.= "" broadcast= "" broadcaststats= "" bs= "inflight.mBroadcastStats;" bs.nesting= "1;" bs.starttime= "nowELAPSED;" catch = "" elapsed_realtime_wakeup= "" else = "" filterstats= "" final = "" fs= "inflight.mFilterStats;" fs.nesting= "1;" fs.starttime= "nowELAPSED;" have= "" if = "" inflight= "" mbroadcastrefcount= "=" pendingintent.canceledexception= "" sending= "" so= "" stay= "" try = "" we= "" > 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); } } } } } }</triggerlist.size();></alarm></alarm> |
总结
本文没有详细介绍如何使用Alarm,因为很简单,看一下官方文档或者网上搜一下,到处都是。关于Alarm,有一点需要强调一下:当手机重启或者应用被杀死的时候,Alarm会被删除,因此,如果想通过Alarm来完成长久定时任务是不可靠的,如果非要完成长久定时任务,可以这样:将应用的所有Alarm信息存到数据库中,每次应用启动的时候都重新注册Alarm并更新Alarm的触发时间,通过这种方式就不存在Alarm丢失的情况了。本文很长,耗时8个小时才完成的,感谢大家阅读本文,希望本文能给大家带来一点帮助。