笔记57 | Android保持设备唤醒

保持设备唤醒

为了避免电量过度消耗,Android设备会在被闲置之后迅速进入睡眠状态。然而有时候应用会需要唤醒屏幕或者是唤醒CPU并且保持它们的唤醒状态,直至一些任务被完成。

想要做到这一点,所采取的方法依赖于应用的具体需求。但是通常来说,我们应该使用最轻量级的方法,减小其对系统资源的影响。在接下来的部分中,我们将会描述在设备默认的睡眠行为与应用的需求不相符合的情况下,我们应该如何进行对应的处理。


保持屏幕常亮

某些应用需要保持屏幕常亮,比如游戏与视频应用。最好的方式是在你的Activity中(且仅在Activity中,而不是在Service或其他应用组件里)使用FLAGKEEPSCREEN_ON属性,例如:

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);
  }

该方法的优点与唤醒锁(Wake Locks)不同(唤醒锁的内容在本章节后半部分),它不需要任何特殊的权限,系统会正确地 管理应用之间的切换,且不必关心释放资源的问题。

另外一种方法是在应用的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"与使用FLAGKEEPSCRRE_ON等效。你可以选择最适合你的应用的方法。在Activity中通过代码设置常亮标识的优点在于:你可以通过代码动态清除这个标示,从而使屏幕可以关闭。

Notes:除非你不再希望正在运行的应用长时间点亮屏幕(例如:在一定时间无操作发生后,你想要将屏幕关闭),否则你是不需要清除FLAGKEEPSCRRE_ON标识的。WindowManager会在应用进入后台或者返回前台时,正确管理屏幕的点亮或者关闭。但是如果你想要显式地清除这一标识,从而使得屏幕能够关闭,可以使用 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)方法。


保持CPU运行

如果你需要在设备睡眠之前,保持CPU运行来完成一些工作,你可以使用PowerManager系统服务中的唤醒锁功能。唤醒锁允许应用控制设备的电源状态。

创建和保持唤醒锁会对设备的电源寿命产生巨大影响。因此你应该仅在你确实需要时使用唤醒锁,且使用的时间应该越短越好。如果想要在Activity中使用唤醒锁就显得没有必要了。如上所述,可以在Activity中使用FLAGKEEPSCRRE_ON让屏幕保持常亮。

使用唤醒锁的一种合理情况可能是:一个后台服务需要在屏幕关闭时利用唤醒锁保持CPU运行。再次强调,应该尽可能规避使用该方法,因为它会影响到电池寿命。

不必使用唤醒锁的情况

  1. 如果你的应用正在执行一个HTTP长连接的下载任务,可以考虑使用DownloadManager。
  2. 如果你的应用正在从一个外部服务器同步数据,可以考虑创建一个SyncAdapter
  3. 如果你的应用需要依赖于某些后台服务,可以考虑使用RepeatingAlarm或者Google Cloud Messaging,以此每隔特定的时间,将这些服务激活。

为了使用唤醒锁,首先需要在应用的Manifest清单文件中增加WAKE_LOCK权限:

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

如果你的应用包含一个BroadcastReceiver并使用Service来完成一些工作,你可以通过WakefulBroadcastReceiver管理你唤醒锁。后续章节中将会提到,这是一种推荐的方法。如果你的应用不满足上述情况,可以使用下面的方法直接设置唤醒锁:

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

可以调用 wakelock.release()来释放唤醒锁。当应用使用完毕时,应该释放该唤醒锁,以避免电量过度消耗。


使用WakefulBroadcastReceiver

你可以将BroadcastReceiver和Service结合使用,以此来管理后台任务的生命周期。WakefulBroadcastReceiver是一种特殊的BroadcastReceiver,它专注于创建和管理应用的PARTIALWAKELOCK。WakefulBroadcastReceiver会将任务交付给Service(一般会是一个IntentService),同时确保设备在此过程中不会进入睡眠状态。如果在该过程当中没有保持住唤醒锁,那么还没等任务完成,设备就有可能进入睡眠状态了。其结果就是:应用可能会在未来的某一个时间节点才把任务完成,这显然不是你所期望的。

要使用WakefulBroadcastReceiver,首先在Manifest文件添加一个标签:

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

下面的代码通过 startWakefulService()启动 MyIntentService。该方法和 startService()类似,除了WakeflBroadcastReceiver会在Service启动后将唤醒锁保持住。传递给 startWakefulService()的Intent会携带有一个Extra数据,用来标识唤醒锁。

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);
    }
}

当Service结束之后,它会调用 MyWakefulReceiver.completeWakefulIntent()来释放唤醒锁。 completeWakefulIntent()方法中的Intent参数是和WakefulBroadcastReceiver传递进来的Intent参数一致的:

public class MyIntentService extends IntentService {
    public static final int NOTIFICATION_ID = 1;
    private NotificationManager mNotificationManager;
    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);
    }
}

原文发布于微信公众号 - 项勇(xiangy_life)

原文发表时间:2017-12-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android开发指南

7.Activity

33615
来自专栏技术小黑屋

Android内存泄漏检测利器:LeakCanary

到这里你就可以检测到Activity的内容泄露了。其实现原理是设置Application的ActivityLifecycleCallbacks方法监控所有Act...

782
来自专栏Samego开发资源

简单快捷的退出APP应用

1567
来自专栏潇涧技术专栏

Android Training Summary (1) Getting Started

Android Training 中Getting Started部分的阅读笔记

510
来自专栏吴小龙同學

Android之进程和线程

进程 线程 应用启动时,系统会为应用创建一个名为“主线程”的执行线程。 此线程非常重要,因为它负责将事件分派给相应的用户界面小工具,其中包括绘图事件。 此外,它...

3086
来自专栏along的开发之旅

Android 生命周期中每个函数适合处理的事件

从这种图中,我们可以知道Activity生命周期是: onCreate -> onStart -> onResume -> onPause -> onSto...

451
来自专栏Android开发指南

8.广播

33610
来自专栏KK的小酒馆

垃圾回收及内存调试工具的介绍Android应用性能优化

Android的Generational Heap Memory模型和几个内存调试工具:Memory Monitor、Allocation Tracker、He...

1241
来自专栏向治洪

listview的工作原理

/**      * Unsorted views that can be used by the adapter as a convert vi...

20010
来自专栏everhad

Android零散

2016-03-13 Android零散 ListView中嵌套GridView 要实现分组列表这样的效果:点击ListView中的分组名称,即展开此分组显示其...

1956

扫码关注云+社区