电源管理PowerManager

PowerManager是Android的电源管理类,用于管理电源操作如睡眠、唤醒、重启以及调节屏幕亮度等等。
PowerManager的对象从系统服务POWER_SERVICE中获取,它的主要方法如下:
goToSleep : 睡眠,即锁屏。
wakeUp : 唤醒,即解锁。
reboot : 重启。
另有下列几个隐藏的方法:
getMinimumScreenBrightnessSetting : 获取屏幕亮度的最小值。
getMaximumScreenBrightnessSetting : 获取屏幕亮度的最大值。
getDefaultScreenBrightnessSetting : 获取屏幕亮度的默认值。
setBacklightBrightness : 设置屏幕亮度。

但对多数开发者来说,PowerManager在实际开发中毫无用处,因为一旦调用该类的方法,你的app运行时就会崩溃,查看日志报错“java.lang.SecurityException: Neither user 10150 nor current process has android.permission.DEVICE_POWER.”这个错误信息倒是容易看懂,好吧,那我便在AndroidManifest.xml中加上DEVICE_POWER的权限。可是加了权限之后,ADT又提示错误“Permission is only granted to system apps”。这下傻眼了,怎么会说“权限只授予系统应用程序”呢?不过这难不倒我,咱把app工程clean一下,错误提示就不见了,然后重新Run之,结果Console栏出现红色文字“Installation error: INSTALL_FAILED_SHARED_USER_INCOMPATIBLE”,还是不行呀。
找了大量的资料,才发现这是因为电源管理的权限,只有系统程序(打了系统签名)才可以获得,用户程序无法获取这个权限。大伙对该问题基本是束手无策,只有Stack Overflow上的大神给了个解决方案,主要做三方面的修改:
1、在AndroidManifest.xml中加上DEVICE_POWER、REBOOT、SHUTDOWN的权限。
2、在AndroidManifest.xml的manifest节点中增加属性说明“android:sharedUserId="android.uid.system"”,这表示使用系统用户的uid。
3、为了能够共享系统用户的uid,你的app得采用系统签名打包,即先找到目标Android系统的platform.pk8和platform.x509.pem密钥文件,然后使用signapk.jar将apk签名到指定密钥。
这个解决方案理论上可行,但就真机来说,每个品牌每个型号的手机,其系统签名都是不一样的。因此,就算你真的搞出来一个系统应用,那也仅适用于该签名版本的Android系统,而不能用于其他签名的Android系统,所以PowerManager只能是手机厂商内部使用了。

下面是PowerManager几个用途的示例代码(一般用不到,仅供参考):

import java.lang.reflect.Field;
import java.lang.reflect.Method;import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;//注意,PowerManager只有系统应用才能操作,普通应用不能操作,所以下面代码仅供参考
public class PowerUtil {private final static String TAG = "PowerUtil";private static int getValue(Context ctx, String methodName, int defValue) {int value = defValue;PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);try {Class<?> pmClass = Class.forName(pm.getClass().getName());Field field = pmClass.getDeclaredField("mService");field.setAccessible(true);Object iPM = field.get(pm);Class<?> iPMClass = Class.forName(iPM.getClass().getName());Method method = iPMClass.getDeclaredMethod(methodName);method.setAccessible(true);value = (Integer) method.invoke(iPM);} catch (Exception e) {e.printStackTrace();}Log.d(TAG, "methodName="+methodName+", value="+value);return value;}public static int getMinLight(Context ctx) {return getValue(ctx, "getMinimumScreenBrightnessSetting", 0);}public static int getMaxLight(Context ctx) {return getValue(ctx, "getMaximumScreenBrightnessSetting", 255);}public static int getDefLight(Context ctx) {return getValue(ctx, "getDefaultScreenBrightnessSetting", 100);}//设置屏幕亮度。light取值0-255public static void setLight(Context ctx, int light) {PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);try {Class<?> pmClass = Class.forName(pm.getClass().getName());// 得到PowerManager类中的成员mService(mService为PowerManagerService类型)Field field = pmClass.getDeclaredField("mService");field.setAccessible(true);// 实例化mServiceObject iPM = field.get(pm);// 得到PowerManagerService对应的Class对象Class<?> iPMClass = Class.forName(iPM.getClass().getName());/** 得到PowerManagerService的函数setBacklightBrightness对应的Method对象,* PowerManager的函数setBacklightBrightness实现在PowerManagerService中*/Method method = iPMClass.getDeclaredMethod("setBacklightBrightness", int.class);method.setAccessible(true);// 调用实现PowerManagerService的setBacklightBrightnessmethod.invoke(iPM, light);} catch (Exception e) {e.printStackTrace();}}public static void resetLight(Context ctx, int light) {try {Object power;Class <?> ServiceManager = Class.forName("android.os.ServiceManager");Class <?> Stub = Class.forName("android.os.IPowerManager$Stub");Method getService = ServiceManager.getMethod("getService", new Class[] {String.class});//Method asInterface = GetStub.getMethod("asInterface", new Class[] {IBinder.class});//of this class?Method asInterface = Stub.getMethod("asInterface", new Class[] {IBinder.class});    //of this class?IBinder iBinder = (IBinder) getService.invoke(null, new Object[] {Context.POWER_SERVICE});//power = asInterface.invoke(null,iBinder);//or call constructor Stub?//Method setBacklightBrightness = power.getClass().getMethod("setBacklightBrightness", new Class[]{int.class}); setBacklightBrightness.invoke(power, new Object[]{light});} catch (Exception e) {e.printStackTrace();}}//锁屏public static void lockScreen(Context ctx) {PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);pm.goToSleep(SystemClock.uptimeMillis());}//解锁@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)public static void unLockScreen(Context ctx) {PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);pm.wakeUp(SystemClock.uptimeMillis());}//重启public static void reboot(Context ctx) {PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);pm.reboot(null);}//关机public static void shutDown(Context ctx) {Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN");intent.putExtra("android.intent.extra.KEY_CONFIRM", false);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 弹出系统内置的对话框,选择确定关机或取消关机ctx.startActivity(intent);}}

