转载请标明出处:http://www.jianshu.com/users/183339cdc7ae/latest_articles
概述
这片文章会分析alarm的实现原理(以驱动支持的情况为例),从而可以知道的是:为什么在5.0以上进程被杀死后没有办法在执行alarm的指定动作,
alarm的系统架构十分简单,
AlarmManager
API的使用,大家肯定不会陌生,直接给出代码,以Broadcast为例(省略掉了AndroidManifest.xml里面的权限)
Intent intent = new Intent();
intent.setAction("xxxxx");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,intent, 0);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 5 * 1000, pendingIntent);
5 * 1000:单位是毫秒,意思是5s后,以broadcast的方式发送intent
来看看setRepeating的实现
public void setRepeating(int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, operation, null, null);
}
private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) {
...
mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
workSource, alarmClock);
....
}
其实是调用了mService的set方法,mService是IAlarmManager的对象,看名字就可以知道,这里是一个代理类,具体的实现实在AlarmManagerService里面
AlarmManagerService
private final IBinder mService = new IAlarmManager.Stub() {
@Override
public void set(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock) {
...
setImpl(type, triggerAtTime, windowLength, interval, operation,
windowLength == AlarmManager.WINDOW_EXACT, workSource, alarmClock);
}
来看看setImpl的代码
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, boolean isStandalone, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock) {
....
if (interval > 0 && interval < MIN_INTERVAL) {
interval = MIN_INTERVAL;
}
....
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
interval, operation, isStandalone, true, workSource, alarmClock, userId,
private static final long MIN_INTERVAL = 60 * 1000;
在5.0的android上面,如果app里面设置的触发时间<1min,系统会强制将触发时间设置成1min。
setImplLocked这个方法会更新AlarmManagerService中的list,该list保存的是整个系统中所有的alarm。
然后调用rescheduleKernelAlarmsLocked-->setLocked
private void setLocked(int type, long when) {
if (mNativeData != 0) {...}
...
set(mNativeData, type, alarmSeconds, alarmNanoseconds);
} else {
Message msg = Message.obtain();
msg.what = ALARM_EVENT;
mHandler.removeMessages(ALARM_EVENT);
mHandler.sendMessageAtTime(msg, when);
}
}
这个mNativeData成员变量如果!=0,表示的是该系统支持驱动设置时间(笔者这里猜想的是可以通过设置时间到驱动中,通过硬件中断来完成时间到点后的回调)。如果=0,则使用handler的方式来执行,handler的具体实现其实是使用的Linux中的一个叫epoll的api来实现的,这里不做具体讲解。 我们先假设mNativeData !=0的情况,会调用一个native的set方法
com_android_server_AlarmManagerService.cpp
static JNINativeMethod sMethods[] = {
....
{"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
....
};
static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds)
{
AlarmImpl *impl = reinterpret_cast(nativeData);
...
int result = impl->set(type, &ts);
...
}
AlarmImpl的set方法是一个虚方法,有2个类实现该方法
- AlarmImplAlarmDriver
- AlarmImplTimerFd
到底是使用的哪个对象来实现,先来看看初始化的时候使用的哪一个
static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject) {
jlong ret = init_alarm_driver();
if (ret) {
return ret;
}
return init_timerfd();
}
init_alarm_driver
static jlong init_alarm_driver() {
int fd = open("/dev/alarm", O_RDWR);
if (fd < 0) {
ALOGV("opening alarm driver failed: %s", strerror(errno));
return 0;
}
AlarmImpl *ret = new AlarmImplAlarmDriver(fd);
return reinterpret_cast(ret);
}
可以知道如果驱动支持,会在dev目录下面生产一个alarm的节点,在初始化alarm系统的时候fd就不会<0
本文最开始说了,这里以驱动支持为例.
当我们系统不支持驱动设置时间的时候,会调用init_timerfd方法,大家可以自行去看看该方法,使用的就是epoll的机制,前文也提到过
所以来看看AlarmImplAlarmDriver的set方法
int AlarmImplAlarmDriver::set(int type, struct timespec *ts)
{
return ioctl(fds[0], ANDROID_ALARM_SET(type), ts);
}
通过ioctl的方式将时间设置给驱动,后面的实现需要去看驱动代码,因为各个厂商的实现可能会不同,这里不做详细讲解
但是最后的作用应该都是一样的,就是把时间设置给驱动,等到硬件中断返回后,再回调到native层,native再回调到framework
native回调
那硬件中断产生后是如何回调的呢?来看看AlarmManagerService里做的一个重要事情(假设驱动支持,即mNativeData !=0)
如果不支持的话会以epoll的方式回调
AlarmManagerService
@Override
public void onStart() {
...
if (mNativeData != 0) {
AlarmThread waitThread = new AlarmThread();
waitThread.start();
} else {
Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
...
}
对,你没有看错,这里启动一个thread
private class AlarmThread extends Thread {
...
public void run() {
while (true) {
int result = waitForAlarm(mNativeData);
...
deliverAlarmsLocked(triggerList, nowELAPSED);
}
}
}
一个死循环来等待驱动的返回,waitForAlarm是一个native方法具体的实现在驱动中。如果整个系统中没有alarm的时间回调,waitForAlarm则阻塞在这,直到有回调的时候才往后执行,这样会减少CPU的开销。
framework回调
deliverAlarmsLocked
void deliverAlarmsLocked(ArrayList triggerList, long nowELAPSED) {
...
alarm.operation.send(getContext(), 0,
mBackgroundIntent.putExtra(
Intent.EXTRA_ALARM_COUNT, alarm.count),
mResultReceiver, mHandler);
...
这里的operation是PendingIntent对象,最后回调其实是PendingIntent的send方法
public void send(Context context, int code, Intent intent,
OnFinished onFinished, Handler handler, String requiredPermission)
throws CanceledException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
int res = mTarget.send(code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
requiredPermission);
...
}
其中最重要的是mTarget,来看看mTarget是何方圣神
private final IIntentSender mTarget;
/*package*/ PendingIntent(IIntentSender target) {
mTarget = target;
}
那又是合适调用的PendingIntent(IIntentSender target)方法呢,这里以getBroadcast为例
public static PendingIntent getBroadcast(Context context, int requestCode,
Intent intent, @Flags int flags) {
return getBroadcastAsUser(context, requestCode, intent, flags,
new UserHandle(UserHandle.myUserId()));
}
public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
Intent intent, int flags, UserHandle userHandle) {
...
try {
...
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_BROADCAST, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, userHandle.getIdentifier());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
return null;
}
当我们再app中使用PendingIntent的getBroadcast方法的时候,返回的PendingIntent对象中包含了一个私有变量target,该taget就是驱动回调到framework层后正真做事情的发起点。
如果当我们app所在的进程被杀死后,进程中的变量会被回收,当然这个target也会被回收掉,所以没有办法进行回调
那taget的send方法是怎么实现的呢?
PendingIntentRecord.java
final class PendingIntentRecord extends IIntentSender.Stub {
...
public int send(int code, Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission) {
return sendInner(code, intent, resolvedType, finishedReceiver,
requiredPermission, null, null, 0, 0, 0, null, null);
}
...
int sendInner(int code, Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues, Bundle options, IActivityContainer container) {
...
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
...
owner.startActivitiesInPackage(uid, key.packageName, allIntents,
allResolvedTypes, resultTo, options, userId);
case ActivityManager.INTENT_SENDER_BROADCAST:
...
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
requiredPermission, (finishedReceiver != null), false, userId);
...
case ActivityManager.INTENT_SENDER_SERVICE:
...
owner.startServiceInPackage(uid,
finalIntent, resolvedType, userId);
...
}
...
}
}
最后根据不同的类型调用ams的方法启动Broadcast / Activity / Service
总结
老规矩,上一张流程图