AlarmManager詳解

版權說明:本文為 開開向前沖 原創(chuàng)文章萍虽,轉載請注明出處轴脐;
注:限于作者水平有限,文中有不對的地方還請指教

項目需求:AP需要在開機24小時后自檢重啟砾嫉;
針對上述需求幼苛,我們首先想想有哪些實現(xiàn)方法呢?

  • 1:開機接收廣播焕刮,啟動一個常駐服務舶沿,在服務中輪訓。輪訓的方式可以通過Handler或者啟動一個常駐服務配并,在服務中開啟線程中做死循環(huán)括荡;
  • 2:使用Timer 來定時操作;
  • 3:使用AlarmManager 來實現(xiàn)定時操作功能溉旋;

上述三個方法看似都可行畸冲;實際上只有最后一種可行,前兩種都是不可行的,為什么不可行呢召夹?
一:服務輪訓:Handler發(fā)送消息岩喷,消息的發(fā)送依賴于Handler 線程,如果線程結束监憎,GG!那開啟一個常駐服務呢婶溯,在服務中開啟一個線程做死循環(huán)鲸阔,我們先不說省電問題,如果系統(tǒng)進入深度睡眠迄委,即使這個while(1)的循環(huán)也不能得到執(zhí)行褐筛;
二:Timer:Timer的問題也是在于如果系統(tǒng)進入深度睡眠杰刽,將無法喚醒焙格;

所以只有AlarmManager∝参剩可以通過AlarmManager定時喚醒系統(tǒng)執(zhí)行任務信轿,即使系統(tǒng)處于深度睡眠也能喚醒——這就和我們的鬧鐘差不多晃痴;也能省電;那使用AlarmManager如果做呢财忽?

代碼敬上:

AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        Intent in = new Intent();
        in.setClass(context, RebootService.class);
        PendingIntent pi = PendingIntent.getService(context, 0, in, 0);
        aManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,24 * 60 * 60 * 1000, pi);

沒錯倘核,AlarmManager的使用就是這個簡單,在開機后24小時系統(tǒng)將會啟動RebootService即彪,RebootService是我自己實現(xiàn)的一個服務紧唱,在這個服務中任意Lifecycle中實現(xiàn)關機操作就OK;

            PowerManager pm = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
            pm.reboot("self-inspection");

AlarmManager不僅可以喚醒服務隶校,也可以發(fā)送廣播漏益,也可以調起Activity;需要將上述PendingIntent.getService()修改成對應的PendingIntent.getActivity()或者PendingIntent.getBroadcast()方法深胳;

因為之前有維護過原生Clock APP绰疤,對AlarmManager有了解,正好借此機會完整機會闡述一下AlarmManager稠屠;

AlarmManager 對象獲嚷退:

 AlarmManager aManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

AlarmManager 常用接口:

AlarmManager.png
  • set(int type, long triggerAtMillis, PendingIntent operation)
    一次性任務;
  • setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
    重復任務权埠;時間固定榨了;
  • setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
    重復任務,時間不固定攘蔽;
  • cancel(PendingIntent operation)
    取消上述設置的定時任務龙屉;此時PendingIntent務必和需要取消的任務的PendingIntent一模一樣;

這里主要介紹了幾個常用于定時任務的接口;關于AlarmManager 的接口和用法转捕,
可以參考Google官方API文檔AlarmManager 文檔作岖;

接口參數(shù)詳解:

  • int type
AlarmManager.RTC:硬件時間,不喚醒休眠設備五芝;當休眠時不發(fā)起鬧鐘痘儡。
AlarmManager.RTC_WAKEUP:硬件時間,當鬧鐘發(fā)射時喚醒休眠設備枢步;
AlarmManager.ELAPSED_REALTIME:真實時間流逝沉删,不喚醒休眠設備;當設備休眠時不發(fā)起鬧鐘醉途。
AlarmManager.ELAPSED_REALTIME_WAKEUP:真實時間流逝矾瑰,當鬧鐘發(fā)起時喚醒手機休眠;

    RTC鬧鐘和ELAPSED_REALTIME 最大的差別就是前者可以通過修改手機時間觸發(fā)鬧鐘事件隘擎,
后者要通過真實時間的流逝殴穴,即使在休眠狀態(tài),時間也會被計算货葬。
  • long triggerAtMillis : 鬧鐘第一次執(zhí)行時間采幌,毫秒為單位,需與第一個type參數(shù)匹配宝惰,
    1. 如果是RTC類型植榕,triggerAtMillis 則一般使用System.currentTimeMillis();
    2. 如果是ELAPSED類型尼夺,triggerAtMillis 則一般使用SystemClock.elapsedRealtime();
  • long intervalMillis : 兩次鬧鐘執(zhí)行間隔
  • PendingIntent operation : 任務的執(zhí)行動作尊残,發(fā)送廣播,啟動activity淤堵,啟動service

上述大概講了AlarmManager接口如何使用寝衫,作為一名System Engineer,我們還需要研究研究真實的服務提供者AlarmManagerService:

從上述名字中我們知道AlarmManager 只是AlarmManagerService的代理拐邪,實際實現(xiàn)都是在AlarmManagerService中實現(xiàn)的慰毅;

AlarmManagerService啟動

frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices() {
      ...
            mAlarmManagerService = mSystemServiceManager.startService(AlarmManagerService.class);
    //mSystemServiceManager 即是SystemServiceManger
            alarm = IAlarmManager.Stub.asInterface(
                    ServiceManager.getService(Context.ALARM_SERVICE));
      ...
}

frameworks\base\services\core\java\com\android\server\SystemServiceManager.java
public <T extends SystemService> T startService(Class<T> serviceClass) {
...
            Constructor<T> constructor = serviceClass.getConstructor(Context.class);
            service = constructor.newInstance(mContext);
...
            mServices.add(service);
            service.onStart(); //service 即是AlarmManagerService
}

frameworks\base\services\core\java\com\android\server\AlarmManagerService.java
@Override
    public void onStart() {
        mNativeData = init();//init方法是native 方法,調用JNI
        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));

        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");

        mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
                new Intent(Intent.ACTION_TIME_TICK).addFlags(
                        Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_FOREGROUND), 0,
                        UserHandle.ALL);
        Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
        
        // now that we have initied the driver schedule the alarm
        mClockReceiver = new ClockReceiver();
        mClockReceiver.scheduleTimeTickEvent();
        mClockReceiver.scheduleDateChangedEvent();
        mInteractiveStateReceiver = new InteractiveStateReceiver();
        mUninstallReceiver = new UninstallReceiver();
        /*
        上述代碼注冊了各種監(jiān)聽器扎阶,監(jiān)聽各種和時間變化相關的事務
        */
        if (mNativeData != 0) {
            AlarmThread waitThread = new AlarmThread(); //創(chuàng)建AlarmThread
            waitThread.start();
        } else {
            Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
        }

        publishBinderService(Context.ALARM_SERVICE, mService); //注冊服務汹胃,調用ServiceManager.addService添加服務;
    }

最后的publishBinderService即調用ServiceManager.addService注冊服務东臀,服務

AlarmManagerService的onStart方法工作

  1. 由于重啟內核沒有時區(qū)信息着饥,需要將時區(qū)信息保存到內核;
  2. 創(chuàng)建ClockReceiver用于監(jiān)聽TIME_TICK和DATE_CHANGED廣播惰赋;
  3. 創(chuàng)建InteractiveStateReceiver宰掉,用于監(jiān)聽亮屏/滅屏廣播;
  4. 創(chuàng)建UninstallReceiver,用于監(jiān)聽package移除/重啟轨奄,sdcard不可用的廣播孟害;
  5. 創(chuàng)建線程”AlarmManager”;
  6. 注冊服務挪拟。

AlarmManagerService中的native JNI方法:

    private native long init();
    private native void close(long nativeData);
    private native void set(long nativeData, int type, long seconds, long nanoseconds);
    private native void clear(long nativeData, int type, long seconds, long nanoseconds);
    private native int waitForAlarm(long nativeData);
    private native int setKernelTime(long nativeData, long millis);
    private native int setKernelTimezone(long nativeData, int minuteswest);

我們根據(jù)Android JNI 文件命名規(guī)范(報名中"."替換為"_")知道對應的JNI文件名為com_android_server_AlarmManagerService.cpp

static JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
    {"init", "()J", (void*)android_server_AlarmManagerService_init},
    {"close", "(J)V", (void*)android_server_AlarmManagerService_close},
    {"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
    {"clear", "(JIJJ)V", (void*)android_server_AlarmManagerService_clear},
    {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
    {"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime},
    {"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
};

上述說道在AlarmManagerService.java中onStart方法中有調用native JNI init()方法挨务;

static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
{
    jlong ret = init_alarm_driver(); //初始化alarm driver
    if (ret) {
        return ret;
    }
    return init_timerfd(); //初始化文件描述符
}

static jlong init_alarm_driver()
{
    int fd = open("/dev/alarm", O_RDWR); //打開節(jié)點/dev/alarm,并創(chuàng)建Alarm驅動對象舞丛。
    if (fd < 0) {
        ALOGV("opening alarm driver failed: %s", strerror(errno));
        return 0;
    }
    AlarmImpl *ret = new AlarmImplAlarmDriver(fd);//創(chuàng)建AlarmImplAlarmDriver對象
    return reinterpret_cast<jlong>(ret);
}

static jlong init_timerfd()
{
    int epollfd;
    int fds[N_ANDROID_TIMERFDS];
     ......
    epollfd = epoll_create(N_ANDROID_TIMERFDS);
    for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
        fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
       ......
    }
    AlarmImpl *ret = new AlarmImplTimerFd(fds, epollfd);//創(chuàng)建AlarmImplTimerFd對象

    for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
        epoll_event event;
        event.events = EPOLLIN | EPOLLWAKEUP;
        event.data.u32 = i;

        int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event);
        if (err < 0) {
            ALOGV("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno));
            delete ret;
            return 0;
        }
    }

    struct itimerspec spec;
    memset(&spec, 0, sizeof(spec));
    /* 0 = disarmed; the timerfd doesn't need to be armed to get
       RTC change notifications, just set up as cancelable */

    int err = timerfd_settime(fds[ANDROID_ALARM_TYPE_COUNT],
            TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL);
    if (err < 0) {
        ALOGV("timerfd_settime() failed: %s", strerror(errno));
        delete ret;
        return 0;
    }

    return reinterpret_cast<jlong>(ret);
}
//android_alarm_to_clockid 數(shù)組的定義
android_alarm_to_clockid[N_ANDROID_TIMERFDS] = {
    CLOCK_REALTIME_ALARM,
    CLOCK_REALTIME,
    CLOCK_BOOTTIME_ALARM,
    CLOCK_BOOTTIME,
    CLOCK_MONOTONIC,
    CLOCK_REALTIME,
};

AlarmThread

private class AlarmThread extends Thread
    {
        public AlarmThread()
        {
            super("AlarmManager");
        }
        
        public void run()
        {
            ArrayList<Alarm> triggerList = new ArrayList<Alarm>();

            while (true) //無線循環(huán)
            {
                int result = waitForAlarm(mNativeData); //JNI 方法耘子,使用EPOLL監(jiān)聽FD,等待驅動返回執(zhí)行下面的分發(fā)執(zhí)行球切;

                triggerList.clear();

                if ((result & TIME_CHANGED_MASK) != 0) {
                    if (DEBUG_BATCH) {
                        Slog.v(TAG, "Time changed notification from kernel; rebatching");
                    }
                    removeImpl(mTimeTickSender);
                    rebatchAllAlarms();
                    mClockReceiver.scheduleTimeTickEvent();
                    synchronized (mLock) {
                        mNumTimeChanged++;
                    }
                    Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
                    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                            | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                            | Intent.FLAG_RECEIVER_FOREGROUND);
                    getContext().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);
                        }
                    }

                    boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);

                    if (SystemProperties.getInt("sys.quickboot.enable", 0) == 1) {
                        filtQuickBootAlarms(triggerList);
                    }

                    if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
                        // if there are no wakeup alarms and the screen is off, we can
                        // delay what we have so far until the future.
                        if (mPendingNonWakeupAlarms.size() == 0) {
                            mStartCurrentDelayTime = nowELAPSED;
                            mNextNonWakeupDeliveryTime = nowELAPSED
                                    + ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
                        }
                        mPendingNonWakeupAlarms.addAll(triggerList);
                        mNumDelayedAlarms += triggerList.size();
                        rescheduleKernelAlarmsLocked();
                        updateNextAlarmClockLocked();
                    } else {
                        // now deliver the alarm intents; if there are pending non-wakeup
                        // alarms, we need to merge them in to the list.  note we don't
                        // just deliver them first because we generally want non-wakeup
                        // alarms delivered after wakeup alarms.
                        rescheduleKernelAlarmsLocked();
                        updateNextAlarmClockLocked();
                        if (mPendingNonWakeupAlarms.size() > 0) {
                            calculateDeliveryPriorities(mPendingNonWakeupAlarms);
                            triggerList.addAll(mPendingNonWakeupAlarms);
                            Collections.sort(triggerList, mAlarmDispatchComparator);
                            final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
                            mTotalDelayTime += thisDelayTime;
                            if (mMaxDelayTime < thisDelayTime) {
                                mMaxDelayTime = thisDelayTime;
                            }
                            mPendingNonWakeupAlarms.clear();
                        }
                        deliverAlarmsLocked(triggerList, nowELAPSED); //alarm 事件分發(fā)
                    }
                }
            }
        }
    }
