使设备保持唤醒状态

为避免消耗电池电量,处于空闲状态的 Android 设备会快速进入休眠模式。不过,有时应用需要唤醒屏幕或 CPU 并使之保持唤醒状态,以完成某项工作。

所采用的方法取决于应用的需求。但是,一般而言,您应该为应用使用尽可能轻量的方法,以便最大限度减少应用对系统资源的影响。以下几个部分介绍了如何处理设备的默认休眠行为不符合应用要求的情况。

使用唤醒锁定的替代方案

在为您的应用添加唤醒锁定支持之前,请考虑应用的使用情形是否支持以下某种替代解决方案:

  • 如果您的应用执行长时间运行的 HTTP 下载,请考虑使用 DownloadManager
  • 如果您的应用同步来自外部服务器的数据,请考虑创建同步适配器
  • 如果您的应用依赖后台服务,请考虑使用 JobSchedulerFirebase 云消息传递,以便以特定的时间间隔触发这些服务。

使屏幕保持开启状态

某些应用需要使屏幕保持开启状态,例如游戏或电影应用。要实现此目标,最好的方法是在您的 Activity 中(仅在 Activity 中,切勿在服务或其他应用组件中)使用 FLAG_KEEP_SCREEN_ON。例如:

Kotlin

    class MainActivity : Activity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        }
    }
    

Java

    public class MainActivity extends Activity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
      }
    }
    

这种方法的优点是,与唤醒锁定(在使 CPU 保持运行状态部分进行讨论)不同,它不需要特殊权限,并且平台可以正确管理用户在不同应用之间的切换,您的应用无需担心释放未使用的资源。

实现此目标的另一种方法是,在应用的布局 XML 文件中,使用 android:keepScreenOn 属性:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:keepScreenOn="true">
        ...
    </RelativeLayout>

使用 android:keepScreenOn="true" 相当于使用 FLAG_KEEP_SCREEN_ON。您可以使用最适合您的应用的任意一种方法。在 Activity 中以编程方式设置标记的优势在于,您可以选择稍后以编程方式清除该标记,从而使屏幕可以关闭。

注意:除非您不再希望屏幕在正在运行的应用中保持开启状态(例如,如果您希望屏幕在处于不活动状态一段时间后超时),否则不需要清除 FLAG_KEEP_SCREEN_ON 标记。窗口管理器负责确保当应用进入后台或返回前台时,具有正确的行为。但是,如果您想要明确清除该标记,从而使屏幕再次可以关闭,请使用 clearFlags()getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

使 CPU 保持运行状态

如果您需要使 CPU 保持运行状态,以便在设备进入休眠模式之前完成某项工作,则可以使用一项称为“唤醒锁定”的 PowerManager 系统服务功能。唤醒锁定可使应用控制主机设备的电源状态。

创建和持有唤醒锁定会对主机设备的电池续航时间产生重大影响。因此,您应仅在绝对必要时使用唤醒锁定,并持有尽可能短的时间。例如,您绝不需要在 Activity 中使用唤醒锁定。如上所述,如果您希望屏幕在 Activity 中保持开启状态,请使用 FLAG_KEEP_SCREEN_ON

使用唤醒锁定的一种合理情形是,某项后台服务需要获取唤醒锁定,以便 CPU 在屏幕关闭时保持运行状态,可以完成相关工作。再次声明,由于这种做法会影响电池续航时间,因此应尽量减少其使用频率。

要使用唤醒锁定,首先要将 WAKE_LOCK 权限添加到应用的清单文件中:

<uses-permission android:name="android.permission.WAKE_LOCK" />

如果您的应用包含使用服务的广播接收器来完成相关工作,则您可以通过 WakefulBroadcastReceiver 管理唤醒锁定,如使用可使设备保持唤醒状态的广播接收器部分中所述。这是首选方法。如果您的应用未采用该模式,则您可以使用以下方法直接设置唤醒锁定:

Kotlin

    val wakeLock: PowerManager.WakeLock =
            (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
                    acquire()
                }
            }
    

Java

    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "MyApp::MyWakelockTag");
    wakeLock.acquire();
    

要释放唤醒锁定,请调用 wakelock.release()。这会释放您对 CPU 的声明。请务必在应用结束对唤醒锁定的使用后立即将其释放,以避免消耗电池电量。

使用可使设备保持唤醒状态的广播接收器

通过将广播接收器与服务结合使用,您可以管理后台任务的生命周期。

WakefulBroadcastReceiver 是一种特殊类型的广播接收器,负责为您的应用创建和管理 PARTIAL_WAKE_LOCKWakefulBroadcastReceiver 将工作看作是 Service(通常为 IntentService),同时确保设备不会在转换期间重新进入休眠模式。如果您未在将工作转换为服务的过程中持有唤醒锁定,那么您实际上允许设备在完成工作之前重新进入休眠模式。最终结果是,应用可能直到未来的某个时间点才能完成工作,而这并不是您所希望的。

与其他任何广播接收器一样,使用 WakefulBroadcastReceiver 的第一步是将其添加到清单中:

<receiver android:name=".MyWakefulReceiver"></receiver>

以下代码使用方法 startWakefulService() 启动 MyIntentService。该方法与 startService() 大致相同,区别在于 WakefulBroadcastReceiver 会在服务启动时持有唤醒锁定。通过 startWakefulService() 传递的 intent 包含标识唤醒锁定的额外信息:

Kotlin

    class MyWakefulReceiver : WakefulBroadcastReceiver() {

        override fun 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(context, MyIntentService::class.java).also { service ->
                WakefulBroadcastReceiver.startWakefulService(context, service)
            }
        }
    }
    

Java

    public class MyWakefulReceiver 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, MyIntentService.class);
            startWakefulService(context, service);
        }
    }
    

当服务完成时,会调用 MyWakefulReceiver.completeWakefulIntent() 以释放唤醒锁定。completeWakefulIntent() 方法与其参数具有从 WakefulBroadcastReceiver 传入的同一 intent:

Kotlin

    const val NOTIFICATION_ID = 1

    class MyIntentService : IntentService("MyIntentService") {

        private val notificationManager: NotificationManager? = null
        internal var builder: NotificationCompat.Builder? = null

        override fun onHandleIntent(intent: Intent) {
            val extras: Bundle = intent.extras
            // Do the work that requires your app to keep the CPU running.
            // ...
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            MyWakefulReceiver.completeWakefulIntent(intent)
        }
    }
    

Java

    public class MyIntentService extends IntentService {
        public static final int NOTIFICATION_ID = 1;
        private NotificationManager notificationManager;
        NotificationCompat.Builder builder;
        public MyIntentService() {
            super("MyIntentService");
        }
        @Override
        protected void onHandleIntent(Intent intent) {
            Bundle extras = intent.getExtras();
            // Do the work that requires your app to keep the CPU running.
            // ...
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            MyWakefulReceiver.completeWakefulIntent(intent);
        }
    }