[toc]
基于 Android 7.1.1 源码,分析 AlarmManagerService 的机制
0 综述 下面是设置精确 alarm 的方法:
1 2 3 4 5 6 7 8 9 10 11 12 public static void setGlobalNoticeDialogForceShowAlarm (Context context) { Intent intent = new Intent("com.coolqi.alarm_start" ); PendingIntent pi = PendingIntent.getBroadcast(context, 0 , intent, 0 ); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarmManager.cancel(pi); int timeHour = 2 *24 *60 *60 ; OppoLog.d(TAG, "show global time: " + timeHour); long triggerTime = CommonUtil.getTriggerTime(System.currentTimeMillis(), timeHour); alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pi); }
1 AlarmManager.setAlarm AlarmManagerSerivce 提供了很丰富的接口来设置不同类型的 alarm,可以通过 AlarmManager.java 来看到所有的接口:
1.1 AlarmManager.set set 接口用于设置一个一次性的闹钟,该闹钟是非精确的,我们需要传入一个 triggerAtMillis 毫秒值被表示闹钟触发的时间点!
1 2 3 4 public void set (@AlarmType int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, legacyExactLength(), 0 , 0 , operation, null , null , null , null , null ); }
这个 set 方法最常用:第一个参数是 alarm 的类型,第二个是触发时间,第三个是 PendingIntent,用于启动 Service,activity,或者是 broadcastReceiver!
可以实现跨进程,即:设置该 alarm 的进程和处理 alarm 触发的进程可以不是同一个!
参数传递 :
int type :闹钟类型 type;
long triggerAtMillis :触发时间,单位毫秒;
long windowMillis :legacyExactLength(),API 19 以后返回值为 WINDOW_HEURISTIC,即 -1;
long intervalMillis :0;
int flags :0;
PendingIntent operation :operation;
final OnAlarmListener listener :null;
String listenerTag :null;
Handler targetHandler :null;
WorkSource workSource :null;
AlarmClockInfo alarmCloc :null;
1 2 3 4 5 public void set (@AlarmType int type, long triggerAtMillis, String tag, OnAlarmListener listener, Handler targetHandler) { setImpl(type, triggerAtMillis, legacyExactLength(), 0 , 0 , null , listener, tag, targetHandler, null , null ); }
这个 set 方法并不适用于跨进程通信,其需要传入一个实现了 OnAlarmListener 接口的对象用于监听 alarm 的触发,当 alarm 触发后,OnAlarmListener 的会被 onAlarm() 执行!
targetHandler 表示的是 OnAlarmListener.onAlarm 执行时,目标线程的 handler 对象!
1 2 3 4 5 6 7 8 @SystemApi @RequiresPermission (android.Manifest.permission.UPDATE_DEVICE_STATS)public void set (@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, PendingIntent operation, WorkSource workSource) { setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0 , operation, null , null , null , workSource, null ); }
1 2 3 4 5 6 public void set (@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, String tag, OnAlarmListener listener, Handler targetHandler, WorkSource workSource) { setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0 , null , listener, tag, targetHandler, workSource, null ); }
1 2 3 4 5 6 7 8 @SystemApi @RequiresPermission (android.Manifest.permission.UPDATE_DEVICE_STATS)public void set (@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, OnAlarmListener listener, Handler targetHandler, WorkSource workSource) { setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0 , null , listener, null , targetHandler, workSource, null ); }
set 方法设置的 alarm 是非精确的 !
1.2 AlarmManager.setExact 用于设置一个精确的闹钟,该方法是相对于 set 方法的,参数和 set 方法一样,不多说,也有 2 个方法!
1 2 3 4 5 6 7 8 9 10 public void setExact (@AlarmType int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0 , 0 , operation, null , null , null , null , null ); } public void setExact (@AlarmType int type, long triggerAtMillis, String tag, OnAlarmListener listener, Handler targetHandler) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0 , 0 , null , listener, tag, targetHandler, null , null ); }
不多说了!!
1.3 AlarmManager.setWindow 用于设置一个在给定的时间窗触发的闹钟。该方法允许应用程序精确地控制操作系统调整闹钟触发时间的程度。
其中,windowStartMillis 表示时间窗的起始时间!windowStartMillis 表示时间窗的长度,其他参数和 set 方法一样!
1 2 3 4 5 6 7 8 9 10 11 public void setWindow (@AlarmType int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation) { setImpl(type, windowStartMillis, windowLengthMillis, 0 , 0 , operation, null , null , null , null , null ); } public void setWindow (@AlarmType int type, long windowStartMillis, long windowLengthMillis, String tag, OnAlarmListener listener, Handler targetHandler) { setImpl(type, windowStartMillis, windowLengthMillis, 0 , 0 , null , listener, tag, targetHandler, null , null ); }
setWindow 设置的是非精确 的闹钟!
1.4 AlarmManager.setXXXRepeating setRepeating 和 setInexactRepeating 用于设置一个可重复触发的闹钟,但是二者却有着不同:
1 2 3 4 5 public void setRepeating (@AlarmType int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0 , operation, null , null , null , null , null ); }
setRepeating 方法在 API19 以前是精确的,其时间间隔是固定的,但是在 API19 以后则是非精确闹钟 ,其等价于 setInexactRepeating 方法!
1 2 3 4 5 public void setInexactRepeating (@AlarmType int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0 , operation, null , null , null , null , null ); }
setInexactRepeating 则是非精确闹钟 ,间隔时间不固定!
1.5 AlarmManager.setAlarmClock setAlarmClock 方法用于通过 AlarmClockInfo 来设置一个精确闹钟 !
1 2 3 4 public void setAlarmClock (AlarmClockInfo info, PendingIntent operation) { setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0 , 0 , operation, null , null , null , null , info); }
1.6 AlarmManager.setIdleUntil setIdleUntil 方法会将 alarm manager service 置为 idle 状态,并设置一个精确闹钟,当该闹钟触发后 alarm manager service 才会退出 idle 状态!
1 2 3 4 5 public void setIdleUntil (@AlarmType int type, long triggerAtMillis, String tag, OnAlarmListener listener, Handler targetHandler) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0 , FLAG_IDLE_UNTIL, null , listener, tag, targetHandler, null , null ); }
可以看到,其调用 setImpl 方法的时候,传入了一个 flag,表示要将 AlarmManagerService 置为 idle 状态!
1 public static final int FLAG_IDLE_UNTIL = 1 <<4 ;
该方法只能是系统调用!
1.7 AlarmManager.setXXXAndAllowWhileIdle setAndAllowWhileIdle 和 setExactAndAllowWhileIdle 方法用于设置在设备处于 idle 状态下,仍然能够触发的 alarm,但是二者有不同之处:
1 2 3 4 5 public void setAndAllowWhileIdle (@AlarmType int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0 , FLAG_ALLOW_WHILE_IDLE, operation, null , null , null , null , null ); }
setAndAllowWhileIdle 方法设置的是非精确闹钟 !1 2 3 4 5 public void setExactAndAllowWhileIdle (@AlarmType int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0 , FLAG_ALLOW_WHILE_IDLE, operation, null , null , null , null , null ); }
setExactAndAllowWhileIdle 方法设置的是精确闹钟 !
当 setXXXAndAllowWhileIdle 设置闹钟时候,会传入一个 flags,表示该闹钟在设别处于 idle 状态时依然可以触发!1 public static final int FLAG_ALLOW_WHILE_IDLE = 1 <<2 ;
2 AlarmManager.setImpl 可以看到,最后都会调用 setImpl 接口:
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 private void setImpl (@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener, String listenerTag, Handler targetHandler, WorkSource workSource, AlarmClockInfo alarmClock) { if (triggerAtMillis < 0 ) { triggerAtMillis = 0 ; } ListenerWrapper recipientWrapper = null ; if (listener != null ) { synchronized (AlarmManager.class) { if (sWrappers == null ) { sWrappers = new ArrayMap<OnAlarmListener, ListenerWrapper>(); } recipientWrapper = sWrappers.get(listener); if (recipientWrapper == null ) { recipientWrapper = new ListenerWrapper(listener); sWrappers.put(listener, recipientWrapper); } } final Handler handler = (targetHandler != null ) ? targetHandler : mMainThreadHandler; recipientWrapper.setHandler(handler); } try { mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, recipientWrapper, listenerTag, workSource, alarmClock); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }
2.1 new ListenerWrapper 如果我们指定了 OnAlarmListener,那么就会创建对应的 ListenerWrapper 实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 final class ListenerWrapper extends IAlarmListener .Stub implements Runnable { final OnAlarmListener mListener; Handler mHandler; IAlarmCompleteListener mCompletion; public ListenerWrapper (OnAlarmListener listener) { mListener = listener; } public void setHandler (Handler h) { mHandler = h; } ... ... ... }
其实 ListenerWrapper 是一个 Runnable 对象,其构造函数和 setHandler 都很简单,这里就不说了!
2.1.1 ListenerWrapper.doAlarm 这里先简单说一下,当闹钟触发后,会回调其 doAlarm 方法:1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public void doAlarm (IAlarmCompleteListener alarmManager) { mCompletion = alarmManager; synchronized (AlarmManager.class) { if (sWrappers != null ) { sWrappers.remove(mListener); } } mHandler.post(this ); }
这里的 IAlarmCompleteListener 是一个接口,支持 Binder 通信,当闹钟触发后,AlarmManagerService 会传递一个实现了 IAlarmCompleteListener 接口的对象给当前进程
然后会调用自身的 run 方法,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public void run () { try { mListener.onAlarm(); } finally { try { mCompletion.alarmComplete(this ); } catch (Exception e) { Log.e(TAG, "Unable to report completion to Alarm Manager!" , e); } } }
逻辑很简单,不多说了!
3 AlarmManagerService 我们知道 set 方法最后调用了:1 2 mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, recipientWrapper, listenerTag, workSource, alarmClock);
这个 mService 是服务端的 proxy 对象!AlarmManager 框架实现了 Aidl 模板,实现跨进程通讯:
1 2 3 4 5 6 7 8 9 10 11 interface IAlarmManager { void set (String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, in PendingIntent operation, in IAlarmListener listener, String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock) ; boolean setTime (long millis) ; void setTimeZone (String zone) ; void remove (in PendingIntent operation, in IAlarmListener listener) ; long getNextWakeFromIdleTime () ; AlarmManager.AlarmClockInfo getNextAlarmClock (int userId) ; }
AlarmManagerService 内部有一个 IBinder 对象,是 IAlarmManager.Stub 的实现对象,作为服务端的 “桩”:
3.1 AlarmMS.mService.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 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 private final IBinder mService = new IAlarmManager.Stub() { @Override public void set (String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { final int callingUid = Binder.getCallingUid(); mAppOps.checkPackage(callingUid, callingPackage); if (interval != 0 ) { if (directReceiver != null ) { throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers" ); } } if (workSource != null ) { getContext().enforcePermission( android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), callingUid, "AlarmManager.set" ); } flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); if (callingUid != Process.SYSTEM_UID) { flags &= ~AlarmManager.FLAG_IDLE_UNTIL; } if (windowLength == AlarmManager.WINDOW_EXACT) { flags |= AlarmManager.FLAG_STANDALONE; } if (alarmClock != null ) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID || Arrays.binarySearch(mDeviceIdleUserWhitelist, UserHandle.getAppId(callingUid)) >= 0 )) { flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE; } setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver, listenerTag, flags, workSource, alarmClock, callingUid, callingPackage); } ... ... ... ... }
标志位 :
AlarmManager.FLAG_WAKE_FROM_IDLE:
如果设备处于 idle 状态,该类型的 alarm 会将设别唤醒!
AlarmClock 默认就是 FLAG_WAKE_FROM_IDLE 类型的!
AlarmManager.FLAG_IDLE_UNTIL:
doze 模式的闹钟,只能由系统通过 setIdleUtil 来设置,这个方法会使得系统进入 idle 状态,直到这个 alarm 触发;如果系统中已经有 FLAG_WAKE_FROM_IDLE 类型的 alarm,那么 FLAG_IDLE_UNTIL 类型的 alarm 的触发事件会提前!
AlarmManager.FLAG_STANDALONE:
精确闹钟,如果设置 alarm 时,指定了闹钟为精确闹钟:WINDOW_EXACT,那么该 flags 会被设置 FLAG_STANDALONE 标志位
AlarmManager.FLAG_ALLOW_WHILE_IDLE:
即使设备处于 idle 状态,该 alarm 也能触发,通过 setAndAllowWhileIdle 和 setExactAndAllowWhileIdle 设置!
AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
如果是系统应用, 或者调用者在 doze 模式的白名单中,那么会设置 FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED 标志位,而不是 FLAG_ALLOW_WHILE_IDLE 标志位!
方法流程总结 :
通过 appOps 校验,uid 和包名是否一致;
重复触发的 alarm 必须要使用 PendingIntent,不能使用 directReceiver!
如果 workSource 不为 null,调用者必须有 android.Manifest.permission.UPDATE_DEVICE_STATS 的权限!
非 system uid 的调用者,其不能调用 setIdleUntil 方法设置 FLAG_IDLE_UNTIL!
如果是精确闹钟,那就设置 AlarmManager.FLAG_STANDALONE 标志位!
如果是 alarmClock,那就设置 AlarmManager.FLAG_WAKE_FROM_IDLE 和 AlarmManager.FLAG_STANDALONE!
如果是系统应用, 或者调用者在 doze 模式的白名单中,那么会设置 FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED 标志位,而不是 FLAG_ALLOW_WHILE_IDLE 标志位!
最后,调用 setImpl 方法继续设置!
可以看到,我们在 AlarmManager 中调用的 set 接口,会调用该“桩”对象的 set 方法,桩对象的 set 最后会调用 AlarmManagerService 的 setImpl 方法!
3.2 AlarmMS.setImpl setImpl 方法中,首先会做一些参数校验!
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 76 77 78 79 80 81 void setImpl (int type, long triggerAtTime, long windowLength, long interval, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) { if ((operation == null && directReceiver == null ) || (operation != null && directReceiver != null )) { Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver" ); return ; } if (windowLength > AlarmManager.INTERVAL_HALF_DAY) { Slog.w(TAG, "Window length " + windowLength + "ms suspiciously long; limiting to 1 hour" ); windowLength = AlarmManager.INTERVAL_HOUR; } final long minInterval = mConstants.MIN_INTERVAL; if (interval > 0 && interval < minInterval) { Slog.w(TAG, "Suspiciously short interval " + interval + " millis; expanding to " + (minInterval/1000 ) + " seconds" ); interval = minInterval; } if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) { throw new IllegalArgumentException("Invalid alarm type " + type); } if (triggerAtTime < 0 ) { final long what = Binder.getCallingPid(); Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + callingUid + " pid=" + what); triggerAtTime = 0 ; } final long nowElapsed = SystemClock.elapsedRealtime(); final long nominalTrigger = convertToElapsed(triggerAtTime, type); final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY; final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger; final long maxElapsed; if (windowLength == AlarmManager.WINDOW_EXACT) { maxElapsed = triggerElapsed; } else if (windowLength < 0 ) { maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval); windowLength = maxElapsed - triggerElapsed; } else { maxElapsed = triggerElapsed + windowLength; } synchronized (mLock) { if (DEBUG_BATCH) { Slog.v(TAG, "set(" + operation + ") : type=" + type + " triggerAtTime=" + triggerAtTime + " win=" + windowLength + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed + " interval=" + interval + " flags=0x" + Integer.toHexString(flags)); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, directReceiver, listenerTag, flags, true , workSource, alarmClock, callingUid, callingPackage); } }
我们来看看这个过程:
3.2.1 AlarmMS.convertToElapsed 对于触发事件,要根据闹钟的类型,来修正:1 2 3 4 5 6 7 8 9 static long convertToElapsed (long when, int type) { final boolean isRtc = (type == RTC || type == RTC_WAKEUP); if (isRtc) { when -= System.currentTimeMillis() - SystemClock.elapsedRealtime(); } return when; }
计算结果: when = when - System.currentTimeMillis() + SystemClock.elapsedRealtime();
如果是 rtc 类型,那就将其转为了相对于开机的时间!
3.2.2 AlarmMS.maxTriggerTime 接着是计算最大的触发时间,这里的 MIN_FUZZABLE_INTERVAL 表示的是最小的时间窗间隔!
1 2 static final long MIN_FUZZABLE_INTERVAL = 10000 ;
继续来看:1 2 3 4 5 6 7 8 9 10 11 12 13 static long maxTriggerTime (long now, long triggerAtTime, long interval) { long futurity = (interval == 0 ) ? (triggerAtTime - now) : interval; if (futurity < MIN_FUZZABLE_INTERVAL) { futurity = 0 ; } return triggerAtTime + (long )(.75 * futurity); }
对于非精确的 alarm,这里通过 maxTriggerTime 方法计算其批处理的时间窗为:1 0.75 * (interval or triggerAtTime - now)
如果计算出的时间窗小于 10 s,那么就不设置最晚执行时间!
3.3 AlarmMS.setImplLocked[15] 参数传递 :
int type :闹钟的类型
long when : 触发时间点;
long whenElapsed :触发时间点,相对于开机时间;
long windowLength :时间窗;
long maxWhen :最晚触发的时间点,触发时间点,相对于开机时间!
long interval :重复触发的时间间隔;
PendingIntent operation :这个很简单,不多说!
IAlarmListener directReceiver :这个很简单,也不多说!
String listenerTag :AlarmListener 的字符串描述信息!
int flags :alarm 属性标志位!
boolean doValidate :传入 true!
WorkSource workSource :工作源对象!
AlarmManager.AlarmClockInfo alarmClock :通过 setAlarmClock 方法设置才不为 null;
int callingUid :调用者的 uid;
String callingPackage :调用者的 package name;
对于参数,就简单的介绍到这里!
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 private void setImplLocked (int type, long when, long whenElapsed, long windowLength, long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, int flags, boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) { Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval, operation, directReceiver, listenerTag, workSource, flags, alarmClock, callingUid, callingPackage); try { if (ActivityManagerNative.getDefault().getAppStartMode(callingUid, callingPackage) == ActivityManager.APP_START_MODE_DISABLED) { Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a + " -- package not allowed to start" ); return ; } } catch (RemoteException e) { } removeLocked(operation, directReceiver); setImplLocked(a, false , doValidate); }
3.3.1 AlarmMS.Alarm 下面是会创建一个 Alarm 对象,保存本次 set 的 alarm 的信息:
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 private static class Alarm { public final int type; public final long origWhen; public final boolean wakeup; public final PendingIntent operation; public final IAlarmListener listener; public final String listenerTag; public final String statsTag; public final WorkSource workSource; public final int flags; public final AlarmManager.AlarmClockInfo alarmClock; public final int uid; public final int creatorUid; public final String packageName; public int count; public long when; public long windowLength; public long whenElapsed; public long maxWhenElapsed; public long repeatInterval; public PriorityClass priorityClass; public Alarm (int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag, WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info, int _uid, String _pkgName) { type = _type; origWhen = _when; wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP || _type == AlarmManager.RTC_WAKEUP; when = _when; whenElapsed = _whenElapsed; windowLength = _windowLength; maxWhenElapsed = _maxWhen; repeatInterval = _interval; operation = _op; listener = _rec; listenerTag = _listenerTag; statsTag = makeTag(_op, _listenerTag, _type); workSource = _ws; flags = _flags; alarmClock = _info; uid = _uid; packageName = _pkgName; creatorUid = (operation != null ) ? operation.getCreatorUid() : uid; } public static String makeTag (PendingIntent pi, String tag, int type) { final String alarmString = type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP ? "*walarm*:" : "*alarm*:" ; return (pi != null ) ? pi.getTag(alarmString) : (alarmString + tag); } ... ... ... }
Alarm 有几个方法,我们来看下:
3.3.1.1 Alarm.make 1 2 3 4 5 6 public WakeupEvent makeWakeupEvent (long nowRTC) { return new WakeupEvent(nowRTC, creatorUid, (operation != null ) ? operation.getIntent().getAction() : ("<listener>:" + listenerTag)); }
这个方法是用来创建一个 wake up event!
3.3.1.2 Alarm.matches 1 2 3 4 5 6 7 8 9 10 11 12 public boolean matches (PendingIntent pi, IAlarmListener rec) { return (operation != null ) ? operation.equals(pi) : rec != null && listener.asBinder().equals(rec.asBinder()); } public boolean matches (String packageName) { return (operation != null ) ? packageName.equals(operation.getTargetPackage()) : packageName.equals(this .packageName); }
这个方法是用来匹配 Alarm 的,代码逻辑很简单,使用 packageName 或者 PendingIntent,AlarmListener 进行匹配!
我们继续来看:
3.3.2 AlarmMS.removeLocked removeLocked 方法用于移除一个已经存在的 alarm!
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 private void removeLocked (PendingIntent operation, IAlarmListener directReceiver) { boolean didRemove = false ; for (int i = mAlarmBatches.size() - 1 ; i >= 0 ; i--) { Batch b = mAlarmBatches.get(i); didRemove |= b.remove(operation, directReceiver); if (b.size() == 0 ) { mAlarmBatches.remove(i); } } for (int i = mPendingWhileIdleAlarms.size() - 1 ; i >= 0 ; i--) { if (mPendingWhileIdleAlarms.get(i).matches(operation, directReceiver)) { mPendingWhileIdleAlarms.remove(i); } } if (didRemove) { if (DEBUG_BATCH) { Slog.v(TAG, "remove(operation) changed bounds; rebatching" ); } boolean restorePending = false ; if (mPendingIdleUntil != null && mPendingIdleUntil.matches(operation, directReceiver)) { mPendingIdleUntil = null ; restorePending = true ; } if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) { mNextWakeFromIdle = null ; } rebatchAllAlarmsLocked(true ); if (restorePending) { restorePendingWhileIdleAlarmsLocked(); } updateNextAlarmClockLocked(); } }
这边是匹配到和本次设置的 alarm 相同的 alarm,然后做移除操作,然后恢复一些需要执行的 alarm!
3.3.2.1 AlarmMS.Batch.remove 从一个 Batch 中移除一个 alarm,Batch 中的非精确 alarm 是按照触发事件排序的,同时 Batch 也有一个 start 变量,表示批处理内部所有 alarm 的时间点!
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 boolean remove (final PendingIntent operation, final IAlarmListener listener) { if (operation == null && listener == null ) { if (localLOGV) { Slog.w(TAG, "requested remove() of null operation" , new RuntimeException("here" )); } return false ; } boolean didRemove = false ; long newStart = 0 ; long newEnd = Long.MAX_VALUE; int newFlags = 0 ; for (int i = 0 ; i < alarms.size(); ) { Alarm alarm = alarms.get(i); if (alarm.matches(operation, listener)) { alarms.remove(i); didRemove = true ; if (alarm.alarmClock != null ) { mNextAlarmClockMayChange = true ; } } else { if (alarm.whenElapsed > newStart) { newStart = alarm.whenElapsed; } if (alarm.maxWhenElapsed < newEnd) { newEnd = alarm.maxWhenElapsed; } newFlags |= alarm.flags; i++; } } if (didRemove) { start = newStart; end = newEnd; flags = newFlags; } return didRemove; }
我们可以看到:
我们可以看到,当我们从 Batch 中移除一个 Alarm 后,Batch 的 start 和 end 时间点发生了变化,这样会导致 Batch 在 mAlarmBatches 中的顺序发生变化!
3.3.2.2 AlarmMS.rebatchAllAlarmsLocked 该方法用于重新对所有的 alarm 进行 batch 批处理!
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 void rebatchAllAlarmsLocked (boolean doValidate) { ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); Alarm oldPendingIdleUntil = mPendingIdleUntil; final long nowElapsed = SystemClock.elapsedRealtime(); final int oldBatches = oldSet.size(); for (int batchNum = 0 ; batchNum < oldBatches; batchNum++) { Batch batch = oldSet.get(batchNum); final int N = batch.size(); for (int i = 0 ; i < N; i++) { reAddAlarmLocked(batch.get(i), nowElapsed, doValidate); } } if (oldPendingIdleUntil != null && oldPendingIdleUntil != mPendingIdleUntil) { Slog.wtf(TAG, "Rebatching: idle until changed from " + oldPendingIdleUntil + " to " + mPendingIdleUntil); if (mPendingIdleUntil == null ) { restorePendingWhileIdleAlarmsLocked(); } } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); }
3.3.2.2.1 AlarmManagerService.reAddAlarmLocked 该方法用于重新添加 alarm!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void reAddAlarmLocked (Alarm a, long nowElapsed, boolean doValidate) { a.when = a.origWhen; long whenElapsed = convertToElapsed(a.when, a.type); final long maxElapsed; if (a.windowLength == AlarmManager.WINDOW_EXACT) { maxElapsed = whenElapsed; } else { maxElapsed = (a.windowLength > 0 ) ? (whenElapsed + a.windowLength) : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval); } a.whenElapsed = whenElapsed; a.maxWhenElapsed = maxElapsed; setImplLocked(a, true , doValidate); }
可以看到这里调用了 setImplLocked 三参数方法,重新设置该 alarm!
在 setImplLocked 方法中,会重新为 Alarm 分配 Batch,并对 mAlarmBatches 中的所有 Batch 重新排序,后面我们能够看到对该方法的分析,在第 3.4 节!
3.3.2.3 AlarmMS.restorePendingWhileIdleAlarmsLocked 接着,当系统退出 idle 状态后,要恢复那些因为 idle 状态而等待触发的 alarm!
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 void restorePendingWhileIdleAlarmsLocked () { if (RECORD_DEVICE_IDLE_ALARMS) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = 0 ; ent.pkg = "FINISH IDLE" ; ent.elapsedRealtime = SystemClock.elapsedRealtime(); mAllowWhileIdleDispatches.add(ent); } if (mPendingWhileIdleAlarms.size() > 0 ) { ArrayList<Alarm> alarms = mPendingWhileIdleAlarms; mPendingWhileIdleAlarms = new ArrayList<>(); final long nowElapsed = SystemClock.elapsedRealtime(); for (int i=alarms.size() - 1 ; i >= 0 ; i--) { Alarm a = alarms.get(i); reAddAlarmLocked(a, nowElapsed, false ); } } mConstants.updateAllowWhileIdleMinTimeLocked(); rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); try { mTimeTickSender.send(); } catch (PendingIntent.CanceledException e) { } }
继续看!
3.4 AlarmMS.setImplLocked[3] 接下来,我们来看一个非常重要的方法 setImplLocked!,第二个参数 rebatching 表示是否是 rebatch,正常流程下,rebatching 是为 false;
但是,当我们 rebatch 或者 restore 其他 alarm 时,rebatching 传入的是 true!
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 private void setImplLocked (Alarm a, boolean rebatching, boolean doValidate) { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0 ) { if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) { a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; } final long nowElapsed = SystemClock.elapsedRealtime(); final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed); if (fuzz > 0 ) { if (mRandom == null ) { mRandom = new Random(); } final int delta = mRandom.nextInt(fuzz); a.whenElapsed -= delta; if (false ) { Slog.d(TAG, "Alarm when: " + a.whenElapsed); Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed)); Slog.d(TAG, "Applied fuzz: " + fuzz); Slog.d(TAG, "Final delta: " + delta); Slog.d(TAG, "Final when: " + a.whenElapsed); } a.when = a.maxWhenElapsed = a.whenElapsed; } } else if (mPendingIdleUntil != null ) { if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | AlarmManager.FLAG_WAKE_FROM_IDLE)) == 0 ) { mPendingWhileIdleAlarms.add(a); return ; } } if (RECORD_DEVICE_IDLE_ALARMS) { if ((a.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0 ) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = a.uid; ent.pkg = a.operation.getCreatorPackage(); ent.tag = a.operation.getTag("" ); ent.op = "SET" ; ent.elapsedRealtime = SystemClock.elapsedRealtime(); ent.argRealtime = a.whenElapsed; mAllowWhileIdleDispatches.add(ent); } } int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0 ) ? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed); if (whichBatch < 0 ) { Batch batch = new Batch(a); addBatchLocked(mAlarmBatches, batch); } else { Batch batch = mAlarmBatches.get(whichBatch); if (batch.add(a)) { mAlarmBatches.remove(whichBatch); addBatchLocked(mAlarmBatches, batch); } } if (a.alarmClock != null ) { mNextAlarmClockMayChange = true ; } boolean needRebatch = false ; if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0 ) { if (RECORD_DEVICE_IDLE_ALARMS) { if (mPendingIdleUntil == null ) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = 0 ; ent.pkg = "START IDLE" ; ent.elapsedRealtime = SystemClock.elapsedRealtime(); mAllowWhileIdleDispatches.add(ent); } } mPendingIdleUntil = a; mConstants.updateAllowWhileIdleMinTimeLocked(); needRebatch = true ; } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0 ) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { mNextWakeFromIdle = a; if (mPendingIdleUntil != null ) { needRebatch = true ; } } } if (!rebatching) { if (DEBUG_VALIDATE) { if (doValidate && !validateConsistencyLocked()) { Slog.v(TAG, "Tipping-point operation: type=" + a.type + " when=" + a.when + " when(hex)=" + Long.toHexString(a.when) + " whenElapsed=" + a.whenElapsed + " maxWhenElapsed=" + a.maxWhenElapsed + " interval=" + a.repeatInterval + " op=" + a.operation + " flags=0x" + Integer.toHexString(a.flags)); rebatchAllAlarmsLocked(false ); needRebatch = false ; } } if (needRebatch) { rebatchAllAlarmsLocked(false ); } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); } }
该方法的主要逻辑如下:
3.4.1 AlarmMS.fuzzForDuration 我们来看看 fuzzForDuration 方法:
1 2 3 4 5 6 7 8 9 10 11 12 static int fuzzForDuration (long duration) { if (duration < 15 *60 *1000 ) { return (int )duration; } else if (duration < 90 *60 *1000 ) { return 15 *60 *1000 ; } else { return 30 *60 *1000 ; } }
3.4.2 AlarmMS.attemptCoalesceLocked 通过非精确闹钟的触发时间,找到一个合适的 Batch !
1 2 3 4 5 6 7 8 9 10 11 12 int attemptCoalesceLocked (long whenElapsed, long maxWhen) { final int N = mAlarmBatches.size(); for (int i = 0 ; i < N; i++) { Batch b = mAlarmBatches.get(i); if ((b.flags&AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) { return i; } } return -1 ; }
可以看到,一个合适的 Batch 满足的条件如下:
Batch 的 flags 没有 AlarmManager.FLAG_STANDALONE 标志位,即该 Batch 是只能用于保存非精确闹钟!
canHold 方法返回 true,即:这个 Batch 能够容纳这个 Alarm!
下面,我们来看看 canHold 方法的逻辑:
3.4.2.1 AlarmMS.Batch.canHold canHold 方法用来判断,该 alarm 是否可以加入到这个 Batch 中:1 2 3 boolean canHold (long whenElapsed, long maxWhen) { return (end >= whenElapsed) && (start <= maxWhen); }
从方法中可以看出,可以容纳的依据是:
batch.end >= alarm.whenElapsed
batch.start <= alarm.maxWhen
即:batch 的时间间隔和 alarm 的触发时间间隔必须有交集!!
3.4.3 AlarmMS.addBatchLocked 我们来看看 addBatchLocked 方法的逻辑:
1 2 3 4 5 6 7 8 9 10 11 static boolean addBatchLocked (ArrayList<Batch> list, Batch newBatch) { int index = Collections.binarySearch(list, newBatch, sBatchOrder); if (index < 0 ) { index = 0 - index - 1 ; } list.add(index, newBatch); return (index == 0 ); }
这里的 sBatchOrder 是一个 BatchTimeOrder 实例,实现了 Comparator 接口,用来比较两个 Batch 的 start 时间的大小!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static final BatchTimeOrder sBatchOrder = new BatchTimeOrder(); static class BatchTimeOrder implements Comparator <Batch > { public int compare (Batch b1, Batch b2) { long when1 = b1.start; long when2 = b2.start; if (when1 > when2) { return 1 ; } if (when1 < when2) { return -1 ; } return 0 ; } }
可以看到,mAlarmBatches 中的 Batch 是按照开始时间从小到大排序的!!
下面我们来看看 Batch 的相关方法:
3.4.3.1 AlarmMS.Batch.Batch 当我们要将一个 Alarm 添加到新创建的 Batch 中的时候,会对 Batch 进行初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 Batch() { start = 0 ; end = Long.MAX_VALUE; flags = 0 ; } Batch(Alarm seed) { start = seed.whenElapsed; end = seed.maxWhenElapsed; flags = seed.flags; alarms.add(seed); }
不多说了,继续看!
3.4.3.2 AlarmMS.Batch.add 将一个 Alarm 添加到已存在的一个 Batch,通过 add 方法: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 boolean add (Alarm alarm) { boolean newStart = false ; int index = Collections.binarySearch(alarms, alarm, sIncreasingTimeOrder); if (index < 0 ) { index = 0 - index - 1 ; } alarms.add(index, alarm); if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this ); } if (alarm.whenElapsed > start) { start = alarm.whenElapsed; newStart = true ; } if (alarm.maxWhenElapsed < end) { end = alarm.maxWhenElapsed; } flags |= alarm.flags; if (DEBUG_BATCH) { Slog.v(TAG, " => now " + this ); } return newStart; }
可以看到,如果 Batch 的 start 时间发生变化,那么 add 会返回 true!
这里用到了一个比较器对象:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder();public static class IncreasingTimeOrder implements Comparator <Alarm > { public int compare (Alarm a1, Alarm a2) { long when1 = a1.whenElapsed; long when2 = a2.whenElapsed; if (when1 > when2) { return 1 ; } if (when1 < when2) { return -1 ; } return 0 ; } }
sIncreasingTimeOrder 是用来对 Batch 中的 alarm 进行排序的,规则是按照开始时间递增的顺序!
3.4.4 AlarmMS.rescheduleKernelAlarmsLocked 该方法用于设置下一个 alarm!
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 void rescheduleKernelAlarmsLocked () { long nextNonWakeup = 0 ; if (mAlarmBatches.size() > 0 ) { final Batch firstWakeup = findFirstWakeupBatchLocked(); final Batch firstBatch = mAlarmBatches.get(0 ); if (firstWakeup != null && mNextWakeup != firstWakeup.start) { mNextWakeup = firstWakeup.start; mLastWakeupSet = SystemClock.elapsedRealtime(); setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); } if (firstBatch != firstWakeup) { nextNonWakeup = firstBatch.start; } } if (mPendingNonWakeupAlarms.size() > 0 ) { if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) { nextNonWakeup = mNextNonWakeupDeliveryTime; } } if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) { mNextNonWakeup = nextNonWakeup; setLocked(ELAPSED_REALTIME, nextNonWakeup); } }
变量解释:
mNextWakeup 表示下一个最早的 wake up 类型 alarm 的触发时间;
mLastWakeupSet 表示上一次设置 wake up 类型 alarm 的时间,取值为 SystemClock.elapsedRealtime();
mNextNonWakeup 表示的是下一个最早的 no wake up 类型 alarm 的触发时间;
通常,二者是一起设置的!
逻辑梳理 :
该方法首先是确定下一个要触发的 wake up 和非 wake up 类型的 alarm 的触发时间:mNextWakeup 和 mNextNonWakeup!
对于 wake up 类型,那就在所有的 Batch 中找到第一个持有 wake up 类型 alarm 的 Batch,其 Batch.start 就是 mNextWakeup!
对于非 wake up 类型,确定 mNextNonWakeup 要分为以下几步:
如果第一个持有 wake up 类型 alarm 的 Batch 不是 first batch,那么 firstBatch.Start 为可选值,保存到 nextNonWakeup 中!
如果此时 mPendingNonWakeupAlarms 不为 empty,说明系统中有正在等待的 no wake up 类型的闹钟,如果此时 nextNonWakeup 为 0,或者 mNextNonWakeupDeliveryTime 小于 nextNonWakeup,那么 mNextNonWakeupDeliveryTime 就是一个更优的选择,保存到 nextNonWakeup 中;
最后,如果 nextNonWakeup 和 mNextNonWakeup 不相等,那就使用 nextNonWakeup 更新 mNextNonWakeup!
3.4.4.1 AlarmMS.findFirstWakeupBatchLocked findFirstWakeupBatchLocked 方法在所有的 Batch 中找到第一个包含 wake up 类型 alarm 的 Batch,然后返回!
1 2 3 4 5 6 7 8 9 10 private Batch findFirstWakeupBatchLocked () { final int N = mAlarmBatches.size(); for (int i = 0 ; i < N; i++) { Batch b = mAlarmBatches.get(i); if (b.hasWakeups()) { return b; } } return null ; }
这里调用了 Batch 的 hasWakeups 接口:
3.4.4.1.1 AlarmMS.Batch.hasWakeups 1 2 3 4 5 6 7 8 9 10 11 12 boolean hasWakeups () { final int N = alarms.size(); for (int i = 0 ; i < N; i++) { Alarm a = alarms.get(i); if ((a.type & TYPE_NONWAKEUP_MASK) == 0 ) { return true ; } } return false ; }
3.4.4.2 AlarmMS.setLocked 用于设置一个 alarm!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void setLocked (int type, long when) { if (mNativeData != 0 ) { long alarmSeconds, alarmNanoseconds; if (when < 0 ) { alarmSeconds = 0 ; alarmNanoseconds = 0 ; } else { alarmSeconds = when / 1000 ; alarmNanoseconds = (when % 1000 ) * 1000 * 1000 ; } set(mNativeData, type, alarmSeconds, alarmNanoseconds); } else { Message msg = Message.obtain(); msg.what = ALARM_EVENT; mHandler.removeMessages(ALARM_EVENT); mHandler.sendMessageAtTime(msg, when); } }
我们可以看到,这里使用了 2 中方式来设置闹钟!
第一种 :通过 Alarm 驱动来设置
mNativeData 不为 0,表示 Alarm 驱动存在,就直接调用 set 方法,通过 Alarm 驱动设置这个 alarm!
1 private native void set (long nativeData, int type, long seconds, long nanoseconds) ;
当然,正常情况,是通过 Alarm 驱动来设置的,因为这样能够实现休眠唤醒!
除非找不到 Alarm 驱动
mNativeData 为 0,说明 Alarm 驱动不存在,那就通过 Timer 定时器设置,这里通过发送一个 ALARM_EVENT 给 AlarmHandler,然后处理消息,设置 alarm!
3.4.4.3 AlarmMS.updateNextAlarmInfoForUserLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void updateNextAlarmInfoForUserLocked (int userId, AlarmManager.AlarmClockInfo alarmClock) { if (alarmClock != null ) { if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Next AlarmClockInfoForUser(" + userId + "): " + formatNextAlarm(getContext(), alarmClock, userId)); } mNextAlarmClockForUser.put(userId, alarmClock); } else { if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Next AlarmClockInfoForUser(" + userId + "): None" ); } mNextAlarmClockForUser.remove(userId); } mPendingSendNextAlarmClockChangedForUser.put(userId, true ); mHandler.removeMessages(AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED); mHandler.sendEmptyMessage(AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED); }
关于 AlarmHandler 我们后面会提到!
3.4.5 AlarmMS.updateNextAlarmClockLocked 该方法用于更新下一个 AlarmClock 的时间,当我们触发或者移除了一个 setAlarmClock 设置的 alarm 后,需要更新下一个 AlarmClock 的触发!
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 private void updateNextAlarmClockLocked () { if (!mNextAlarmClockMayChange) { return ; } mNextAlarmClockMayChange = false ; SparseArray<AlarmManager.AlarmClockInfo> nextForUser = mTmpSparseAlarmClockArray; nextForUser.clear(); final int N = mAlarmBatches.size(); for (int i = 0 ; i < N; i++) { ArrayList<Alarm> alarms = mAlarmBatches.get(i).alarms; final int M = alarms.size(); for (int j = 0 ; j < M; j++) { Alarm a = alarms.get(j); if (a.alarmClock != null ) { final int userId = UserHandle.getUserId(a.uid); AlarmManager.AlarmClockInfo current = mNextAlarmClockForUser.get(userId); if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Found AlarmClockInfo " + a.alarmClock + " at " + formatNextAlarm(getContext(), a.alarmClock, userId) + " for user " + userId); } if (nextForUser.get(userId) == null ) { nextForUser.put(userId, a.alarmClock); } else if (a.alarmClock.equals(current) && current.getTriggerTime() <= nextForUser.get(userId).getTriggerTime()) { nextForUser.put(userId, current); } } } } final int NN = nextForUser.size(); for (int i = 0 ; i < NN; i++) { AlarmManager.AlarmClockInfo newAlarm = nextForUser.valueAt(i); int userId = nextForUser.keyAt(i); AlarmManager.AlarmClockInfo currentAlarm = mNextAlarmClockForUser.get(userId); if (!newAlarm.equals(currentAlarm)) { updateNextAlarmInfoForUserLocked(userId, newAlarm); } } final int NNN = mNextAlarmClockForUser.size(); for (int i = NNN - 1 ; i >= 0 ; i--) { int userId = mNextAlarmClockForUser.keyAt(i); if (nextForUser.get(userId) == null ) { updateNextAlarmInfoForUserLocked(userId, null ); } } }
变量说明 :
mNextAlarmClockForUser 用于保存每个 userId 下即将出发的下一个 AlarmClock!
方法的流程总结 :
获得当前系统中最新的 AlarmClock 信息,保存到缓存 nextForUser 中;
如果缓存 nextForUser 中 userId 下还没有 AlarmClock,那就将该 AlarmClock 添加到缓存中!
如果缓存 nextForUser 中 userId 下已经有某个 AlarmClock,那就进一步比较:
只有当该 userId 下的缓存 AlarmClock 和 mNextAlarmClockForUser 中对应的 AlarmClock 相等,且 mNextAlarmClockForUser 中对应的 AlarmClock 触发时间更早,才会替换缓存;否则,保留缓存!
用缓存 nextForUser 来更新 mNextAlarmClockForUser 列表;
再次比较缓存 nextForUser 和 mNextAlarmClockForUser 中 userId 相同的 AlarmClock,如果二者不相同,用缓存更新 mNextAlarmClockForUser!
删除 mNextAlarmClockForUser 中某些 userId 下已经无效的 Alarm;
3.4.5.1 AlarmMS.updateNextAlarmInfoForUserLocked 为指定 userId 更新 AlarmClock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void updateNextAlarmInfoForUserLocked (int userId, AlarmManager.AlarmClockInfo alarmClock) { if (alarmClock != null ) { if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Next AlarmClockInfoForUser(" + userId + "): " + formatNextAlarm(getContext(), alarmClock, userId)); } mNextAlarmClockForUser.put(userId, alarmClock); } else { if (DEBUG_ALARM_CLOCK) { Log.v(TAG, "Next AlarmClockInfoForUser(" + userId + "): None" ); } mNextAlarmClockForUser.remove(userId); } mPendingSendNextAlarmClockChangedForUser.put(userId, true ); mHandler.removeMessages(AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED); mHandler.sendEmptyMessage(AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED); }
mPendingSendNextAlarmClockChangedForUser 列表用来记录某个 userId 下的 AlarmClock 发生了变化,如果发生了变化,他会用 userId -> true 的映射关系保存!
最后会发送 AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED 给 AlarmHandler 来处理,我们去看看:
3.4.5.1.1 AlarmMS.AlarmHandler[SEND_NEXT_ALARM_CLOCK_CHANGED] 1 2 3 case SEND_NEXT_ALARM_CLOCK_CHANGED: sendNextAlarmClockChanged(); break ;
AlarmHandler 会调用 sendNextAlarmClockChanged 来处理消息:
3.4.5.1.2 AlarmMS.sendNextAlarmClockChanged 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 private void sendNextAlarmClockChanged () { SparseArray<AlarmManager.AlarmClockInfo> pendingUsers = mHandlerSparseAlarmClockArray; pendingUsers.clear(); synchronized (mLock) { final int N = mPendingSendNextAlarmClockChangedForUser.size(); for (int i = 0 ; i < N; i++) { int userId = mPendingSendNextAlarmClockChangedForUser.keyAt(i); pendingUsers.append(userId, mNextAlarmClockForUser.get(userId)); } mPendingSendNextAlarmClockChangedForUser.clear(); } final int N = pendingUsers.size(); for (int i = 0 ; i < N; i++) { int userId = pendingUsers.keyAt(i); AlarmManager.AlarmClockInfo alarmClock = pendingUsers.valueAt(i); Settings.System.putStringForUser(getContext().getContentResolver(), Settings.System.NEXT_ALARM_FORMATTED, formatNextAlarm(getContext(), alarmClock, userId), userId); getContext().sendBroadcastAsUser(NEXT_ALARM_CLOCK_CHANGED_INTENT, new UserHandle(userId)); } }
mHandlerSparseAlarmClockArray 是一个预先创建的 SparseArray,没有很特殊的用途!
在 sendNextAlarmClockChanged 方法中,我们会处理前面的 mPendingSendNextAlarmClockChangedForUser!
3.5 阶段总结 到这里,我们 setAlarm 的整个流程就分析完成了,下面,我们来总结一下整个过程!
… … …