Android Framework : Alarm 機制

samay發表於2017-09-25

Alarm是什麼?

Alarm可以理解為系統鬧鐘。系統通過採用該種機制來實現系統在未來某個時間點執行某種功能。系統中Alarm通過AlarmManagerService管理系統中的Alarm。

        Intent intent = new Intent();
        intent.setAction(Constatnt.AUTO_CLEAN_BROADCAST);
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
        //5分鐘之後執行該Alarm
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+5*Constatnt.ONE_MINUTE, pendingIntent);複製程式碼

Alarm 設定流程

如下流程圖,描述了Alarm簡單的設定流程

AlarmManagerService是如何管理Alarm的呢?

Android官方文件指出:從Android4.4開始,Alarm預設採用的是非準確模式(該種該模式的目的是減少wakeup以及降低電量使用).在AlarmManagerService中,每一個Alarm根據其
觸發時間和最大觸發時間存放在不同的batch。在典型的情況下:最大觸發時間 = 觸發時間 + 時間視窗。在Batch內部所有的batch是按開始
時間升序排列,所有的Alarm喚醒順序按照batch的排列順序觸發,同一個batch的不同alarm是同時發生的。示意圖如下

程式碼描述入下:

    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傳入的值構建一個Alarm
        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);
    }


    private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
        ..........
            //根據Alarm屬性獲取對應的Batch,如果沒有返回-1;否則獲得對應的位置
            int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)
                    ? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed);
            if (whichBatch < 0) {
                //如果沒有對應batch,構建一個新的batch.
                Batch batch = new Batch(a);
                //將這個Batch加入到Batch List
                addBatchLocked(mAlarmBatches, batch);
            } else {
                //獲得對應的Batch
                Batch batch = mAlarmBatches.get(whichBatch);
                //將Alarm新增到Batch中
                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);
                }
            }
        ........
        }複製程式碼

Alarm如果被喚醒,如何執行相關的操作呢?

AlarmManagerService中有一個AlarmThread,該執行緒在該Service啟動後就一直執行。該執行緒不停的遍歷Batch列表,檢視是否有Alarm需要執行(該流程類似於Loop與MessageQueue)。
流程圖如下

參考資料

[1]blog.csdn.net/wh_19910525…

相關文章