深入理解AlarmManagerService
通过AlarmManager方便的设置定时任务,详见《AlarmManager定时任务》。现在来看看其中更深层次的原理,主AlarmManager要通过AIDL远程调用AlarmManagerService接口方法来在系统Service中设置定时任务。以set方法为例跟随设置流程来看看一个定时任务是如何被添加到系统中的。
1、setImpl(...)
所有调用AlarmManager设置定时任务的方法最终都是调用setImpl(int type, long triggerAtMillis, long windowMillis,long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,String listenerTag, Handler targetHandler, WorkSource workSource,AlarmClockInfo alarmClock)私有方法,
1.1、参数含义
setImpl(...)方法一共有11个参数,可见Android提供的AlarmManager类为开发者做了很多封装。setImpl(...)方法中每个参数的含义如下:
①type:定时任务类型,详见《AlarmManager定时任务》第二节
②triggerAtMillis:触发时间
③windowMillis:窗口时间
④intervalMillis:重复时间
⑤flags:一般为0,内部使用;此外还有FLAG_STANDALONE、FLAG_WAKE_FROM_IDLE、FLAG_ALLOW_WHILE_IDLE等
⑥operation:时间到时执行的PendingIntent
⑦listener:回调OnAlarmListener
⑧listenerTag:tag标记
⑨targetHandler:在该Handler上执行OnAlarmListener 回调,如为null则默认在应用程序Main Handler上执行
⑩workSource:一般为null
⑪alarmClock:一般为null
1.2、完整代码
setImpl(...)方法的源码如下,一共
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) { /* NOTYET if (mAlwaysExact) { // Fatal error for KLP+ apps to use negative trigger times throw new IllegalArgumentException("Invalid alarm trigger time " + triggerAtMillis); } */ triggerAtMillis = 0; } ListenerWrapper recipientWrapper = null; if (listener != null) { synchronized (AlarmManager.class) { if (sWrappers == null) { sWrappers = new ArrayMap<OnAlarmListener, ListenerWrapper>(); } recipientWrapper = sWrappers.get(listener); // no existing wrapper => build a new one 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(); } }
1.3、代码片段详解
①传入设置定时任务的时间triggerAtMillis为负就设置为0,并不会抛出异常,一般如果传入的时间为负没有意义理应直接抛出异常才对。
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; }
②这一段代码的含义就是设置将OnAlarmListener封装为ListenerWrapper,并且保存在AlarmManager静态成员集合sWrappers中。
ListenerWrapper recipientWrapper = null; if (listener != null) { synchronized (AlarmManager.class) { if (sWrappers == null) { sWrappers = new ArrayMap<OnAlarmListener, ListenerWrapper>(); } recipientWrapper = sWrappers.get(listener); // no existing wrapper => build a new one if (recipientWrapper == null) { recipientWrapper = new ListenerWrapper(listener); sWrappers.put(listener, recipientWrapper); } } final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler; recipientWrapper.setHandler(handler); }
并且设置执行OnAlarmListener的Handler,可以看到传入的targetHandler为null,就使用主线程的Handler来执行。AlarmManager中的mMainThreadHandler成员变量在类构造方法中使用主线程的Main Looper构造。
/** * package private on purpose */ AlarmManager(IAlarmManager service, Context ctx) { mService = service; mContext = ctx; mPackageName = ctx.getPackageName(); mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion; mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT); mMainThreadHandler = new Handler(ctx.getMainLooper()); }
③最后,也是最关键的一步,借助mService设置定时任务。最终还是由Service来接管定时任务。
try { mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, recipientWrapper, listenerTag, workSource, alarmClock); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }
mService是IAlarmManager,是AIDL接口类,定义在android.app包中。
@UnsupportedAppUsage private final IAlarmManager mService;
2、AlarmManagerService
IAlarmManager AIDL接口在AlarmManagerService中实现。AlarmManagerService属于系统其它服务。
2.1、AlarmManagerService的创建和启动
SystemServer启动的过程中,在startOtherServices()方法中启动该服务,详见《AMS服务的启动》。
/** * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized. */ private void startOtherServices() { ... traceBeginAndSlog("StartAlarmManagerService"); mSystemServiceManager.startService(new AlarmManagerService(context)); ... }
调用AlarmManagerService的构造函数。
AlarmManagerService(Context context, Injector injector) { super(context); mInjector = injector; } AlarmManagerService(Context context) { this(context, new Injector(context)); }
在《AMS服务的启动》2.1节中了解到startService()开启服务调用的是各个系统服务的onStart()方法,AlarmManagerService的onStart()方法完整代码如下:
public void onStart() { mInjector.init(); synchronized (mLock) { mHandler = new AlarmHandler(); mOperationCancelListener = (intent) -> removeImpl(intent, null); mConstants = new Constants(mHandler); mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW); mNextWakeup = mNextNonWakeup = 0; // We have to set current TimeZone info to kernel // because kernel doesn't keep this after reboot setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY)); // Ensure that we're booting with a halfway sensible current time. Use the // most recent of Build.TIME, the root file system's timestamp, and the // value of the ro.build.date.utc system property (which is in seconds). final long systemBuildTime = Long.max( 1000L * SystemProperties.getLong("ro.build.date.utc", -1L), Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); if (mInjector.getCurrentTimeMillis() < systemBuildTime) { Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis() + ", advancing to build time " + systemBuildTime); mInjector.setKernelTime(systemBuildTime); } // Determine SysUI's uid mSystemUiUid = mInjector.getSystemUiUid(); if (mSystemUiUid <= 0) { Slog.wtf(TAG, "SysUI package not found!"); } mWakeLock = mInjector.getAlarmWakeLock(); mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); mTimeTickTrigger = new IAlarmListener.Stub() { @Override public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException { if (DEBUG_BATCH) { Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); } // Via handler because dispatch invokes this within its lock. OnAlarmListener // takes care of this automatically, but we're using the direct internal // interface here rather than that client-side wrapper infrastructure. mHandler.post(() -> { getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL); try { callback.alarmComplete(this); } catch (RemoteException e) { /* local method call */ } }); synchronized (mLock) { mLastTickReceived = mInjector.getCurrentTimeMillis(); } mClockReceiver.scheduleTimeTickEvent(); } }; Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); mClockReceiver = mInjector.getClockReceiver(this); new InteractiveStateReceiver(); new UninstallReceiver(); if (mInjector.isAlarmDriverPresent()) { AlarmThread waitThread = new AlarmThread(); waitThread.start(); } else { Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); } try { ActivityManager.getService().registerUidObserver(new UidObserver(), ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE, ActivityManager.PROCESS_STATE_UNKNOWN, null); } catch (RemoteException e) { // ignored; both services live in system_server } } publishLocalService(AlarmManagerInternal.class, new LocalService()); publishBinderService(Context.ALARM_SERVICE, mService); }
主要完成创建AlarmHandler、注册广播接收器监听应用的卸载安装、LocalService注册服务、mService服务发布等关键操作。
2.2、IAlarmManager AIDL接口
IAlarmManagerAIDL接口定义在android.app包中,定义了如下7个接口方法,都是AlarmManager中用到的:
/** * System private API for talking with the alarm manager service. */ interface IAlarmManager { /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */ 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); long currentNetworkTimeMillis(); }
2.3、IAlarmManager接口实现
mService是IAlarmManager接口匿名实现类的实例,实现了IAlarmManager中的接口方法。
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(); // make sure the caller is not lying about which package should be blamed for // wakelock time spent in alarm delivery mAppOps.checkPackage(callingUid, callingPackage); // Repeating alarms must use PendingIntent, not direct listener 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"); } // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm // manager when to come out of idle mode, which is only for DeviceIdleController. if (callingUid != Process.SYSTEM_UID) { flags &= ~AlarmManager.FLAG_IDLE_UNTIL; } // If this is an exact time alarm, then it can't be batched with other alarms. if (windowLength == AlarmManager.WINDOW_EXACT) { flags |= AlarmManager.FLAG_STANDALONE; } // If this alarm is for an alarm clock, then it must be standalone and we will // use it to wake early from idle if needed. if (alarmClock != null) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; // If the caller is a core system component or on the user's whitelist, and not calling // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. // This means we will allow these alarms to go off as normal even while idle, with no // timing restrictions. } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID || UserHandle.isSameApp(callingUid, mSystemUiUid) || ((mAppStateTracker != null) && mAppStateTracker.isUidPowerSaveUserWhitelisted(callingUid)))) { 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); } @Override public boolean setTime(long millis) { getContext().enforceCallingOrSelfPermission( "android.permission.SET_TIME", "setTime"); return setTimeImpl(millis); } @Override public void setTimeZone(String tz) { getContext().enforceCallingOrSelfPermission( "android.permission.SET_TIME_ZONE", "setTimeZone"); final long oldId = Binder.clearCallingIdentity(); try { setTimeZoneImpl(tz); } finally { Binder.restoreCallingIdentity(oldId); } } @Override public void remove(PendingIntent operation, IAlarmListener listener) { if (operation == null && listener == null) { Slog.w(TAG, "remove() with no intent or listener"); return; } synchronized (mLock) { removeLocked(operation, listener); } mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, operation).sendToTarget(); } @Override public long getNextWakeFromIdleTime() { return getNextWakeFromIdleTimeImpl(); } @Override public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false /* allowAll */, false /* requireFull */, "getNextAlarmClock", null); return getNextAlarmClockImpl(userId); } @Override public long currentNetworkTimeMillis() { final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext()); if (time.hasCache()) { return time.currentTimeMillis(); } else { throw new ParcelableException(new DateTimeException("Missing NTP fix")); } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; if (args.length > 0 && "--proto".equals(args[0])) { dumpProto(fd); } else { dumpImpl(pw); } } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { (new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver); } };
2.4、Alarm内部类
AlarmManagerService内部封装了一个Alarm类,包含传入的PendingIntent还有一些时间参数。
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; ... }
3、set(...)
重点来看看IAlarmManager接口的set(...)实现方法的执行流程。
3.1、set(...)实现
IAlarmManager中的set(...)方法在Stub匿名类中实现,完整实现方法如下:
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(); // make sure the caller is not lying about which package should be blamed for // wakelock time spent in alarm delivery mAppOps.checkPackage(callingUid, callingPackage); // Repeating alarms must use PendingIntent, not direct listener 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"); } // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm // manager when to come out of idle mode, which is only for DeviceIdleController. if (callingUid != Process.SYSTEM_UID) { flags &= ~AlarmManager.FLAG_IDLE_UNTIL; } // If this is an exact time alarm, then it can't be batched with other alarms. if (windowLength == AlarmManager.WINDOW_EXACT) { flags |= AlarmManager.FLAG_STANDALONE; } // If this alarm is for an alarm clock, then it must be standalone and we will // use it to wake early from idle if needed. if (alarmClock != null) { flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; // If the caller is a core system component or on the user's whitelist, and not calling // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. // This means we will allow these alarms to go off as normal even while idle, with no // timing restrictions. } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID || UserHandle.isSameApp(callingUid, mSystemUiUid) || ((mAppStateTracker != null) && mAppStateTracker.isUidPowerSaveUserWhitelisted(callingUid)))) { 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); }
注意其中小细节,判断如果设置了重复的定时任务,即interval不为零(为什么不是大于零呢?难道重复还能负?)。那么会判断IAlarmListener是否为空,不空就抛出异常,注意说明已经很清楚了“Repeating alarms must use PendingIntent, not direct listener”,设置重复定时任务仅允许使用PendIngIntent,不准使用OnAlarmListener。不然出现问题还不知道哪里错了,原来Android系统在这里进行了条件限制!!!
3.2、setImpl(...)
调用了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)方法,和AlarmManager类似的同名方法。
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) { // must be *either* PendingIntent or AlarmReceiver, but not both if ((operation == null && directReceiver == null) || (operation != null && directReceiver != null)) { Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver"); // NB: previous releases failed silently here, so we are continuing to do the same // rather than throw an IllegalArgumentException. return; } ... setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, directReceiver, listenerTag, flags, true, workSource, alarmClock, callingUid, callingPackage); } }
继续调用setImplLocked(...)方法
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 (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) { Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a + " -- package not allowed to start"); mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, operation).sendToTarget(); return; } } catch (RemoteException e) { } removeLocked(operation, directReceiver); incrementAlarmCount(a.uid); setImplLocked(a, false, doValidate); }
接着调用另一个同名的重载函数setImplLocked(Alarm a, boolean rebatching, boolean doValidate)
private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { ... 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; } } ... }
调用rebatchAllAlarmsLocked(boolean)方法
void rebatchAllAlarmsLocked(boolean doValidate) { ... rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start); }
调用rescheduleKernelAlarmsLocked()方法
void rescheduleKernelAlarmsLocked() { // Schedule the next upcoming wakeup alarm. If there is a deliverable batch // prior to that which contains no wakeups, we schedule that as well. ... if (firstWakeup != null) { mNextWakeup = firstWakeup.start; mNextWakeUpSetAt = nowElapsed; setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); } ... }
调用setLocked(...)方法
private void setLocked(int type, long when) { if (mInjector.isAlarmDriverPresent()) { mInjector.setAlarm(type, when); } else { Message msg = Message.obtain(); msg.what = AlarmHandler.ALARM_EVENT; mHandler.removeMessages(msg.what); mHandler.sendMessageAtTime(msg, when); } }
3.3、Injector.setAlarm
最后调用Injector的setAlarm,Injector类实例在AlarmManagerService构造函数中创建
void setAlarm(int type, long millis) { // The kernel never triggers alarms with negative wakeup times // so we ensure they are positive. final long alarmSeconds, alarmNanoseconds; if (millis < 0) { alarmSeconds = 0; alarmNanoseconds = 0; } else { alarmSeconds = millis / 1000; alarmNanoseconds = (millis % 1000) * 1000 * 1000; } final int result = AlarmManagerService.set(mNativeData, type, alarmSeconds, alarmNanoseconds); if (result != 0) { final long nowElapsed = SystemClock.elapsedRealtime(); Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed + " type=" + type + " @ (" + alarmSeconds + "," + alarmNanoseconds + "), ret = " + result + " = " + Os.strerror(result)); } }
3.4、native set()方法
最后到了native方法set设置定时任务,底层使用C++实现的。
private static native int set(long nativeData, int type, long seconds, long nanoseconds);
service系统服务相关的native类和方法在/frameworks/base/services/core/jni目录下,和AlarmManagerService相关的native代码在com_android_server_AlarmManagerService.cpp中。定义的set底层方法为:
static jint android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds) { AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; const int result = impl->set(type, &ts); if (result < 0) { ALOGE("Unable to set alarm to %lld.%09lld: %s\n", static_cast<long long>(seconds), static_cast<long long>(nanoseconds), strerror(errno)); } return result >= 0 ? 0 : errno; }
调用AlarmImpl类的set方法
int AlarmImpl::set(int type, struct timespec *ts) { if (static_cast<size_t>(type) > ANDROID_ALARM_TYPE_COUNT) { errno = EINVAL; return -1; } if (!ts->tv_nsec && !ts->tv_sec) { ts->tv_nsec = 1; } /* timerfd interprets 0 = disarm, so replace with a practically equivalent deadline of 1 ns */ struct itimerspec spec; memset(&spec, 0, sizeof(spec)); memcpy(&spec.it_value, ts, sizeof(spec.it_value)); return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL); }
最后通过Linux的timerfd_settime函数设置定时对象,并返回文件描述符。
了解一下timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行定时通知,通常配合select/poll/epoll使用。
到这里就算结束了,从应用层到frameworks层再到linux底层都理清了一遍,了解了通过AlarmManager设置定时任务的大概流程和原理。
推荐博客: