#android电池电量优化
在09年Google IO大会Jeffrey Sharkey指出android应用的耗电量主要在以下三个方面:
- 大数据量的传输
- 不停地在网络间切换
- 解析大量的文本数据
并提出了相关的优化建议:
- 在需要网络连接的程序中, 首先检查网络连接是否正常,如果没有网络连接,就不要执行相应的程序
- 使用效率高的数据格式和解析方法,推荐使用JSON和Protobuf
- 在进行大数据量下载时,尽量使用GZIP方式下载
- 其他:回收java对象,特别是较大的java对象,使用reset方法;对定位要求不是太高的话尽量不要使用GPS定位;尽量不要使用浮点运算;获取屏幕尺寸等信息可以使用缓存技术,不需要进行多次请求;使用AlarmManager来定时启动服务替代使用sleep方式的定时任务
作为app开发者,应该限制app对电量的影响,当没有网络连接的时候,禁用后台服务更新,当电池电量低的时候减少更新的频率,当电池充电或者电量比较饱和时,可以最大限度的发挥app的刷新率
获取手机的当前充电状态
//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);
int chargePlug=batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
boolean acCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_AC);
if(acCharge){
Log.v(LOG_TAG,“Thephoneischarging!”);
}
优化的建议
- 任何App包括后台Service应该尽可能减少唤醒CPU的次数
- 每次唤醒CPU执行的代码应该尽可能少,从而让CPU迅速恢复休眠
- Wi-Fi比2G、3G更省电
- 尽量在Wi-Fi下传输数据,在有Wi-Fi的时候做预加载,比如应用中心的zip包、离线资源等
- 非Wi-Fi下,尽量减少网络访问,每一次后台交互都要考虑是否必须
- 使用推送,代替循环请求
- 对于自定义控件,通过canvas.clipRect()来帮助系统识别那些可见的区域,这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视
- 避免嵌套太多层控件
- 合理使用include、merge
- 避免大量对象被创建又在短时间内马上被释放
- 避免在for循环,onDraw中创建对象,无法避免的可以创建对象池,然后在不使用的时候释放
JobScheduler API会根据当前的情况与任务,组合出理想的唤醒时间,例如等到正在充电或者连接到WiFi的时候,或者集中任务一起执行,可以通过这个API实现很多免费的调度算法
//示例 public class MyJobService extendsJ obService{ private static final String LOG_TAG="MyJobService"; @Override public void onCreate(){ super.onCreate(); Log.i(LOG_TAG,"MyJobServi cecreated"); } @Override public voidon Destroy(){ super.onDestroy(); Log.i(LOG_TAG,"MyJobService destroyed"); } @Override public boolean onStartJob(JobParameters params){ //This is where you would implement all of the logic for your job.Note that this runs //on the main thread,so you will want to use as eparate thread for asynchronous work //(as we demonstrate below to establishanet work connection). //If you use as eparatethread,return true to indicate that you need a "reschedule" to //return to the job at somepoint in the future to finish processing the work.Otherwise, //return false whenfinished. Log.i(LOG_TAG,"Totally and completely working on job"+params.getJobId()); //First,check the network,and the nattempt to connect. if(isNetworkConnected()){ new SimpleDownloadTask().execute(params); return true; }else{ Log.i(LOG_TAG,"No connection on job"+params.getJobId()+";sad face"); } return false; } @Override public boolean onStopJob(JobParameters params){ //Called if the job must be stopped before job Finished() has been called.This may //happen if the requirements are no longer being met,such as the user no longer //connecting to WiFi,or the device no longer being idle.Use this callback to resolve //anything that may cause your application to mis behave from the job being halted. //Return true if the job should be rescheduled based on there try criteria specified //when the job was created or return false to drop the job.Regardless of the value //returned,your job must stop executing. Log.i(LOG_TAG,"Whelp,something changed,so I'm callingiton job"+params.getJobId()); return false; } /** *Determines if the device is currently on line. */ private boolean isNetworkConnected(){ ConnectivityManager connectivityManager= (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo(); return(networkInfo!=null&&networkInfo.isConnected()); } /** *Uses AsyncTask to create a task away from the mainUIthread.This task creates a *HTTPUrlConnection,and then downloads the content soft heweb page as an InputStream. *The InputStream is then converted to a String,which is logged by the *onPostExecute() method. */ private class SimpleDownloadTask extends AsyncTask<JobParameters,Void,String>{ protected JobParameters mJobParam; @Override protected String doInBackground(JobParameters...params){ //cache system provided job requirements mJobParam=params[0]; try{ InputStreamis=null; //Only display the first 50 characters of the retrieved webpage content. int len=50; URL url=new URL("https://www.google.com"); HttpURLConnection conn=(HttpURLConnection)url.openConnection(); conn.setReadTimeout(10000);//10sec conn.setConnectTimeout(15000);//15sec conn.setRequestMethod("GET"); //Starts the query conn.connect(); int response=conn.getResponseCode(); Log.d(LOG_TAG,"Theresponseis:"+response); is=conn.getInputStream(); //Convert the input stream toastring Reader reader=null; reader=new InputStreamReader(is,"UTF-8"); char[] buffer=new char[len]; reader.read(buffer); return newString(buffer); }catch(IOExceptione){ return"Unabletoretrievewebpage."; } } @Override protected void onPostExecute(Stringresult){ jobFinished(mJobParam,false); Log.i(LOG_TAG,result); } } } //模拟通过点击Button触发N个任务,交给JobService来处理: public class FreeTheWakelockActivity extends ActionBarActivity{ public static final String LOG_TAG="FreeTheWakelockActivity"; TextView mWakeLockMsg; ComponentName mServiceComponent; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_wakelock); mWakeLockMsg=(TextView)findViewById(R.id.wakelock_txt); mServiceComponent=new ComponentName(this,MyJobService.class); Intent startServiceIntent=new Intent(this,MyJobService.class); startService(startServiceIntent); Button theButtonThatWakelocks=(Button)findViewById(R.id.wakelock_poll); theButtonThatWakelocks.setText(R.string.poll_server_button); theButtonThatWakelocks.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ pollServer(); } }); } /** *This method polls the server via the JobScheduler API.By scheduling the job with this API, *your app can be confident it will execute,but without the need for awakelock.Rather,the *API will take your networkjobs and execute the min batch to best take advantage of the *initial network connection cost. * *The JobScheduler API works through a background service.In this sample,we have *a simple service in MyJobService to get you started.The job isscheduled here in *the activity,but the jobit self is executed in MyJobService in the start Job() method.For *example,to poll your server,you would create the network connection,send your GET *request,and then process the response all in MyJobService.This allows the JobScheduler API *to invoke your logic without neededtore start your activity. * *For brevity in the sample,we are scheduling the same jobsever alt imes in quick succession, *but again,try to consider similar task so ccurring over time in your applicationth at can *afford towait and maybe nefit from batching. */ publicvoidpollServer(){ JobScheduler scheduler=(JobScheduler)getSystemService(Context.JOB_SCHEDULER_SERVICE); for(inti=0;i<10;i++){ JobInfo jobInfo=new JobInfo.Builder(i,mServiceComponent) .setMinimumLatency(5000)//5seconds .setOverrideDeadline(60000)//60seconds(forbrevityinthesample) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//WiFiordataconnections .build(); mWakeLockMsg.append("Schedulingjob"+i+"!n"); scheduler.schedule(jobInfo); } } }
在移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求