`
MyEyeOfJava
  • 浏览: 1149741 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
7af2d6ca-4fe1-3e9a-be85-3f65f7120bd0
测试开发
浏览量:71074
533896eb-dd7b-3cde-b4d3-cc1ce02c1c14
晨记
浏览量:0
社区版块
存档分类
最新评论

Android性能系列-电量篇

阅读更多

电量篇

1) Understanding Battery Drain

手机各个硬件模块的耗电量是不一样的,有些模块非常耗电,而有些模块则相对显得耗电量小很多。

电量消耗的计算与统计是一件麻烦而且矛盾的事情,记录电量消耗本身也是一个费电量的事情。唯一可行的方案是使用第三方监测电量的设备,这样才能够获取到真实的电量消耗。

当设备处于待机状态时消耗的电量是极少的,以N5为例,打开飞行模式,可以待机接近1个月。可是点亮屏幕,硬件各个模块就需要开始工作,这会需要消耗很多电量。

使用WakeLock或者JobScheduler唤醒设备处理定时的任务之后,一定要及时让设备回到初始状态。每次唤醒蜂窝信号进行数据传递,都会消耗很多电量,它比WiFi等操作更加的耗电。

2) Battery Historian

Battery Historian是Android 5.0开始引入的新API。通过下面的指令,可以得到设备上的电量消耗信息:

 

$ adb shell dumpsys batterystats > xxx.txt  //得到整个设备的电量消耗信息
$ adb shell dumpsys batterystats > com.package.name > xxx.txt //得到指定app相关的电量消耗信息

 

得到了原始的电量消耗数据之后,我们需要通过Google编写的一个python脚本把数据信息转换成可读性更好的html文件:

 

$ python historian.py xxx.txt > xxx.html

 

打开这个转换过后的html文件,可以看到类似TraceView生成的列表数据,这里的数据信息量很大,这里就不展开了。

3) Track Battery Status & Battery Manager

我们可以通过下面的代码来获取手机的当前充电状态:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. // It is very easy to subscribe to changes to the battery state, but you can get the current  
  2. // state by simply passing null in as your receiver.  Nifty, isn't that?  
  3. IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);  
  4. Intent batteryStatus = this.registerReceiver(null, filter);  
  5. int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);  
  6. boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);  
  7. if (acCharge) {  
  8.     Log.v(LOG_TAG,“The phone is charging!”);  
  9. }  

 

在上面的例子演示了如何立即获取到手机的充电状态,得到充电状态信息之后,我们可以有针对性的对部分代码做优化。比如我们可以判断只有当前手机为AC充电状态时 才去执行一些非常耗电的操作。

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /** 
  2.  * This method checks for power by comparing the current battery state against all possible 
  3.  * plugged in states. In this case, a device may be considered plugged in either by USB, AC, or 
  4.  * wireless charge. (Wireless charge was introduced in API Level 17.) 
  5.  */  
  6. private boolean checkForPower() {  
  7.     // It is very easy to subscribe to changes to the battery state, but you can get the current  
  8.     // state by simply passing null in as your receiver.  Nifty, isn't that?  
  9.     IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);  
  10.     Intent batteryStatus = this.registerReceiver(null, filter);  
  11.   
  12.     // There are currently three ways a device can be plugged in. We should check them all.  
  13.     int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);  
  14.     boolean usbCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_USB);  
  15.     boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);  
  16.     boolean wirelessCharge = false;  
  17.     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {  
  18.         wirelessCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS);  
  19.     }  
  20.     return (usbCharge || acCharge || wirelessCharge);  
  21. }  

 

4) Wakelock and Battery Drain

高效的保留更多的电量与不断促使用户使用你的App会消耗电量,这是矛盾的选择题。不过我们可以使用一些更好的办法来平衡两者。

假设你的手机里面装了大量的社交类应用,即使手机处于待机状态,也会经常被这些应用唤醒用来检查同步新的数据信息。Android会不断关闭各种硬件来延长手机的待机时间,首先屏幕会逐渐变暗直至关闭,然后CPU进入睡眠,这一切操作都是为了节约宝贵的电量资源。但是即使在这种睡眠状态下,大多数应用还是会尝试进行工作,他们将不断的唤醒手机。一个最简单的唤醒手机的方法是使用PowerManager.WakeLock的API来保持CPU工作并防止屏幕变暗关闭。这使得手机可以被唤醒,执行工作,然后回到睡眠状态。知道如何获取WakeLock是简单的,可是及时释放WakeLock也是非常重要的,不恰当的使用WakeLock会导致严重错误。例如网络请求的数据返回时间不确定,导致本来只需要10s的事情一直等待了1个小时,这样会使得电量白白浪费了。这也是为何使用带超时参数的wakelock.acquice()方法是很关键的。

但是仅仅设置超时并不足够解决问题,例如设置多长的超时比较合适?什么时候进行重试等等?解决上面的问题,正确的方式可能是使用非精准定时器。通常情况下,我们会设定一个时间进行某个操作,但是动态修改这个时间也许会更好。例如,如果有另外一个程序需要比你设定的时间晚5分钟唤醒,最好能够等到那个时候,两个任务捆绑一起同时进行,这就是非精确定时器的核心工作原理。我们可以定制计划的任务,可是系统如果检测到一个更好的时间,它可以推迟你的任务,以节省电量消耗。

这正是JobScheduler API所做的事情。它会根据当前的情况与任务,组合出理想的唤醒时间,例如等到正在充电或者连接到WiFi的时候,或者集中任务一起执行。我们可以通过这个API实现很多免费的调度算法。

5) Network and Battery Drain

下面内容来自官方Training文档中高效下载章节关于手机(Radio)蜂窝信号对电量消耗的介绍。

通常情况下,使用3G移动网络传输数据,电量的消耗有三种状态:

 

  • Full power: 能量最高的状态,移动网络连接被激活,允许设备以最大的传输速率进行操作。
  • Low power: 一种中间状态,对电量的消耗差不多是Full power状态下的50%。
  • Standby: 最低的状态,没有数据连接需要传输,电量消耗最少。

 

下图是一个典型的3G Radio State Machine的图示(来自AT&T,详情请点击这里):

总之,为了减少电量的消耗,在蜂窝移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求。

通过前面学习到的Battery Historian我们可以得到设备的电量消耗数据,如果数据中的移动蜂窝网络(Mobile Radio)电量消耗呈现下面的情况,间隔很小,又频繁断断续续的出现,说明电量消耗性能很不好:

经过优化之后,如果呈现下面的图示,说明电量消耗的性能是良好的:

另外WiFi连接下,网络传输的电量消耗要比移动网络少很多,应该尽量减少移动网络下的数据传输,多在WiFi环境下传输数据。

那么如何才能够把任务缓存起来,做到批量化执行呢?下面就轮到Job Scheduler出场了。

6) Using Job Scheduler

使用Job Scheduler,应用需要做的事情就是判断哪些任务是不紧急的,可以交给Job Scheduler来处理,Job Scheduler集中处理收到的任务,选择合适的时间,合适的网络,再一起进行执行。

下面是使用Job Scheduler的一段简要示例,需要先有一个JobService:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. public class MyJobService extends JobService {  
  2.     private static final String LOG_TAG = "MyJobService";  
  3.   
  4.     @Override  
  5.     public void onCreate() {  
  6.         super.onCreate();  
  7.         Log.i(LOG_TAG, "MyJobService created");  
  8.     }  
  9.   
  10.     @Override  
  11.     public void onDestroy() {  
  12.         super.onDestroy();  
  13.         Log.i(LOG_TAG, "MyJobService destroyed");  
  14.     }  
  15.   
  16.     @Override  
  17.     public boolean onStartJob(JobParameters params) {  
  18.         // This is where you would implement all of the logic for your job. Note that this runs  
  19.         // on the main thread, so you will want to use a separate thread for asynchronous work  
  20.         // (as we demonstrate below to establish a network connection).  
  21.         // If you use a separate thread, return true to indicate that you need a "reschedule" to  
  22.         // return to the job at some point in the future to finish processing the work. Otherwise,  
  23.         // return false when finished.  
  24.         Log.i(LOG_TAG, "Totally and completely working on job " + params.getJobId());  
  25.         // First, check the network, and then attempt to connect.  
  26.         if (isNetworkConnected()) {  
  27.             new SimpleDownloadTask() .execute(params);  
  28.             return true;  
  29.         } else {  
  30.             Log.i(LOG_TAG, "No connection on job " + params.getJobId() + "; sad face");  
  31.         }  
  32.         return false;  
  33.     }  
  34.   
  35.     @Override  
  36.     public boolean onStopJob(JobParameters params) {  
  37.         // Called if the job must be stopped before jobFinished() has been called. This may  
  38.         // happen if the requirements are no longer being met, such as the user no longer  
  39.         // connecting to WiFi, or the device no longer being idle. Use this callback to resolve  
  40.         // anything that may cause your application to misbehave from the job being halted.  
  41.         // Return true if the job should be rescheduled based on the retry criteria specified  
  42.         // when the job was created or return false to drop the job. Regardless of the value  
  43.         // returned, your job must stop executing.  
  44.         Log.i(LOG_TAG, "Whelp, something changed, so I'm calling it on job " + params.getJobId());  
  45.         return false;  
  46.     }  
  47.   
  48.     /** 
  49.      * Determines if the device is currently online. 
  50.      */  
  51.     private boolean isNetworkConnected() {  
  52.         ConnectivityManager connectivityManager =  
  53.                 (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);  
  54.         NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();  
  55.         return (networkInfo != null && networkInfo.isConnected());  
  56.     }  
  57.   
  58.     /** 
  59.      *  Uses AsyncTask to create a task away from the main UI thread. This task creates a 
  60.      *  HTTPUrlConnection, and then downloads the contents of the webpage as an InputStream. 
  61.      *  The InputStream is then converted to a String, which is logged by the 
  62.      *  onPostExecute() method. 
  63.      */  
  64.     private class SimpleDownloadTask extends AsyncTask<JobParameters, Void, String> {  
  65.   
  66.         protected JobParameters mJobParam;  
  67.   
  68.         @Override  
  69.         protected String doInBackground(JobParameters... params) {  
  70.             // cache system provided job requirements  
  71.             mJobParam = params[0];  
  72.             try {  
  73.                 InputStream is = null;  
  74.                 // Only display the first 50 characters of the retrieved web page content.  
  75.                 int len = 50;  
  76.   
  77.                 URL url = new URL("https://www.google.com");  
  78.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  79.                 conn.setReadTimeout(10000); //10sec  
  80.                 conn.setConnectTimeout(15000); //15sec  
  81.                 conn.setRequestMethod("GET");  
  82.                 //Starts the query  
  83.                 conn.connect();  
  84.                 int response = conn.getResponseCode();  
  85.                 Log.d(LOG_TAG, "The response is: " + response);  
  86.                 is = conn.getInputStream();  
  87.   
  88.                 // Convert the input stream to a string  
  89.                 Reader reader = null;  
  90.                 reader = new InputStreamReader(is, "UTF-8");  
  91.                 char[] buffer = new char[len];  
  92.                 reader.read(buffer);  
  93.                 return new String(buffer);  
  94.   
  95.             } catch (IOException e) {  
  96.                 return "Unable to retrieve web page.";  
  97.             }  
  98.         }  
  99.   
  100.         @Override  
  101.         protected void onPostExecute(String result) {  
  102.             jobFinished(mJobParam, false);  
  103.             Log.i(LOG_TAG, result);  
  104.         }  
  105.     }  
  106. }  

 

然后模拟通过点击Button触发N个任务,交给JobService来处理:

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. public class FreeTheWakelockActivity extends ActionBarActivity {  
  2.     public static final String LOG_TAG = "FreeTheWakelockActivity";  
  3.   
  4.     TextView mWakeLockMsg;  
  5.     ComponentName mServiceComponent;  
  6.   
  7.     @Override  
  8.     protected void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.activity_wakelock);  
  11.   
  12.         mWakeLockMsg = (TextView) findViewById(R.id.wakelock_txt);  
  13.         mServiceComponent = new ComponentName(this, MyJobService.class);  
  14.         Intent startServiceIntent = new Intent(this, MyJobService.class);  
  15.         startService(startServiceIntent);  
  16.   
  17.         Button theButtonThatWakelocks = (Button) findViewById(R.id.wakelock_poll);  
  18.         theButtonThatWakelocks.setText(R.string.poll_server_button);  
  19.   
  20.         theButtonThatWakelocks.setOnClickListener(new View.OnClickListener() {  
  21.             @Override  
  22.             public void onClick(View v) {  
  23.                     pollServer();  
  24.             }  
  25.         });  
  26.     }  
  27.   
  28.     /** 
  29.      * This method polls the server via the JobScheduler API. By scheduling the job with this API, 
  30.      * your app can be confident it will execute, but without the need for a wake lock. Rather, the 
  31.      * API will take your network jobs and execute them in batch to best take advantage of the 
  32.      * initial network connection cost. 
  33.      * 
  34.      * The JobScheduler API works through a background service. In this sample, we have 
  35.      * a simple service in MyJobService to get you started. The job is scheduled here in 
  36.      * the activity, but the job itself is executed in MyJobService in the startJob() method. For 
  37.      * example, to poll your server, you would create the network connection, send your GET 
  38.      * request, and then process the response all in MyJobService. This allows the JobScheduler API 
  39.      * to invoke your logic without needed to restart your activity. 
  40.      * 
  41.      * For brevity in the sample, we are scheduling the same job several times in quick succession, 
  42.      * but again, try to consider similar tasks occurring over time in your application that can 
  43.      * afford to wait and may benefit from batching. 
  44.      */  
  45.     public void pollServer() {  
  46.         JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);  
  47.         for (int i=0; i<10; i++) {  
  48.             JobInfo jobInfo = new JobInfo.Builder(i, mServiceComponent)  
  49.                     .setMinimumLatency(5000// 5 seconds  
  50.                     .setOverrideDeadline(60000// 60 seconds (for brevity in the sample)  
  51.                     .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // WiFi or data connections  
  52.                     .build();  
  53.   
  54.             mWakeLockMsg.append("Scheduling job " + i + "!\n");  
  55.             scheduler.schedule(jobInfo);  
  56.         }  
  57.     }  
  58. }  
分享到:
评论

相关推荐

    android-java-前端-面经-工具集合

    这篇压缩包文件"android-java-前端-面经-工具集合"提供了一系列宝贵的面试资源,涵盖了Java、Android以及前端开发领域,为准备面试的工程师提供了全面的学习材料。以下是这些资源所涉及的重要知识点的详细说明: 1....

    Android性能优化篇 [ 谷歌官方 ]-102116321

    谷歌官方推出的Android性能优化系列视频,旨在帮助开发者创建更快速、更高效的App。本篇文章将概述其中的一些核心概念,包括渲染性能、过度绘制和垂直同步(VSYNC)。 首先,渲染性能是用户体验中至关重要的因素。...

    Android-Android粒子篇之文字的粒子化运动

    "Android-Android粒子篇之文字的粒子化运动"这个主题深入探讨了如何在Android应用中实现文字转化为粒子效果的动画。这种效果常见于游戏开场、过渡界面或者特殊通知提示中,为用户带来视觉上的新颖和动态感。 首先,...

    android-sdk4.4-src

    本篇文章将围绕"android-sdk4.4-src"这一主题,深入探讨Android 4.4系统的源码,以及如何利用这些源码进行开发参考和学习。 首先,Android源码包含了系统的各个组件和库,是理解Android系统工作原理的关键。在...

    Android内核和驱动篇-Android内核介绍.doc

    这篇文档旨在为初学者提供一个关于Android内核的概述,同时也为后续深入探讨各个驱动程序打下基础。 首先,Android内核包含了对Android平台特有的硬件和功能的支持。例如,Android Binder驱动是Android进程间通信...

    Android培训-开发环境的使用

    在本篇文章中,我们将深入探讨Android开发环境中的关键工具和技术,特别是针对多模拟器测试方法以及如何精确地为模拟器设置真机参数等内容。这些知识点对于Android开发者来说至关重要,能够帮助他们更高效地进行应用...

    Android开发技巧5篇

    "Android开发技巧5篇"这个压缩包文件提供了丰富的学习资源,涵盖了Android开发中的核心知识点。下面,我们将详细探讨这些技巧,并深入理解它们在实际开发中的应用。 1. **Activity管理**:在Android应用中,...

    完整的android 安卓入门demo

    6. RecyclerView:作为ListView的替代,性能更优,支持更复杂的数据布局。 二、服务篇 Android服务是在后台长时间运行的组件,即使用户离开应用程序也可以继续执行任务。ApiDemos包括了启动服务、绑定服务的示例,...

    Android研发工程师高级进阶

    Android性能优化 性能优化是Android开发中非常重要的一部分,涉及到应用启动速度、运行流畅度、内存和电量消耗等多个方面。性能优化包括但不限于减少布局层次、合理使用内存、优化图片资源、使用线程池执行耗时任务...

    android应用开发详解

    这篇详细的讲解将涵盖Android应用开发的基础概念、开发环境搭建、UI设计、数据存储、网络通信、多线程处理以及性能优化等多个方面。 首先,基础概念是学习Android开发的起点。Android是一个基于Linux内核的操作系统...

    4.04数字电量

    Android 4.0系列引入了许多新特性,提升了系统的稳定性和性能。 在Android 4.0.4中,"数字电量"可能是指电池电量显示方式的一种改变,从图标形式转变为具体数字百分比的形式,让用户更直观地了解剩余电量。这种电量...

    android GifViewDemo源码.rar

    本篇文章将深入探讨如何在Android项目中创建一个自定义的GifView组件,以便更好地管理和播放GIF图像。通过分析"android GifViewDemo源码.rar"中的代码,我们可以学习到以下几个关键知识点: 1. **GIF解码库的选择与...

    Android高薪之路:Android程序员面试宝典

    本篇将基于"Android高薪之路:Android程序员面试宝典"这一主题,深入探讨Android程序员应掌握的核心知识点,以便在面试中脱颖而出,走上高薪之路。 首先,基础是所有技能的基石。对于Android程序员来说,Java或...

    新版Android开发教程&笔记

    Android性能优化包括内存管理、UI流畅性、电量消耗等多个方面。了解并应用ProGuard、Systrace、LeakCanary等工具进行性能分析和调试,可以提升应用的质量和用户体验。 十二、测试与发布 应用的测试分为单元测试、...

    Google-IO-IntroductionToAndroid.pdf

    在2008年的Google I/O大会上,Jason Chen作为开发者倡导者,发布了一篇关于Android IO的详细介绍。这份由谷歌官方提供的PPT文档,不仅概述了Android的基本概念和发展历程,还深入探讨了其架构和技术细节,对于理解...

    Android应用开发揭秘19

    10. **性能优化**:内存优化、CPU使用率优化、电量优化,使用Android Profiler进行性能分析。 11. **第三方库集成**:如RxJava、Retrofit、Butter Knife、Glide等,它们可以极大地提高开发效率。 12. **Android...

    Android 开发

    在Android开发领域,开发者需要掌握一系列的技术和工具来创建功能丰富的移动应用程序。Android是由Google主导开发的开源操作系统,它为全球无数的智能手机和平板电脑提供平台。本篇将深入探讨Android开发的关键知识...

    android 教程

    这篇博文链接虽然没有提供具体的描述,但通常这样的教程会包括基础到高级的Android编程知识,例如环境搭建、UI设计、数据存储、网络通信、多线程处理以及性能优化等方面。 在Android开发过程中,理解和掌握以下几个...

    Android模仿UC等软件启动画面加载时闪烁点的样式-IT计算机-毕业设计.zip

    7. **性能优化**: 考虑到性能和电量消耗,尽量减少动画对CPU的影响。可以通过使用硬件加速、优化绘制代码、合理调度动画等方式提高效率。 8. **论文撰写**: 对于毕业设计,你需要撰写一篇论文详细阐述这一实现过程...

Global site tag (gtag.js) - Google Analytics