要进行电量优化首先需要知道到我们的设备中有哪些耗电大户:
一般而言手机中耗电最多的模块有手机的显示屏,网络模块,GPS传感器模块,冗余的后台线程和Service。
所以总体而言我们有下优化措施:

  1. 尽量减少唤醒屏幕的次数以及持续的时间。
    例如下图中有三个Apk共唤醒了三次,而如果将上述的后台任务打包延迟处理,等待一个合适的时机将这些任务一并处理,如图2所示那么手机只需要被唤醒一次,这样就节省了电量的损耗。

  2. 某些非必须立刻执行的操作可以等到手机处于充电或者电量充足的时候进行。

这里就涉及到了如何判断手机电量状态的问题:

/
* This method checks for power by comparing the current battery state against all possible
* plugged in states. In this case, a device may be considered plugged in either by USB, AC, or
* wireless charge. (Wireless charge was introduced in API Level 17.)
*/
private boolean checkForPower() {
// It is very easy to subscribe to changes to the battery state, but you can get the current
// state by simply passing null in as your receiver. Nifty, isn't that?
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, filter);

// There are currently three ways a device can be plugged in. We should check them all.
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_USB);
boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);
boolean wirelessCharge = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
wirelessCharge = (chargePlug ==BatteryManager.BATTERY_PLUGGED_WIRELESS);
}
return (usbCharge || acCharge || wirelessCharge);
}
  1. 由于每次网络请求被出发后都会持续无线信号一段时间,这段时间会耗费较多电量(如下图所示),为了避免这个问题我们可以通过将多次零散的网络请求打包成一次操作,尽量避免频繁触发网络请求。


  1. 对于传感器尽量减少刷新请求,在Activity不需要监听某些Sensor数据的时候需要尽快释放监听注册,对于Sensor的数据我们尽量做批处理,待数据累积一定次数或者某个程度的时候再进行处理并更新到UI上。

  2. 对于定位功能是一个相对来说比较耗电的操作,通常来说,我们会使用类似下面这样的代码来发出定位请求:

    这里关键的是setInterval()这个方法它指的意思是每隔多长的时间获取一次位置更新,时间相隔越短, 自然花费的电量就越多,但是时间相隔太长,又无法及时获取到更新的位置信息。其中存在的一个优化点是,我们可以通过判断返回的位置信息是否相同,从而决定 设置下次的更新间隔是否增加一倍,通过这种方式可以减少电量的消耗。
    除了从网络请求频率的角度来优化外还可以从定位精度角度来考虑
    通过GPS定位服务相比起使用网络进行定位更加的耗电,但是精度方面也相对更加精准一些为了提供不同精度的定位需求,Android提供了下面4种不同精度与耗电量的参数给应用进行设置调用,应用只需要决 定在适当的场景下使用对应的参数就好了,通过LocationRequest.setPriority()方法传递下面的参数就好了。

  3. 对于定时任务尽量使用AlarmManager,而不是sleep或者Timer进行管理

在Android系统中为避免电量过度消耗,提供了电源唤醒锁wakeLock以及JobScheduler API。

电源唤醒锁wakeLock:

借助电源唤醒锁Android设备可以在被闲置之后迅速进入睡眠状态。还可以在屏幕关闭的时候利用唤醒锁保持后台服务的正常运行。但是一定要保证唤醒锁在最后的时刻回到初始状态。这是使用唤醒锁时候十分关键的一点。
至于唤醒锁的使用大家可以在晚上搜索相应的用法。除了唤醒锁外还有个WakefulBroadcastReceiver
,这个我自己用得比较少,所以借助这次机会对其进行总结下:
WakefulBroadcastReceiver 会将任务交给服务的同时保证设备在此过程中不会进入休眠状态。
下面是一个WakefulBroadcastReceiver 使用的例子:
在 WakefulReceiver中通过startWakefulService()来启动WakefulService,在这个过程中WakeflBroadcastReceiver会在Service启动后将唤醒锁保持住,当Service结束之后,它会调用WakefulReceiver.completeWakefulIntent()来释放唤醒锁。