waitForAlarm是一個native方法具體的實現(xiàn)在驅動中。如果整個系統(tǒng)中沒有alarm的時間回調绒障,
waitForAlarm則阻塞在這吨凑,直到有回調的時候才往后執(zhí)行,這樣會減少CPU的開銷户辱。

com_android_server_AlarmManagerService.cpp waitForAlarm()

int AlarmImplTimerFd::waitForAlarm()
{
    epoll_event events[N_ANDROID_TIMERFDS];

    int nevents = epoll_wait(epollfd, events, N_ANDROID_TIMERFDS, -1);//監(jiān)聽事件
    if (nevents < 0) {
        return nevents;
    }

    int result = 0;
    for (int i = 0; i < nevents; i++) {
        uint32_t alarm_idx = events[i].data.u32;
        uint64_t unused;
        ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused));
        if (err < 0) {
            if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) {
                result |= ANDROID_ALARM_TIME_CHANGE_MASK;
            } else {
                return err;
            }
        } else {
            result |= (1 << alarm_idx);
        }
    }

    return result;
}
deliverAlarmsLocked() —— AlarmThread事件中分發(fā)Alarm事件
void deliverAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED) {
        mLastAlarmDeliveryTime = nowELAPSED;
        for (int i=0; i<triggerList.size(); i++) {
            Alarm alarm = triggerList.get(i);
            try {
                if (localLOGV) {
                    Slog.v(TAG, "sending alarm " + alarm);
                }
                alarm.operation.send(getContext(), 0, //調用PendingIntent的send方法
                        mBackgroundIntent.putExtra(
                                Intent.EXTRA_ALARM_COUNT, alarm.count),
                        mResultReceiver, mHandler);

                // we have an active broadcast so stay awake.
                if (mBroadcastRefCount == 0 || !mWakeLock.isHeld()) {
                    setWakelockWorkSource(alarm.operation, alarm.workSource,
                            alarm.type, alarm.tag, true);
                    mWakeLock.acquire();
                }
                final InFlight inflight = new InFlight(AlarmManagerService.this,
                        alarm.operation, alarm.workSource, alarm.type, alarm.tag, alarm.uid);
                mInFlight.add(inflight);
                mBroadcastRefCount++;
                mTriggeredUids.add(new Integer(alarm.uid));

                final BroadcastStats bs = inflight.mBroadcastStats;
                bs.count++;
                if (bs.nesting == 0) {
                    bs.nesting = 1;
                    bs.startTime = nowELAPSED;
                } else {
                    bs.nesting++;
                }
                final FilterStats fs = inflight.mFilterStats;
                fs.count++;
                if (fs.nesting == 0) {
                    fs.nesting = 1;
                    fs.startTime = nowELAPSED;
                } else {
                    fs.nesting++;
                }
                if (alarm.type == ELAPSED_REALTIME_WAKEUP
                        || alarm.type == RTC_WAKEUP
                        || alarm.type == RTC_POWEROFF_WAKEUP) {
                    bs.numWakeup++;
                    fs.numWakeup++;
                    if (alarm.workSource != null && alarm.workSource.size() > 0) {
                        for (int wi=0; wi<alarm.workSource.size(); wi++) {
                            ActivityManagerNative.noteWakeupAlarm(
                                    alarm.operation, alarm.workSource.get(wi),
                                    alarm.workSource.getName(wi));
                        }
                    } else {
                        ActivityManagerNative.noteWakeupAlarm(
                                alarm.operation, -1, null);
                    }
                }
            } catch (PendingIntent.CanceledException e) {
                if (alarm.repeatInterval > 0) {
                    // This IntentSender is no longer valid, but this
                    // is a repeating alarm, so toss the hoser.
                    removeImpl(alarm.operation);
                }
            } catch (RuntimeException e) {
                Slog.w(TAG, "Failure sending alarm.", e);
            }
        }
    }

PendingIntent.java

public void send(Context context, int code, @Nullable Intent intent,
        @Nullable OnFinished onFinished, @Nullable Handler handler,
        @Nullable String requiredPermission, @Nullable Bundle options)
        throws CanceledException {
    try {
        String resolvedType = intent != null ?
                intent.resolveTypeIfNeeded(context.getContentResolver())
                : null;
        int res = mTarget.send(code, intent, resolvedType, //////////mTarget 是PendingIntentRecord
                onFinished != null
                        ? new FinishedDispatcher(this, onFinished, handler)
                        : null,
                requiredPermission, options);
        ...
    } catch (RemoteException e) {
        throw new CanceledException(e);
    }
}

mTarget 是什么呢鸵钝??庐镐?
我們前面講述AlarmManger使用時講述會通過PendingIntent.getActivity或者getService吧恩商;
PendingIntent 常用的幾個靜態(tài)方法:

PendingIntent.getActivity
PendingIntent.getService
PendingIntent.getBroadcast
PendingIntent.getBroadcastAsUser

mTarget就是在上述方法中創(chuàng)建PendingIntent時創(chuàng)建的。上述幾個方法最終都會調用ActivityManagerService的getIntentSender方法

public static PendingIntent getService(Context context, int requestCode,
        Intent intent,  int flags) {
   String packageName = context.getPackageName();
   String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
           context.getContentResolver()) : null;
   try {
       intent.prepareToLeaveProcess();
       IIntentSender target =
           ActivityManagerNative.getDefault().getIntentSender( // ActivityManagerNative.getDefault()獲取的是
//AMS的代理ActivityMangerProxy必逆,binder call 調用AMS的相關方法
               ActivityManager.INTENT_SENDER_SERVICE, packageName,
               null, null, requestCode, new Intent[] { intent },
               resolvedType != null ? new String[] { resolvedType } : null,
               flags, null, UserHandle.myUserId());
       return target != null ? new PendingIntent(target) : null;
   } catch (RemoteException e) {
   }
   return null;
}

ActivityMangerService.java的 getIntentSender()獲取的是PendingIntentRecord對象, 而該對象繼承于IIntentSender.Stub怠堪, 經(jīng)過binder call回來, 所以此處target是指PendingIntentRecord對象的代理端, 即為PendingIntent.mTarget,所以上述最終會調用PendingIntentRecord的send方法名眉;具體的喚起Activity,Service等具體業(yè)務就是在這個send 方法中完成粟矿;

下面完整看一下AlarmManager 的set接口操作過程;

AlarmManager.java

    public void set(int type, long triggerAtMillis, PendingIntent operation) {
        setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null, null);
    }

    private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
            PendingIntent operation, 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;
        }

        try {
            mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
                    workSource, alarmClock); //這里的mService就是前面的AlarmManagerService中的mService對象损拢。
        } catch (RemoteException ex) {
        }
    }
    //AlarmManager 類的構造函數(shù)
    AlarmManager(IAlarmManager service, Context ctx) {
        mService = service;

        final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
        mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
    }

ContextImpl.java
AlarmManager對象的獲饶按狻:(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

    @Override
    public Object getSystemService(String name) {
        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
        return fetcher == null ? null : fetcher.getService(this);
    }   

 private static void registerService(String serviceName, ServiceFetcher fetcher) {
        if (!(fetcher instanceof StaticServiceFetcher)) {
            fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
        }
        SYSTEM_SERVICE_MAP.put(serviceName, fetcher); //添加service到SYSTEM_SERVICE_MAP
    }

 registerService(ALARM_SERVICE, new ServiceFetcher() { //注冊
                  public Object createService(ContextImpl ctx) {
                      IBinder b = ServiceManager.getService(ALARM_SERVICE);
                      IAlarmManager service = IAlarmManager.Stub.asInterface(b);
                      return new AlarmManager(service, ctx);
                }});   


    /*package*/ static class ServiceFetcher {
        int mContextCacheIndex = -1;

        /**
         * Main entrypoint; only override if you don't need caching.
         */
        public Object getService(ContextImpl ctx) {
            ArrayList<Object> cache = ctx.mServiceCache;
            Object service;
            synchronized (cache) {
                if (cache.size() == 0) {
                    // Initialize the cache vector on first access.
                    // At this point sNextPerContextServiceCacheIndex
                    // is the number of potential services that are
                    // cached per-Context.
                    for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) {
                        cache.add(null);
                    }
                } else {
                    service = cache.get(mContextCacheIndex);
                    if (service != null) {
                        return service;
                    }
                }
                service = createService(ctx);
                cache.set(mContextCacheIndex, service);
                return service;
            }
        }

        /**
         * Override this to create a new per-Context instance of the
         * service.  getService() will handle locking and caching.
         */
        public Object createService(ContextImpl ctx) {
            throw new RuntimeException("Not implemented");
        }
    }
        

所以getSystemService中返回的就是AlarmManagerService 中mService 的代理;即AlarmManager中mService變量即是AlarmManagerService中mService的代理福压;AlarmManager中方法最終都是有AlarmManagerService的mService處理

關于上述AlarmManager中set方法的處理掏秩,調用

    mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
                    workSource, alarmClock); //這里的mService就是前面的AlarmManagerService中的mService對象。

Binder Call 到AlarmMangerService中
本段代碼是截取Android 5.1源碼荆姆;

        @Override
        public void set(int type, long triggerAtTime, long windowLength, long interval,
                PendingIntent operation, WorkSource workSource,
                AlarmManager.AlarmClockInfo alarmClock) {
            if (workSource != null) {
                getContext().enforceCallingPermission(
                        android.Manifest.permission.UPDATE_DEVICE_STATS,
                        "AlarmManager.set");
            }

            setImpl(type, triggerAtTime, windowLength, interval, operation,
                    windowLength == AlarmManager.WINDOW_EXACT, workSource, alarmClock);
        }


void setImpl(int type, long triggerAtTime, long windowLength, long interval,
            PendingIntent operation, boolean isStandalone, WorkSource workSource,
            AlarmManager.AlarmClockInfo alarmClock) {
        if (operation == null) {
            Slog.w(TAG, "set/setRepeating ignored because there is no intent");
            return;
        }

        // Sanity check the window length.  This will catch people mistakenly
        // trying to pass an end-of-window timestamp rather than a duration.
        if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
            Slog.w(TAG, "Window length " + windowLength
                    + "ms suspiciously long; limiting to 1 hour");
            windowLength = AlarmManager.INTERVAL_HOUR;
        }

        // Sanity check the recurrence interval.  This will catch people who supply
        // seconds when the API expects milliseconds.
        if (interval > 0 && interval < MIN_INTERVAL) {
            Slog.w(TAG, "Suspiciously short interval " + interval
                    + " millis; expanding to " + (int)(MIN_INTERVAL/1000)
                    + " seconds");
            interval = MIN_INTERVAL;
        }

        if (type < RTC_WAKEUP || type > RTC_POWEROFF_WAKEUP) {
            throw new IllegalArgumentException("Invalid alarm type " + type);
        }

        if (triggerAtTime < 0) {
            final long who = Binder.getCallingUid();
            final long what = Binder.getCallingPid();
            Slog.w(TAG, "Invalid alarm trigger time! " + triggerAtTime + " from uid=" + who
                    + " pid=" + what);
            triggerAtTime = 0;
        }

        final long nowElapsed = SystemClock.elapsedRealtime();
        final long nominalTrigger = convertToElapsed(triggerAtTime, type);
        // Try to prevent spamming by making sure we aren't firing alarms in the immediate future
        final long minTrigger = nowElapsed + 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);
        } else {
            maxElapsed = triggerElapsed + windowLength;
        }

        final int userId = UserHandle.getCallingUserId();

        synchronized (mLock) {
            if (DEBUG_BATCH) {
                Slog.v(TAG, "set(" + operation + ") : type=" + type
                        + " triggerAtTime=" + triggerAtTime + " win=" + windowLength
                        + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
                        + " interval=" + interval + " standalone=" + isStandalone);
            }
            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
                    interval, operation, isStandalone, true, workSource, alarmClock, userId);
        }
    }

private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
            long maxWhen, long interval, PendingIntent operation, boolean isStandalone,
            boolean doValidate, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
            int userId) {
        Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
                operation, workSource, alarmClock, userId);
        removeLocked(operation);

        int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);
        if (whichBatch < 0) {
            Batch batch = new Batch(a);
            batch.standalone = isStandalone;
            addBatchLocked(mAlarmBatches, batch);
        } else {
            Batch batch = mAlarmBatches.get(whichBatch);
            if (batch.add(a)) {
                // The start time of this batch advanced, so batch ordering may
                // have just been broken.  Move it to where it now belongs.
                mAlarmBatches.remove(whichBatch);
                addBatchLocked(mAlarmBatches, batch);
            }
        }

        if (alarmClock != null) {
            mNextAlarmClockMayChange = true;
            updateNextAlarmClockLocked();
        }

        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);
            }
        }

        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.
        long nextNonWakeup = 0;
        if (mAlarmBatches.size() > 0) {
            final Batch firstWakeup = findFirstWakeupBatchLocked();
            final Batch firstBatch = mAlarmBatches.get(0);
            final Batch firstRtcWakeup = findFirstRtcWakeupBatchLocked();

            // always update the kernel alarms, as a backstop against missed wakeups
            if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
                mNextWakeup = firstWakeup.start;
                setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);//調用方法
            }
            if (firstRtcWakeup != null && mNextRtcWakeup != firstRtcWakeup.start) {
                mNextRtcWakeup = firstRtcWakeup.start;
                long when = firstRtcWakeup.getWhenByElapsedTime(mNextRtcWakeup);

                if (when != 0) {
                    setLocked(RTC_POWEROFF_WAKEUP, when);
                }
            }
            if (firstBatch != firstWakeup) {
                nextNonWakeup = firstBatch.start;
            }
        }
        if (mPendingNonWakeupAlarms.size() > 0) {
            if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) {
                nextNonWakeup = mNextNonWakeupDeliveryTime;
            }
        }
        // always update the kernel alarm, as a backstop against missed wakeups
        if (nextNonWakeup != 0) {
            mNextNonWakeup = nextNonWakeup;
            setLocked(ELAPSED_REALTIME, nextNonWakeup); //
        }
    }

private void setLocked(int type, long when) {
        if (mNativeData != 0) {
            // The kernel never triggers alarms with negative wakeup times
            // so we ensure they are positive.
            long alarmSeconds, alarmNanoseconds;
            if (when < 0) {
                alarmSeconds = 0;
                alarmNanoseconds = 0;
            } else {
                alarmSeconds = when / 1000;
                alarmNanoseconds = (when % 1000) * 1000 * 1000;
            }
            
            set(mNativeData, type, alarmSeconds, alarmNanoseconds); //native  JNI call蒙幻,調用JN native 方法
        } else {
            Message msg = Message.obtain();
            msg.what = ALARM_EVENT;
            
            mHandler.removeMessages(ALARM_EVENT);
            mHandler.sendMessageAtTime(msg, when);
        }
    }

com_android_server_AlarmManagerService.cpp

static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type,
 jlong seconds, jlong nanoseconds)
{
    AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); //從java傳遞的nativeData,
    //AlarmMangerService中的nativeData 實際是該JNI init() 的返回值胞枕;此時返回的是AlarmImplAlarmDriver
    struct timespec ts;
    ts.tv_sec = seconds;
    ts.tv_nsec = nanoseconds;

    int result = impl->set(type, &ts);//AlarmImplAlarmDriver->set
    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));
    }
}

static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject)
{
    jlong ret = init_alarm_driver();//即操作Alarm device——"/dev/alarm"
    if (ret) {
        return ret; 
    }

    return init_timerfd();//如果沒有Alarm device杆煞,繼續(xù)執(zhí)行
}

int AlarmImplAlarmDriver::set(int type, struct timespec *ts)
{
    return ioctl(fds[0], ANDROID_ALARM_SET(type), ts);// 最終ioctl操作硬件
}

最終通過ioctl的方式將時間設置給驅動,后續(xù)驅動不做詳解,我也不清楚决乎,
目的就是把時間設置給驅動队询,等到硬件中斷返回后,再回調到native層构诚,native再回調到framework蚌斩,
如何回調到framework的呢?范嘱?
我們上述說的AlarmThread送膳,在run()方法中開啟一個無限循環(huán),循環(huán)中會調用

int result = waitForAlarm(mNativeData); //JNI 方法丑蛤,使用EPOLL監(jiān)聽FD叠聋,阻塞等待驅動返回執(zhí)行下面的分發(fā)執(zhí)行;

waitForAlarm是一個native方法受裹,具體的實現(xiàn)在驅動中碌补。如果整個系統(tǒng)中沒有alarm的時間回調,waitForAlarm則阻塞在這棉饶,直到有回調的時候才往后執(zhí)行厦章,才會繼續(xù)執(zhí)行上述的deliverAlarmsLocked()方法,才會去執(zhí)行相應的send方法喚醒activity等照藻。

一個完整的過程就是操作"/dev/alarm"設備的過程袜啃。自此一個完整的操作過程就結束了;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末幸缕,一起剝皮案震驚了整個濱河市群发,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冀值,老刑警劉巖也物,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異列疗,居然都是意外死亡滑蚯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門抵栈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來告材,“玉大人,你說我怎么就攤上這事古劲〕飧常” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵产艾,是天一觀的道長疤剑。 經(jīng)常有香客問我滑绒,道長,這世上最難降的妖魔是什么隘膘? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任疑故,我火速辦了婚禮,結果婚禮上弯菊,老公的妹妹穿的比我還像新娘纵势。我一直安慰自己,他們只是感情好管钳,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布钦铁。 她就那樣靜靜地躺著,像睡著了一般才漆。 火紅的嫁衣襯著肌膚如雪牛曹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天醇滥,我揣著相機與錄音躏仇,去河邊找鬼。 笑死腺办,一個胖子當著我的面吹牛,可吹牛的內容都是我干的糟描。 我是一名探鬼主播怀喉,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼船响!你這毒婦竟也來了躬拢?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤见间,失蹤者是張志新(化名)和其女友劉穎聊闯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體米诉,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡菱蔬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了史侣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拴泌。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖惊橱,靈堂內的尸體忽然破棺而出蚪腐,到底是詐尸還是另有隱情,我是刑警寧澤税朴,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布回季,位于F島的核電站家制,受9級特大地震影響,放射性物質發(fā)生泄漏泡一。R本人自食惡果不足惜颤殴,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘾杭。 院中可真熱鬧诅病,春花似錦、人聲如沸粥烁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讨阻。三九已至芥永,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钝吮,已是汗流浹背埋涧。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奇瘦,地道東北人棘催。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像耳标,于是被迫代替她去往敵國和親醇坝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容