您的位置:首页 > 移动开发 > Android开发

Android4.4之后休眠状态下Alarm不准时的问题

2017-08-08 12:32 357 查看

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 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);
    }
    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