电池管理BatteryManager

BatteryManager名为电池管理,然而查看该类的源代码,里面只有一些常量定义,并非真正意义上的电池管理。事实上,开发者并不能直接管理电池,要想获取电池的相关信息,得通过监听电量改变事件来得知。

电池的电量改变事件,其动作名称是Intent.ACTION_BATTERY_CHANGED,因为接受该事件要求app必须处于活动状态,所以用来监听的广播接收器不能在AndroidManifest.xml中静态注册,而只能在app代码中通过registerReceiver方法来动态注册。下面是电量改变事件中携带的参数信息:
BatteryManager.EXTRA_SCALE : 电量刻度,通过getIntExtra获取。通常是100
BatteryManager.EXTRA_LEVEL : 当前电量,通过getIntExtra获取。
BatteryManager.EXTRA_STATUS : 当前状态,通过getIntExtra获取。
--BATTERY_STATUS_UNKNOWN = 1; 表示未知
--BATTERY_STATUS_CHARGING = 2; 表示正在充电
--BATTERY_STATUS_DISCHARGING = 3; 表示正在断电
--BATTERY_STATUS_NOT_CHARGING = 4; 表示不在充电
--BATTERY_STATUS_FULL = 5; 表示充满
BatteryManager.EXTRA_HEALTH : 健康程度,通过getIntExtra获取。
--BATTERY_HEALTH_UNKNOWN = 1; 表示未知
--BATTERY_HEALTH_GOOD = 2; 表示良好
--BATTERY_HEALTH_OVERHEAT = 3; 表示过热
--BATTERY_HEALTH_DEAD = 4; 表示坏了
--BATTERY_HEALTH_OVER_VOLTAGE = 5; 表示短路
--BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6; 表示未知错误
--BATTERY_HEALTH_COLD = 7; 表示冷却
BatteryManager.EXTRA_VOLTAGE : 当前电压,通过getIntExtra获取。
BatteryManager.EXTRA_PLUGGED : 当前电源,通过getIntExtra获取。
--0 表示电池
--BATTERY_PLUGGED_AC = 1; 表示充电器
--BATTERY_PLUGGED_USB = 2; 表示USB
--BATTERY_PLUGGED_WIRELESS = 4; 表示无线
BatteryManager.EXTRA_TECHNOLOGY : 当前技术,通过getStringExtra获取。比如返回Li-ion表示锂电池。
BatteryManager.EXTRA_TEMPERATURE : 当前温度,通过getIntExtra获取。
BatteryManager.EXTRA_PRESENT : 是否提供电池,通过getBooleanExtra获取。

除了电量改变事件,还有几个事件与电池有关,如下所示
Intent.ACTION_BATTERY_LOW : 电池电量过低,静态注册时使用android.intent.action.BATTERY_LOW
Intent.ACTION_BATTERY_OKAY : 电池电量恢复,静态注册时使用android.intent.action.BATTERY_OKAY
Intent.ACTION_POWER_CONNECTED : 连上外部电源,静态注册时使用android.intent.action.ACTION_POWER_CONNECTED
Intent.ACTION_POWER_DISCONNECTED : 断开外部电源,静态注册时使用android.intent.action.ACTION_POWER_DISCONNECTED

下面是电池事件的监听截图:

下面是监听电池事件的代码示例:

import com.example.exmbattery.util.DateUtils;import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Bundle;
import android.widget.TextView;public class BatteryActivity extends Activity {private TextView tv_battery_change;private static TextView tv_power_status;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_battery);tv_battery_change = (TextView) findViewById(R.id.tv_battery_change);tv_power_status = (TextView) findViewById(R.id.tv_power_status);}@Overrideprotected void onStart() {super.onStart();batteryChangeReceiver = new BatteryChangeReceiver();IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);registerReceiver(batteryChangeReceiver, filter);}@Overrideprotected void onStop() {super.onStop();unregisterReceiver(batteryChangeReceiver);}private BatteryChangeReceiver batteryChangeReceiver;private class BatteryChangeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent != null) {int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 0);int healthy = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, 0);int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 3);String technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);int temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false);String desc = String.format("%s : 收到广播:%s",DateUtils.getNowDateTime(), intent.getAction());desc = String.format("%s\n电量刻度=%d", desc, scale);desc = String.format("%s\n当前电量=%d", desc, level);desc = String.format("%s\n当前状态=%s", desc, mStatus[status]);desc = String.format("%s\n健康程度=%s", desc, mHealthy[healthy]);desc = String.format("%s\n当前电压=%d", desc, voltage);desc = String.format("%s\n当前电源=%s", desc, mPlugged[plugged]);desc = String.format("%s\n当前技术=%s", desc, technology);desc = String.format("%s\n当前温度=%d", desc, temperature/10);desc = String.format("%s\n是否提供电池=%s", desc, present?"是":"否");tv_battery_change.setText(desc);}}}private static String[] mStatus = {"不存在", "未知", "正在充电", "正在断电", "不在充电", "充满"};private static String[] mHealthy = {"不存在", "未知", "良好", "过热", "坏了", "短路", "未知错误", "冷却"};private static String[] mPlugged = {"电池", "充电器", "USB", "不存在", "无线"};private static String mChange = ""; public static class PowerChangeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent != null) {mChange = String.format("%s\n%s : 收到广播:%s", mChange, DateUtils.getNowDateTime(), intent.getAction());tv_power_status.setText(mChange);}}}
}

省电方法/屏幕开关事件

前面说了许多废话,赶快回到本文的主题——省电。app开发与服务器程序开发不同,app所在的移动设备是很缺电的,几天就要充一次电,所以如果你的app特别耗电,一天甚至半天就把用户手机搞没电了,那么通常逃脱不了被卸载的悲惨命运。因此,为人为己,开发者还是尽可能让app运行的时候省电些,绿色环保的低碳生活,从开发app做起。

然而目前尚无法检测每个应用的耗电程度,一般是靠经验判断,基本原则就是:越消耗资源的,耗电就越大。具体到代码编写,主要有以下省电措施:
1、能用整型数计算,就不用浮点数计算。
2、能用json解析,就不用xml解析。
3、能用网络定位,就不用GPS定位。
4、尽量减少大文件的下载(如先压缩再下载,或者缓存已下载的文件)。
5、用完系统资源,要及时回收。占着茅坑不拉屎,用户手机会很蛋疼。相关例子参见《 Android开发笔记(七十五)内存泄漏的处理》
6、能用线程处理,就不用进程处理。
7、多用缓存复用对象资源。如屏幕尺寸只需获取一次,其后可到缓存中读取,全局变量技术参见《 Android开发笔记(二十八)利用Application实现内存读写》。相关例子还可参见《 Android开发笔记(七十六)线程池管理》、《 Android开发笔记(七十七)图片缓存算法》
8、能用定时器广播,就不用后台常驻服务。
9、能用内存存储,就不用文件存储。

