zoukankan      html  css  js  c++  java
  • Android4.4之后休眠状态下Alarm不准时的问题

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

    为了减轻功耗,延长电池使用时间。Android 4.4及之后的版本号採用非精准闹钟机制。以及休眠状态下的wakeup类型的alarm不会实时唤醒设备,而会等到机器被物理唤醒时才触发alarmAndroid 6.0提供了新的api:setExactAndAllowWhileIdle()部分解决问题,但依旧不能在休眠状态下精准唤醒。

    关于alarm api 的支持与使用请參考下图:
    alarm api 的支持与使用
    (图片来源:https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo)

    此外,应用层面不要使用不持有wakelockBroadcastReceiver,而要使用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);
          }
      }
  • 相关阅读:
    git 常用命令
    mac 显示隐藏文件
    android 图片缓存
    字符串与枚举相互转换
    ios 消息通知
    ios 真机调试
    ios 宏定义 系统版本 判定
    autolayout autoresizing
    c++对象创建带括号与无括号的区别
    内存对齐
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/7306227.html
Copyright © 2011-2022 走看看