public class WakefulReceiver 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, WakefulService.class);
startWakefulService(context, service);
}
}
public class WakefulService extends IntentService {

public WakefulService() {
super("WakefulService");
}
@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);
}
}

尽量使用setInexactRepeating()方法替代setRepeating()方法。当你使用setInexactRepeating()方法时,Android系统会集中多个应用的重复闹钟同步请求,并一起触发它们。这可以减少系统将设备唤醒的总次数,以此减少电量消耗。

JobScheduler:

除了使用电源唤醒锁,非精确闹钟来节省电源外,还可以通过JobScheduler API来将某些任务缓存起来推迟到某个时间或者一定条件下执行,比如想要将下载图片或者歌曲的任务安排在接入电源或者Wifi连接的情况下执行。下面是JobScheduler API的使用例子:

public class MyJobService extends JobService {
private static final String TAG = "MyJobService";

/**
* false: 该系统假设任何任务运行不需要很长时间并且到方法返回时已经完成。
* true: 该系统假设任务是需要一些时间并且当任务完成时需要调用jobFinished()告知系统。
*/
@Override
public boolean onStartJob(JobParameters params) {
Log.i(TAG, "Totally and completely working on job " +
params.getJobId());
if (isNetworkConnected()) {
new SimpleDownloadTask().execute(params);
return true;
} else {
Log.i(TAG, "No connection on job " + params.getJobId() + ";
sad face");
}
return false;
}

/**
* 当收到取消请求时,该方法是系统用来取消挂起的任务的。
* 如果onStartJob()返回false,则系统会假设没有当前运行的任务,故不会调用该方法。
*/
@Override
public boolean onStopJob(JobParameters params) {
Log.i(TAG, "stop job " + params.getJobId());
return false;
}

private boolean isNetworkConnected() {
ConnectivityManager manager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
return (info != null && info.isConnected());
}

private class SimpleDownloadTask extends
AsyncTask<JobParameters, Void, String> {

private JobParameters mJobParam;
@Override
protected String doInBackground(JobParameters... params) {
mJobParam = params[0];
try {
InputStream is = null;
int len = 50;
URL url = new URL("http://www.baidu.com");
HttpURLConnection conn = (HttpURLConnection)
url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("GET");
conn.connect();
int responseCode = conn.getResponseCode();
Log.i(TAG, "response code is : " + responseCode);
is = conn.getInputStream();
Reader reader = null;
reader = new InputStreamReader(is, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
} catch (Exception e) {
return "unable to retrieve web page";
}
}

@Override
protected void onPostExecute(String result) {
jobFinished(mJobParam, false);
Log.i(TAG, "获取结果:" + result);
}
}
}

调用:

public class MainActivity extends Activity {

private TextView result;
private ComponentName jobService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jobService = new ComponentName(this, MyJobService.class);
Intent service = new Intent(this, MyJobService.class);
startService(service);

result = (TextView) findViewById(R.id.result_tv);
Button btn = (Button) findViewById(R.id.button);
btn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
pollServer();
}
});
}

private void pollServer() {
JobScheduler scheduler = (JobScheduler)
getSystemService(Context.JOB_SCHEDULER_SERVICE);
int jobId;
for (int i = 0; i < 10; i++) {
jobId = i;
JobInfo jobInfo = new JobInfo.Builder(jobId, jobService)
.setMinimumLatency(5000)// 设置任务运行最少延迟时间
.setOverrideDeadline(60000)
// 设置deadline,若到期还没有达到规定的条件则会开始执行
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 设置网络条件
.setRequiresCharging(true)// 设置是否充电的条件
.setRequiresDeviceIdle(false)// 设置手机是否空闲的条件
.build();
result.append("scheduling job " + i + "!\n");
scheduler.schedule(jobInfo);
}
}

}

注册:

<service
android:name="com.example.jobschedulerdemo.MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>

使用battery-historian来进行电量的分析:

battery-historian 的gitHub地址如下:
https://github.com/google/battery-historian
关于battery-historian2 的使用推荐看下下面的这篇文章。
http://www.jianshu.com/p/a7d9a3aec423/comments/1589962

Contents
  1. 1. 电源唤醒锁wakeLock:
  2. 2. JobScheduler: