1. 程式人生 > >Android4.4之後休眠狀態下Alarm不準時的問題

Android4.4之後休眠狀態下Alarm不準時的問題

schedule hid ext div compute .net als 解決問題 per

Android4.4及之後休眠狀態下Alarm不準時的問題

為了減輕功耗,延長電池使用時間。Android 4.4及之後的版本號採用非精準鬧鐘機制。以及休眠狀態下的wakeup類型的alarm不會實時喚醒設備,而會等到機器被物理喚醒時才觸發alarmAndroid 6.0提供了新的api:setExactAndAllowWhileIdle()部分解決問題,但依舊不能在休眠狀態下精準喚醒。

關於alarm api 的支持與使用請參考下圖:
技術分享
(圖片來源:https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo)

此外,應用層面不要使用不持有wakelock

BroadcastReceiver,而要使用WakefulBroadcastReceiver

為了修復這個問題。以5.0.2版本號為例,須要改動內核下的alarm-dev.c以及framework下的AlarmManagerService。

  • 內核 alarm-dev.c:其原因是使用普通的static struct wakeup_source alarm_wake_lock;,而非帶有WAKE_LOCK_SUSPEND類別信息的struct wake_lock,而且須要使用帶有android_前綴的wakeup lock相關函數。

    即:

    android_wake_lock_init
    android_wake_lock_destroy
    android_wake_lock
    android_wake_lock_timeout
    android_wake_unlock

    而非普通的wake lock操作函數:

    wake_lock_init
    wake_lock_destroy
    wake_lock
    wake_lock_timeout
    wake_unlock
  • framework AlarmManagerService.java:須要將wakeup類型的alarm特殊處理:即精準鬧鈴。在setImpl中加入例如以下代碼:

    public boolean isWakeup(int type)
    {
        return (type & TYPE_NONWAKEUP_MASK) == 0;
    }
    
    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; } if (isWakeup(type)) { windowLength = AlarmManager.WINDOW_EXACT; isStandalone = true; }

    並在alarm被觸發時多取幾個滿足條件的batch做處理:

    boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
            final long nowRTC) {
        boolean hasWakeup = false;
        // batches are temporally sorted, so we need only pull from the
        // start of the list until we either empty it or hit a batch
        // that is not yet deliverable
    
        ArrayList<Alarm> repeatList = new ArrayList<Alarm>();
        ListIterator<Batch> it = mAlarmBatches.listIterator();
        while (it.hasNext()) {
            Batch batch = it.next();
            if (batch.start > nowELAPSED) {
                // Everything else is scheduled for the future
                break;
            }
    
            // We will (re)schedule some alarms now; don‘t let that interfere
            // with delivery of this current batch
            it.remove();
    
            final int N = batch.size();
            for (int i = 0; i < N; i++) {
                Alarm alarm = batch.get(i);
                alarm.count = 1;
                triggerList.add(alarm);
    
                // Recurring alarms may have passed several alarm intervals while the
                // phone was asleep or off, so pass a trigger count when sending them.
                if (alarm.repeatInterval > 0) {
                    // this adjustment will be zero if we‘re late by
                    // less than one full repeat interval
                    alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval;
    
                    // Also schedule its next recurrence
                    repeatList.add(alarm);
                }
    
                if (alarm.wakeup) {
                    hasWakeup = true;
                    mNextWakeup = 0;
                }
    
                // We removed an alarm clock. Let the caller recompute the next alarm clock.
                if (alarm.alarmClock != null) {
                    mNextAlarmClockMayChange = true;
                }
            }
        }
    
        if (repeatList.size() > 0) {
            for (Alarm alarm : repeatList) {
                final long delta = alarm.count * alarm.repeatInterval;
                final long nextElapsed = alarm.whenElapsed + delta;
                setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                        maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                        alarm.repeatInterval, alarm.operation, alarm.windowLength == AlarmManager.WINDOW_EXACT, true,
                        alarm.workSource, alarm.alarmClock, alarm.userId);
            }
        }
    
        // This is a new alarm delivery set; bump the sequence number to indicate that
        // all apps‘ alarm delivery classes should be recalculated.
        mCurrentSeq++;
        calculateDeliveryPriorities(triggerList);
        Collections.sort(triggerList, mAlarmDispatchComparator);
    
        return hasWakeup;
    }
  • 應用層面使用WakefulBroadcastReceiver:

    import android.support.v4.content.WakefulBroadcastReceiver;
    
    public class AutoUpdateAlarmReceiver extends WakefulBroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // Start the service, keeping the device awake while the service is
            // launching. This is the Intent to deliver to the service.
            Intent service = new Intent(context, AutoUpdateIntentService.class);
            service.setAction(intent.getAction());
            startWakefulService(context, service);
        }
    }

    對應的IntentService例如以下所看到的:

    public class AutoUpdateIntentService extends IntentService {
        public AutoUpdateIntentService() {
            super("AutoUpdateIntentService");
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            String action = intent.getAction();
    
            // do your work here.
            // ...
    
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            AutoUpdateAlarmReceiver.completeWakefulIntent(intent);
        }
    }

Android4.4之後休眠狀態下Alarm不準時的問題