转载自
Android 性能优化之旅5–电量优化

2、屏幕保持常亮
为了防止屏幕唤醒一瞬间耗电过多,有一些应用,比如游戏、支付页面,需要保持屏幕常亮来节省电量:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
也可以在布局文件里面使用,但是没有那么灵活:

android:keepScreenOn=“true”
注意:一般不需要人为的去掉FLAG_KEEP_SCREEN_ON的flag,windowManager会管理好程序进入后台回到前台的的操作。如果确实需要手动清掉常亮的flag,使用getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
3.1、使用wake_lock
系统为了节省电量,CPU在没有任务忙的时候就会自动进入休眠。有任务需要唤醒CPU高效执行的时候,就会给CPU加wake_lock锁。wake_lock锁主要是相对系统的休眠而言的,意思就是我的程序给CPU加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行。有的情况如果不这么做就会出现一些问题,比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了wake_lock锁。

PowerManager这个系统服务的唤醒锁(wake locks)特征来保持CPU处于唤醒状态。唤醒锁允许程序控制宿主设备的电量状态。创建和持有唤醒锁对电池的续航有较大的影响,所以,除非是真的需要唤醒锁完成尽可能短的时间在后台完成的任务时才使用它。比如在Acitivity中就没必要用了。一种典型的代表就是在屏幕关闭以后,后台服务继续保持CPU运行。

如果不使用唤醒锁来执行后台服务,不能保证因CPU休眠未来的某个时刻任务会停止,这不是我们想要的。(有的人可能认为以前写的后台服务就没掉过链子呀运行得挺好的,1.可能是你的任务时间比较短;2.可能CPU被手机里面很多其他的软件一直在唤醒状态)。

其中,唤醒锁有下面几种类型:

唤醒锁的类型.png
wake_lock两种锁(从释放、使用的角度来看的话):

一种计数锁
非计数锁(锁了很多次,只需要release一次就可以解除了)
Tips:请注意,自 API 等级17开始,FULL_WAKE_LOCK将被弃用,应使用FLAG_KEEP_SCREEN_ON代替。
综上所述,为了防止CPU唤醒一瞬间耗电过多,在执行关键代码的时候,为了防止CPU睡眠,需要使用唤醒锁来节省电量:

//创建唤醒锁
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "partial_lock");//获取唤醒锁
wakeLock.acquire();//一些关键的代码//释放唤醒锁
wakeLock.release();

需要添加权限:

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

Tips:获取与释放唤醒锁需要成对出现
Tips:有一些意外的情况,比如小米手机是做了同步心跳包(心跳对齐)(如果超过了这个同步的频率就会被屏蔽掉或者降频),所有的app后台唤醒频率不能太高,这时候就需要降频,比如每隔2S中去请求。
3.2、使用WakefulBroadcastReceiver
上面提到,典型的使用场景就是后台服务需要保持CPU保持运行,推荐的方式是使用WakefulBroadcastReceiver:使用广播和Service(典型的IntentService)结合的方式可以让你很好地管理后台服务的生命周期。

WakefulBroadcastReceiver是BroadcastReceiver的一种特例。它会为你的APP创建和管理一个PARTIAL_WAKE_LOCK类型的WakeLock。WakefulBroadcastReceiver把工作交接给service(通常是IntentService),并保证交接过程中设备不会进入休眠状态。如果不持有WakeLock,设备很容易在任务未执行完前休眠。最终结果是你的应用不知道会在什么时候能把工作完成,相信这不是你想要的。

例子:

服务:

public class MyIntentService extends IntentService {public MyIntentService(String name) {super(name);}public MyIntentService() {super(MyIntentService.class.getSimpleName());}@Overrideprotected void onHandleIntent(@Nullable Intent intent) {if (intent != null) {//获取参数Bundle extras = intent.getExtras();//执行一些需要CPU保持唤醒的代码//执行结束,释放唤醒锁MyWakefulReceiver.completeWakefulIntent(intent);}}
}

广播接收者:

public class MyWakefulReceiver extends WakefulBroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Intent service = new Intent(context, MyIntentService.class);startWakefulService(context, service);}}

需要使用服务的时候,像一般的方式一样即可:

Intent intent = new Intent(this, MyIntentService.class);
//传递参数
intent.setData(Uri.parse(“xxx”));
Tips:注意添加权限
Tips:注意服务与广播的注册
Tips:使用广播来设计,就是为了解耦
3.3、大量高频次的CPU唤醒及操作使用JobScheduler/GCM
大量高频次的CPU唤醒及操作,我们可以采取一些算法来解决,把这些操作安排在一个时间点集中处理,而不是分开处理(这样就可以防止了唤醒的耗电)。

我们可以使用谷歌提供的JobScheduler或者GCM来实现这样的功能。

下面举一个频繁请求网络的例子:

这是一个请求网络的服务:

public class MyJobService extends JobService {private static final String TAG = "MyJobService";@Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "MyJobService created");}@Overridepublic void onDestroy() {super.onDestroy();Log.i(TAG, "MyJobService destroyed");}/*** 开启耗时操作* @param params* @return*/@Overridepublic boolean onStartJob(JobParameters params) {Log.i(TAG, "onStartJob:" + params.getJobId());if (isNetworkConnected()) {new SimpleDownloadTask() .execute(params);return true;} else {Log.i(TAG, "No connection:" + params.getJobId());}return false;}/*** jobFinish调用之前会回调* @param params* @return*/@Overridepublic boolean onStopJob(JobParameters params) {Log.i(TAG, "onStopJob:" + params.getJobId());return false;}private boolean isNetworkConnected() {ConnectivityManager connectivityManager =(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();return (networkInfo != null && networkInfo.isConnected());}private class SimpleDownloadTask extends AsyncTask<JobParameters, Void, String> {protected JobParameters mJobParam;@Overrideprotected String doInBackground(JobParameters... params) {mJobParam = params[0];try {InputStream is = null;int len = 50;URL url = new URL("https://www.baidu.com");HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setReadTimeout(10000); //10secconn.setConnectTimeout(15000); //15secconn.setRequestMethod("GET");conn.connect();int response = conn.getResponseCode();Log.d(TAG, "The response is: " + response);is = conn.getInputStream();Reader reader = new InputStreamReader(is, "UTF-8");char[] buffer = new char[len];reader.read(buffer);return new String(buffer);} catch (IOException e) {e.printStackTrace();return null;}}@Overrideprotected void onPostExecute(String result) {//结束任务jobFinished(mJobParam, false);Log.i(TAG, result);}}
}

下面通过循环来模拟频繁调用:

ComponentName serviceComponent = new ComponentName(this,MyJobService.class);
//频繁地唤醒
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
for (int i = 0; i < 500; i++) {JobInfo jobInfo = new JobInfo.Builder(i,serviceComponent).setMinimumLatency(5000)//最小延时5秒.setOverrideDeadline(60000)//最多执行时间60秒//.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//免费的网络---wifi 蓝牙 USB.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//任意网络---wifi.build();jobScheduler.schedule(jobInfo);
}

4、使用AlarmManager来唤醒
当机器一段时间不操作以后,就会进入睡眠状态。向服务器的轮询就会停止、长连接就会断开,为了防止这样的情况,就可以使用AlarmManager:

Intent intent = new Intent(this, TestService.class);
PendingIntent pi = PendingIntent.getService(this, 0, intent, 0);AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);//闹钟在系统睡眠状态下会唤醒系统并执行提示功能
//模糊时间,在API-19中以及以前,setRepeating都是不准确的
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, 2000, pi);
//准确时间,但是需要在API-17之后使用
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, pi);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, pi);

该定时器可以启动Service服务、发送广播、跳转Activity,并且会在系统睡眠状态下唤醒系统。所以该方法不用获取电源锁和释放电源锁。

关于AlarmManager的更多信息,请参考其他文章。

在19以上版本,setRepeating中设置的频率只是建议值(6.0 的源码中最小值是60s),如果要精确一些的用setWindow或者setExact。
5、其他优化
当然,电量优化是包括很多方面的,例如:

渲染优化
定位策略优化
网络优化,例如网络缓存处理,请求方式、次数优化、设置超时时间等等
代码执行效率优化
防止内存泄漏
等等,电量优化无处不在。

深化
首先Android手机有两个处理器,一个叫Application Processor(AP),一个叫Baseband Processor(BP)。AP是ARM架构的处理器,用于运行Linux+Android系统;BP用于运行实时操作系统(RTOS),通讯协议栈运行于BP的RTOS之上。非通话时间,BP的能耗基本上在5mA左右,而AP只要处于非休眠状态,能耗至少在50mA以上,执行图形运算时会更高。另外LCD工作时功耗在100mA左右,WIFI也在100mA左右。一般手机待机时,AP、LCD、WIFI均进入休眠状态,这时Android中应用程序的代码也会停止执行。

Android为了确保应用程序中关键代码的正确执行,提供了Wake Lock的API,使得应用程序有权限通过代码阻止AP进入休眠状态。但如果不领会Android设计者的意图而滥用Wake Lock API,为了自身程序在后台的正常工作而长时间阻止AP进入休眠状态,就会成为待机电池杀手。比如前段时间的某应用,比如现在仍然干着这事的某应用。
AlarmManager 是Android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,在预设的时间到达时,通过中断唤醒 CPU。(极光推送就是利用这个来做的。)
总结:

关键逻辑的执行过程,就需要Wake Lock来保护。如断线重连重新登陆
休眠的情况下如何唤醒来执行任务?用AlarmManager。如推送消息的获取
最后,通过Wakelock Detector(WLD)软件可以看到手机中的Wakelock:

参考文章: 推荐阅读:Android6.0的Doze模式:http://blog.csdn.net/aa20616012/article/details/49781875 http://www.jianshu.com/p/ded0ed6fac3d http://www.jianshu.com/p/5b8bfa6a6c37 http://www.jianshu.com/p/fc2a4d191e18

