Android如何让应用在待机休眠的维持心跳运行

Android省电机制

从 Android 6.0(API 级别 23)开始,Android 引入了两项省电功能,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 CPU 和网络活动,从而降低耗电量。应用待机模式会延迟用户近期未与之交互的应用的后台网络活动。

  1. CPU 休眠:设备的中央处理器 (CPU) 进入休眠状态,停止运行大部分任务和进程。这包括暂停应用程序的代码执行、系统服务的运行以及大部分的设备硬件操作。
  2. 屏幕关闭:设备的屏幕会关闭,进入省电模式。这有助于降低屏幕背光和其他相关硬件的能耗。
  3. 网络断开:通常情况下,当设备进入休眠状态时,会断开与移动网络和 Wi-Fi 网络的连接,以减少网络访问的功耗。

1. 如何让CPU不休眠

通过WakeLock 唤醒锁

WakeLock 是一种 Android 中的机制,允许应用程序保持设备唤醒状态,以防止 CPU 和屏幕进入休眠。WakeLock 主要用于在需要时确保设备保持唤醒状态,以执行特定的任务。以下是 WakeLock 的使用方法:

PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
wakeLock.release();

WakeLock 类提供了不同类型的锁,以满足不同的需求和场景。在 Android 中,常见的 WakeLock 类型包括以下几种:

  1. PARTIAL_WAKE_LOCK:部分唤醒锁
    • 使用 PowerManager.PARTIAL_WAKE_LOCK 标志创建。
    • 当你希望保持 CPU 唤醒而同时允许屏幕关闭时,可以使用这种类型的锁。
  2. SCREEN_DIM_WAKE_LOCK:屏幕暗亮唤醒锁
    • 使用 PowerManager.SCREEN_DIM_WAKE_LOCK 标志创建。
    • 当你需要保持屏幕处于暗亮状态(比全亮稍暗)同时保持 CPU 唤醒时,可以使用这种类型的锁。
  3. SCREEN_BRIGHT_WAKE_LOCK:屏幕全亮唤醒锁
    • 使用 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 标志创建。
    • 当你需要保持屏幕全亮同时保持 CPU 唤醒时,可以使用这种类型的锁。
  4. FULL_WAKE_LOCK:全唤醒锁
    • 使用 PowerManager.FULL_WAKE_LOCK 标志创建。
    • 当你希望保持 CPU 和屏幕都保持唤醒状态时,可以使用这种类型的锁。
  5. PROXIMITY_SCREEN_OFF_WAKE_LOCK:靠近感应器唤醒锁
    • 使用 PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 标志创建。
    • 当你希望在设备靠近感应器时关闭屏幕,同时保持 CPU 唤醒时,可以使用这种类型的锁。
      当我们想要应用在锁屏待机状态继续运行时可以使用 PowerManager.PARTIAL_WAKE_LOCK 来保持设备唤醒状态

通过AlarmManager进行定时唤醒并解锁屏幕

AlarmManager的使用

AlarmManager 是 Android 中用于在指定时间触发后台操作的类。它允许你安排在未来的某个时间点或以固定间隔触发某个操作。以下是使用 AlarmManager 的基本步骤:

  1. 获取 AlarmManager 对象:

    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    

    通过调用 getSystemService() 方法,传入 Context.ALARM_SERVICE 参数获取 AlarmManager 的实例。

  2. 创建 PendingIntent:

    Intent intent = new Intent(this, MyReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    

    在上述代码中,我们创建了一个 Intent 对象,指定了接收闹钟触发事件的广播接收器(MyReceiver)。然后,通过调用 PendingIntent.getBroadcast() 方法创建 PendingIntent。PendingIntent.FLAG_UPDATE_CURRENT 参数表示如果已经存在相同的 PendingIntent,那么更新它的 Extra 数据。

  3. 设置闹钟:

         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
         } else {
             alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), WakeUpBroadcastReceiver.TIME_INTERVAL, pendingIntent);
         }
    

    在上述代码中,我们使用 System.currentTimeMillis() 获取当前时间,并在其基础上加上 5000 毫秒(5 秒)来计算触发时间。然后,通过调用 alarmManager.set() 方法,传入触发类型(AlarmManager.ELAPSED_REALTIME_WAKEUP 表示使用实时时钟触发,同时唤醒设备),触发时间和 PendingIntent 对象来设置闹钟。

  4. 创建广播接收器:

    public class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 处理闹钟触发事件
          Intent intent1 = new Intent(context, MyReceiver.class);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
         AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
         // 重复定时任务
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
         }
        }
    }
    

AlarmManager的唤醒类型

