Android4.4之後休眠狀態下Alarm不準時的問題
阿新 • • 發佈:2017-08-08
schedule hid ext div compute .net als 解決問題 per
的
Android4.4及之後休眠狀態下Alarm不準時的問題
為了減輕功耗,延長電池使用時間。Android 4.4
及之後的版本號採用非精準鬧鐘機制。以及休眠狀態下的wakeup
類型的alarm
不會實時喚醒設備,而會等到機器被物理喚醒時才觸發alarm
。Android 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
並在
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不準時的問題