省电措施虽多,那要如何得知省电效果呢?在实际开发中,耗电大户其实是在后台默默运行的Service服务,想想看,手机待机的时候,屏幕都不亮了,可是手机里面还有一些不知疲倦的Service在愚公移山,愚公也是要吃饭的呀。我做过实验,一个app在系统待机时仍然满血Service运行,一小时后手机电量消耗4%;同一个app改造后在系统待机时不运行任何Service,一小时后手机电量消耗2%;一小时相差2%,十小时便相差20%啊,原来我们手机的电量就是这样被一点一点耗光的。

既然如此,我们若想避免app在手机待机时仍在做无用功,就要在屏幕关闭时结束指定任务,在屏幕点亮时再开始指定任务。这里用到了下面三个屏幕开关事件:
Intent.ACTION_SCREEN_ON : 屏幕点亮事件
Intent.ACTION_SCREEN_OFF : 屏幕关闭事件
Intent.ACTION_USER_PRESENT : 用户解锁事件,静态注册时使用android.intent.action.USER_PRESENT
使用上述三个事件要注意几点:
1、屏幕点亮事件和屏幕关闭事件必须在代码中动态注册。如果在AndroidManifest.xml中静态注册,则不起任何作用。
2、在关闭屏幕时,系统先暂停所有活动页面,然后才关闭屏幕;同样的,在点亮屏幕时,系统点亮屏幕,然后才恢复活动页面。所以这几个事件不能在Activity中注册/注销,只能在自定义Application的onCreate方法中注册,在onTerminate方法中注销。
3、Activity要想获取屏幕开关事件,得通过自定义的Application类去间接获取。

下面是屏幕开关事件的捕捉截图:

下面是屏幕开关事件的代码:

import com.example.exmbattery.util.DateUtils;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class LockScreenReceiver extends BroadcastReceiver {private static final String TAG = "LockScreenReceiver";@Overridepublic void onReceive(Context context, Intent intent) {if (intent != null) {String mChange = "";mChange = String.format("%s\n%s : 收到广播:%s", mChange,DateUtils.getNowDateTime(), intent.getAction());if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {mChange = String.format("%s\n这是屏幕点亮事件", mChange);} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {mChange = String.format("%s\n这是屏幕关闭事件", mChange);} else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {mChange = String.format("%s\n这是用户解锁事件", mChange);}Log.d(TAG, mChange);MainApplication.getInstance().setChangeDesc(mChange);}}}

下面是自定义Application的代码:

import android.app.Application;
import android.content.Intent;
import android.content.IntentFilter;public class MainApplication extends Application {private static MainApplication mApp;private LockScreenReceiver mReceiver;private String mChange = "";public static MainApplication getInstance() {return mApp;}public String getChangeDesc() {return mApp.mChange;}public void setChangeDesc(String change) {mApp.mChange = mApp.mChange + change;}@Overridepublic void onCreate() {super.onCreate();mApp = this;mReceiver = new LockScreenReceiver();IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_USER_PRESENT);registerReceiver(mReceiver, filter);}@Overridepublic void onTerminate() {unregisterReceiver(mReceiver);super.onTerminate();}}

下面是显示屏幕开关事件的页面代码

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;public class ScreenActivity extends Activity {private static TextView tv_screen;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_screen);tv_screen = (TextView) findViewById(R.id.tv_screen);}@Overrideprotected void onStart() {super.onStart();tv_screen.setText(MainApplication.getInstance().getChangeDesc());}}

点击下载本文用到的省电处理的工程代码

点此查看Android开发笔记的完整目录

Android开发笔记(一百一十七)app省电方略相关推荐

  1. Android开发笔记(七十七)图片缓存算法

    ImageCache 由于手机流量有限,又要加快app的运行效率,因此好的app都有做图片缓存.图片缓存说起来简单,做起来就用到很多知识点,可算是集Android技术之大全了.只要理解图片缓存的算法, ...

  2. Android开发笔记(五十七)录像录音与播放

    媒体录制MediaRecorder MediaRecorder是Android自带的录制工具,通过操纵摄像头和麦克风完成媒体录制,既可录制视频,也可单独录制音频.其中对摄像头Camera的介绍参见&l ...

  3. Android开发笔记(四十七)Runnable接口实现多线程

    Runnable概述 Runnable接口可声明一连串的事务,常用于多线程处理.但是实现Runnable接口并不意味着开启了一个新线程,只是定义了接下来要做的事情,至于说这些事情要在主线程处理,还是在 ...

  4. Android开发笔记(三十七)按钮类控件

    Button与ImageButton Button是文本按钮(继承自TextView),而ImageButton是图像按钮(继承自ImageView).两者之间的区别在于: 1.Button即可显示文 ...

  5. Android开发笔记(二十七)对象序列化

    什么是序列化 程序中存储和传递信息,需要有个合适的数据结构,最简单的是定义几个变量,变量多了之后再分门别类,便成了聚合若干变量的对象.代码在函数调用时可以直接传递对象,但更多的场合例如与文件交互.与网 ...

  6. Android开发笔记(九十七)图片的特效处理

    图片特效用到的函数 本文讲述的图片特效处理包括:怀旧.光照.光晕.底片.浮雕.模糊.锐化.黑白.冰冻.素描,所有这些特效都是基于一定的算法,对图像每个点的RGB值进行计算,并汇总所有点的计算结果生成新 ...

  7. Android开发笔记(八十七)几个修饰关键字

    原生native native是方法修饰符,表示该方法是由其他一种语言(如C/C++)实现的原生方法.其实native只在JNI接口中使用,java代码中只有原生方法的定义,具体的实现代码在其他语言( ...

  8. android开发启动画面,Android开发笔记——如何正确实现App启动页

    启动页在当前的Android APP中有着广泛的使用,在Material Design规范出来之前,Android官方是不推荐使用启动页的,理由是让用户在启动APP后干等是一种浪费时间的行为.Mate ...

  9. android小米通知不显示电量,Android开发笔记——小米通知‘坑’ app的通知一直显示在不重要通知里 ......

    Android8.0之后,通知引入渠道的概念,谷歌初衷应该是让用户能够自己管理通知级别以提高用户体验. 不过在小米这,我遇到了一个坑,在其他8.0的手机,渠道设置等级后,能够正常显示通知,代码如下:p ...

  10. 小米重要通知android,Android开发笔记——小米通知‘坑’ app的通知一直显示在不重要通知里 ......

    Android8.0之后,通知引入渠道的概念,谷歌初衷应该是让用户能够自己管理通知级别以提高用户体验. 不过在小米这,我遇到了一个坑,在其他8.0的手机,渠道设置等级后,能够正常显示通知,代码如下:p ...

最新文章

  1. R语言把dataframe数据转化为tibble格式、查看每个数据列的缺失值个数、使用数据列的均值对数据列的缺失值进行填充
  2. javascript闭包-全局变量与局部变量
  3. 在Visual Studio 2010 里使用Nunit 进行Debug 测试
  4. html小球跳跃技术原理,HTML5在文本上跳跃的小球
  5. Java-WAS的Hello world
  6. 【BZOJ4562】食物链,拓扑DP
  7. mysql cmd grep_通过 mysqlbinlog 和 grep 命令定位binlog文件中指定操作
  8. 【李宏毅2020 ML/DL】P57 Unsupervised Learning - Linear Methods | PCA Matrix Factorization
  9. c语言 程序设计 题库答案 p,c语言 程序设计 题库答案 p
  10. Android手机网页字体异常,移动端html5手机网站的中文字体使用
  11. 吝啬SAT问题是NP完全问题的证明
  12. 2023毕业生必看,springboot计算机毕业设计容易通过题目都在这里!
  13. 英特尔酷睿处理器后缀
  14. 禁止用鼠标拖动窗口的大小 - 回复 合肥的石头 的问题
  15. 基于C++的关键字检索系统
  16. [GXYCTF2019]禁止套娃
  17. Android开发-模拟器推荐
  18. 会计计算机功能,会计软件具有什么功能
  19. bitmap的六种压缩方式,Android图片压缩
  20. 【飞秋教程】群聊天/群共享/群日志

热门文章

  1. 工程数学(数值分析)第六讲:数值微积分
  2. c++中queue用法
  3. 吴恩达机器学习ex7:K-means分类
  4. Redis基础(七)——事件、服务器、客户端、多线程
  5. 机器学习-吴恩达-笔记-10-降维
  6. HttpClientHelper的封装
  7. vba 保存word里面的图片_笔记7 【office精华课】一套课程学会Word+Excel+PPT(一)【Word】(2020年第37周 周五)...
  8. 使用STS临时访问凭证访问OSS
  9. 优秀logo设计解析_日本的logo设计为什么那么优秀?
  10. Spring源码解析 -- SpringWeb请求参数获取解析