AlarmManager的四个唤醒类型,它可以使用以下四个常量:
1、AlarmManager.ELAPSED_REALTIME:使用相对时间,可以通过SystemClock.elapsedRealtime()获取(从开机到现在的毫秒数,包括手机的睡眠时间),设备休眠时并不会唤醒设备。
2、AlarmManager.ELAPSED_REALTIME_WAKEUP:与ELAPSED_REALTIME基本功能一样,只是会在设备休眠时唤醒设备。
3、AlarmManager.RTC:使用绝对时间,可以通过System.currentTimeMillis()获取,设备休眠时并不会唤醒设备。
4、AlarmManager.RTC_WAKEUP:与RTC基本功能一样,只是会在设备休眠时唤醒设备。

相对时间:设备boot后到当前经历的时间,SystemClock.elapsedRealtime()获取到的是相对时间。
绝对时间:1970年1月1日到当前经历的时间,System.currentTimeMillis()和Calendar.getTimeInMillis()获取到的都是绝对时间。

如果是相对时间,那么计算triggerAtMillis就需要使用SystemClock.elapsedRealtime();
如果是绝对时间,那么计算triggerAtMillis就需要使用System.currentTimeMillis()或者calendar.getTimeInMillis()。

以上是使用 AlarmManager 在不同Android版本使用的适配,这样可以实现一个定时任务。

存在的问题

但是在华为平板上测试存在的问题:
1.当手机连接usb电源时,息屏不会造成定时器暂停运行,而且时间间隔准确
2.当手机未连接usb电源时,息屏会造成定时器时间间隔不准,设置为30秒的间隔可能会4分钟或者10分钟再执行,并在再次点亮屏幕时定时器又正常。

为啥不用Timer或者handler执行定时任务

可以参考下这个Handler休眠失效

通过JobScheduler定时唤醒

JobScheduler是在Android 5.0添加的,它可以检测网络状态、设备是否充电中、低电量、低存储等状态,当所有条件都满足时就会触发执行对应的JobService来完成任务。

JobScheduler使用

  1. 创建 JobService 类:

    public class MyJobService extends JobService {
        @Override
        public boolean onStartJob(JobParameters params) {
            // 在这里执行后台任务
            return true; // 如果任务是异步的,则返回 true;如果任务是同步的,则返回 false
        }
    
        @Override
        public boolean onStopJob(JobParameters params) {
            // 在这里处理任务停止的逻辑(例如重试机制)
            return true; // 返回 true 以重新安排该作业,返回 false 以放弃该作业
        }
    }
    

    在上述代码中,我们创建了一个继承自 JobService 的类(MyJobService),并重写了 onStartJob()onStopJob() 方法。在 onStartJob() 方法中,你可以执行后台任务的逻辑。如果任务是异步的,记得返回 true,以指示系统该任务正在进行中。如果任务是同步的,则返回 false。在 onStopJob() 方法中,你可以处理任务停止的逻辑(例如重试机制)。

  2. 创建 JobInfo 对象:

		   JobInfo.Builder builder = new JobInfo.Builder(jobId, new ComponentName(this, MyJobService.class));
           builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); // 设置所需的网络类型
           builder.setPersisted(true); // 设置任务持久化,即在设备重启后仍然有效
           //android7.0 及以上系统
           if (Build.VERSION.SDK_INT >= 24) {
              builder.setMinimumLatency(3000) //至少3s 才会执行
              //builder.setOverrideDeadline(3000) //3s后一定会执行
              //builder.setMinimumLatency(3000)
              //默认是10s,如果小于10s,也会按照10s来依次增加
              //builder.setBackoffCriteria(10000, JobInfo.BACKOFF_POLICY_LINEAR);
        } else {
              builder.setPeriodic(5000)
        }
  
   // 设置其他参数和约束条件
   JobInfo jobInfo = builder.build();

在上述代码中,我们使用 JobInfo.Builder 创建一个 JobInfo 对象。jobId 是唯一标识作业的整数值。new ComponentName(this, MyJobService.class) 指定了执行作业的 JobService 类。通过 setRequiredNetworkType() 方法,我们可以设置作业所需的网络类型(如 JobInfo.NETWORK_TYPE_ANY 表示任何网络都可以)。setPersisted(true) 设置任务持久化,即在设备重启后仍然有效。