Android 性能优化之旅5--电量优化相关推荐

  1. Android 性能优化之旅5 电量优化

    ###一.前言 当手机运行的时候,处理各种任务,硬件就会消耗电量.用户可以通过手机自带的电量监测功能来查看电量使用情况: 一旦用户发现你的APP耗电量特别大,那可就麻烦了.因此我们需要在设计app的时 ...

  2. App优化(五)电量优化

    引言 App优化(一)App启动速度优化 App优化(二)布局优化 App优化(三)ANR优化 App优化(四)内存优化 App优化(五)电量优化 App优化(六)网络优化 电量使用优化, 基本上是我 ...

  3. Android 系统性能优化(77)---电量优化

    Android 优化--电量优化 Android 5.0 后用 Battery Historian 工具分析电量. 耗电因素 移动网络请求 手机通过内置的射频模块和基站联系,从而链接上网的,而这个射频 ...

  4. Android性能调优:App启动速度优化

    一.App启动分类 1.冷启动 Cold start 在启动应用前,系统还没有App的任何进程.比如设备开机后应用的第一次启动,系统杀掉应用进程 (如:系统内存吃紧引发的 kill 和 用户主动产生的 ...

  5. Android 性能优化

    为什么80%的码农都做不了架构师?>>>    原文作者:鸿洋 原文地址:点我跳转原文 一般情况下,我们谈性能优化基本上会从以下几个方面: App启动速度优化 UI流畅度优化 内存优 ...

  6. Android 性能优化概述

    在 APP 功能完成之后,不可避免的需要做一些 APP 的性能优化. 所以,在此做一些 APP 优化知识的整理,也相当于一份优化清单,方便以后对照查阅. 在 Android 中,性能优化一般围绕以下 ...

  7. Android代码、图片、布局、网络和电量优化

    这篇文章分为五个部分代码优化.图片优化.布局优化.网络优化.电量优化,尽量每个方法都写了小的Demo! 代码优化:不要做多余的工作,尽量避免次数过多的内存的分配,(需要对api有一定的熟悉) 数据集合 ...

  8. 2.4万字长文!Android代码、图片、布局、网络和电量优化

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 关注订阅号「程序员小乐」,收看更多精彩内容 每日英文 https://wei ...

  9. 【Android 电量优化】电量优化特性 ( Doze 低电耗模式 | Standby 应用待机模式 | 白名单设置 | 白名单添加系统设置界面 | 指定应用的白名单添加界面 | 测试应用 )

    文章目录 一.Doze 低耗电模式简介 二.Standby 应用待机模式简介 三.Doze 和 Standby 模式测试 四.白名单添加 ( 方式一 ) 五.白名单添加 ( 方式二 ) Android ...

最新文章

  1. python 100例(10)
  2. 8个可以提高数据科学工作效率、节省宝贵时间的Python库
  3. koa中使用cookie 和session
  4. Python 创建随机名字的文件夹/文件
  5. Y_CLIENT_QHD_504 role in GM6 ZSAP_CRM_BCR_SALESREP_DAIDE
  6. 用分布式锁解决并发问题
  7. AWR 报告深度解读:Redo Nowait指标的算法和诊断泄露二十多万名用户数据
  8. 网友质疑特斯拉电动汽车电池不利于回收 马斯克回怼!
  9. git commit –a时使用vim编辑提交信息
  10. html调整图片之间的距离,html中如何调整图片之间的间距
  11. 如何去掉PDF右下角的全能扫描王水印
  12. Halcon 算子 complement
  13. linux 使用shell/python编写钉钉自动监控报警脚本
  14. node 拦截器拦截请求下载电子书以及等待前端渲染操作、浏览器操作
  15. 【opencv4.3.0教程】11之调整图像边缘(copyMakeBorder 与 borderInterpolate)
  16. Win键无法使用,被锁定
  17. 反电动势的危害和简易保护措施
  18. 计算机博士银行待遇怎么样,博士生入职某银行,晒出行业收入和待遇,网友:我不信...
  19. 基于亚马逊云科技无服务器服务快速搭建电商平台——部署篇
  20. u盘安装浪潮服务器_浪潮NF5280M5服务器安装windows2008R2

热门文章

  1. linux怎么复制java版本,如何修改linux默认的jdk版本
  2. WOW插件:ShortKey 4.7 发布(2009.9.27)
  3. 游戏装备强化java机制_从游戏中理解Java特性,我悟了!
  4. 安卓游戏录屏v2.5.4会员版
  5. Python编程:实现tail-n查看日志文件
  6. 中医说“特发性震颤”是什么疾病?
  7. 关机后键盘鼠标灯不灭的原因和解决办法
  8. UE4 UMG中文字缩放
  9. PTA 7-26 聪明的高斯
  10. 苹果怎么关闭自动亮度_微信自动关闭怎么解决