你还可以使用其他方法设置作业的约束条件,例如 setRequiresCharging()(要求设备在充电时才执行作业)或 setRequiresDeviceIdle()(要求设备处于空闲状态时才执行作业)等。
如果想要周期执行的话7.0 及以上系统通过setMinimumLatency方法并在执行onStartJob重新创建job,其他则使用setPeriodic方法。

  1. 调度作业:
    JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
    int result = jobScheduler.schedule(jobInfo);
    if (result == JobScheduler.RESULT_SUCCESS) {
        // 作业调度成功
    }
    

存在的问题

在华为平板测试时发现:
1.setPeriodic最低时间间隔为15分钟,低于15分钟不起作用
2.锁屏休眠时JobScheduler 不执行

避免电池优化白名单申请

在低电耗模式和应用待机模式期间,列入白名单的应用可以使用网络并保留部分唤醒锁定。不过,列入白名单的应用仍会受到其他限制,就像其他应用一样。例如,列入白名单的应用的作业和同步会延迟(在搭载 API 级别 23 及更低级别的设备上),并且其常规 AlarmManager 闹钟不会触发。应用可以调用 isIgnoringBatteryOptimizations() 来检查它当前是否在豁免白名单中。

  1. 添加权限
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
  1. 判断并申请加入白名单
    public static void isIgnoreBatteryOption(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            try {
                Intent intent = new Intent();
                String packageName = context.getPackageName();
                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
                if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                    intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                    intent.setData(Uri.parse("package:" + packageName));
                    context.startActivity(intent);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

通过WifiLock锁定Wi-Fi 来保持设备的 Wi-Fi 连接处于活动状态

在 Android 设备上,默认情况下,当设备进入休眠状态时,网络连接会自动断开以节省电池。然而,如果你需要在设备休眠期间保持网络连接,可以尝试以下方法:
在 Android 设备上,默认情况下,当设备进入休眠状态时,网络连接会自动断开以节省电池。然而,如果你需要在设备休眠期间保持网络连接,可以尝试以下方法:

  1. 使用 Wi-Fi 锁定来保持设备的 Wi-Fi 连接处于活动状态。这样可以防止设备在休眠时断开 Wi-Fi 连接。
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiManager.WifiLock wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "MyWifiLockTag");
wifiLock.acquire();
  1. 创建一个后台服务,在其中保持网络连接活动。后台服务可以在设备休眠时保持网络连接,以便持续进行网络操作。
public class MyBackgroundService extends Service {

    private WifiManager.WifiLock wifiLock;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "MyWifiLockTag");
        wifiLock.acquire();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (wifiLock != null && wifiLock.isHeld()) {
            wifiLock.release();
            wifiLock = null;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

具体效果未测试

华为平板最终使用的方案

使用场景,需求是在设备休眠时可以继续访问网络,并维持一个与服务器的长连接。
1.通过WackLock获取PARTIAL_WAKE_LOCK锁,让应用在锁屏时CPU不休眠。
2.通过AlarmManager定时检测长连接是否断
3.避免电池优化白名单申请
4. 通过WifiLock锁定Wi-Fi 来保持设备的 Wi-Fi 连接处于活动状态
5. 去掉华为的自动管理改成手动管理,打开wlan锁屏自动断开的开关(这比较重要)

参考

AlarmManager使用
针对低电耗模式和应用待机模式进行优化
Android休眠机制

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Linux操作系统中的待机休眠和睡眠是三种不同的电源管理模式。它们的区别主要在于系统的状态和功耗。 待机模式是指将电脑关闭显示屏、硬盘等不必要的设备,进入低功耗状态,但仍然保留内存中的数据和运行状态。此时,CPU处于较低的频率和电压,节省了电能,但系统仍处于活动状态,可以快速恢复到之前的工作状态。 休眠模式又称为挂起模式,是将系统状态保存到硬盘上,除了CPU之外的其他硬件设备都被关闭。在休眠模式下,将所有内存中的数据存储到磁盘上,CPU停止运行,并降低系统的功耗。通过这种模式,可以关闭电源而不丢失未保存的工作,系统可以在恢复时恢复到以前的状态。 睡眠模式是指将操作系统和硬件设备都置于低功耗状态,关闭显示屏、硬盘,同时将内存中的数据保留在RAM中。在睡眠模式下,系统会进一步降低功耗,但仍然保持部分硬件设备(如内存)的供电。通过这种模式,可以快速恢复到之前的状态,但它相比于休眠模式消耗的电源更多。 总的来说,待机模式是系统仍然保持活动状态,不会保存当前状态;休眠模式是将状态保存到硬盘上,电源关闭,可以快速恢复;睡眠模式是将状态保存在内存中,保持部分硬件供电且功耗更低,也可以快速恢复。选择适合自己需求和电源管理的模式可以更有效地管理电脑的电能消耗。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值