Android省电开发

《Android省电开发》

被问到啊哈哈哈,宛如一个智障的我为什么没有想到!
性能优化肯定会省电啊啊啊
其他的没说,这个怎么……

《Android省电开发》

憋说了,来来来,整理一波

转载自享受技术带来的快乐

(一)性能优化

Android开发除了NDK之外,使用的都是Java语言,而Java语言是一种基于虚拟机JVM运行的语言,所以相比C/C++语言来说,效率是比较低的。Java需要占用大量内存来换取执行速度,而不定期的GC机制,直接导致Android界面的卡顿现象。

Java语言优化

1. 使用非阻塞I/O

版本较低的JDK不支持非阻塞I/O API。为了避免I/O阻塞,一些应用采用了创建大量线程的方法(在较好的情况下,会使用一个缓冲池)。这种技术可以在许多必须支持并发I/O流的应用中见到,如web服务器等。然而,创建Java线程需要相当可观的开销。Java在JDK 1.4及以后版本中提供了一套API来专门操作非阻塞I/O,我们可以再java.nio包及其子包中找到相关的类和接口。由于这套API是JDK新提供的I/O API,因此,也叫New I/O,这就是包名nio的由来。这套API由3个主要的部分组成:缓冲区(Buffers)、通道(Channels)和非阻塞I/O的核心类。

2. 慎用异常

异常对性能不利。抛出异常首先要创建一个新的对象。<code>Throwable</code>接口的构造函数调用名为<code>fillnStackTrace()的本地(Native)方法,fillnStackTrace()</code>方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。

3. 变量和修饰符

  • 不要重复初始化变量。默认情况下,调用类的构造函数时,Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double设置成0.0,逻辑值设置成false。所有尽量不要重复初始化变量。

  • 尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(stack)中,速度较快,并且随所在线程的死亡而自动销毁。其他变量,如静态变量、实例变量等,都在堆(heap)中创建,速度较慢,垃圾回收是的耗能会导致APP出现卡顿现象。

  • 尽量指定类的final修饰符。带有final修饰符的类是不可派生的。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法。此举能够使性能平均提高50%。

  • 私有内部类要访问外部类的field或方法时,其成员变量不要用private,因为在编译时会生成setter/getter影响性能。可以把外部类的field或者方法声明为包访问权限。

  • 如果方法用不到成员变量,可以把方法申明为static,性能会提高到15%到20%。

4. 其他方面:

  • 用位操作代替乘除法;
  • 用StringBuilder代替拼接操作;
  • 对算法进行复杂度的改进(必要的时候可以空间换时间);
  • 合理利用浮点数,浮点数比整形慢两倍。
  • 网络优化、异步线程、图片使用缓存等。

UI布局优化

  • 1.布局层数尽量少,RelativeLayout来代替LinearLayout,因为RelativeLayout性能更优,且可以简单实现LinearLayout嵌套才能实现的布局。

  • 2.采用<merge>标签优化合并布局层数:系统在编译XML布局文件时不会为<merge>生成任何节点,通过合并可以大大减少标签的生成。

  • 3.采用<include>标签共享重用其他布局:并用android:id属性覆盖被引用布局文件中顶层节点的android:id属性值。

<!—引用mylayout.xml文件>  
<include android:id=”@+id/layout1” layout=”@layout/mylayout”/>  
  • 4.<code><viewstub></code>标签:viewstub标签同include标签一样可以用来引入一个外部布局,不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。<code><viewstub></code>常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。

  • 5.通过Android SDK中tools目录下的<code>layoutopt</code>命令查看你的布局是否需要优化。

  • 6.将Activity中的window的背景图设置为空。<code>.getWindow().setBackgroundDrawable(null)</code>;android默认的背景不为空。

  • 7.View中设置缓存属性<code>lsetDrawingCache</code>l为true。动态加载View时采用ViewStub避免一些不经常的视图长期握住引用。

  • 8.采用SurfaceView在子线程刷新UI,避免手势的处理和绘制在同一UI线程(普通View都这样做)。

  • 9.ListView的优化

    • item尽可能的减少使用的控件和布局的层次;
    • RelativeLayout是绝对的利器,通过它可以减少布局的层次。
    • 尽可能的复用控件,这样可以减少 ListView的内存使用,减少滑动时GC次数。
    • ListView的背景色与cacheColorHint设置相同颜色,可以提高滑动时的渲染性能。
    • ListView中getView是性能是关键,这里要尽可能的优化。
    • getView方法中要重用view;
    • getView方法中不能做复杂的逻辑计算, 特别是数据库操作,否则会严重影响滑动时的性能。

数据库优化

  1. 有些能用文件操作的,尽量使用文件操作,文件操作的速度比数据库的操作要快10倍左右。

  2. Cursor的使用,管理好cursor,不要每次打开关闭cursor,因为打开关闭cursor非常耗时。Cursor.require用于刷cursor。同时由于SQLiteDatabase对象较为耗费资源,所以我们在使用完SQLiteDatabase对象之后,必须立即关闭它,避免它继续占用资源,否则我们继续程序可能会导致OOM或者其他异常。

  3. SQLite使用事务,也可以自定义事务。使用事务好处是原子提交和更优性能。

Db.beginTransaction();  
Db.setTransactionSuccessful();  
Db.end Transaction();  
  1. 可以建立索引,增加检索的速度。(当某字段数据更新频率较低,查询频率较高,经常有范围查询(>,<, =, >=, <=)或orderby、group by发生时建议使用索引; 经常同时存取多列,且每列都含有重复值可考虑建立复合索引)。

  2. 批量插入、更新使用原子操作,采用事务等方式。

  3. 查询时返回更少的结果集及更少的字段

  4. 少用cursor.getColumnIndex(可以在建表的时候用static变量记住某列的index,直接调用相应index而不是每次查询。)

  5. ** 优化sql语句**字符串等,语句的拼接使用StringBuilder代替String。

新机制或者借用工具

  • 采用JNI技术,将耗时间的处理放到c/c++层处理。适当的采用NDK编程可以提高效率,但是内存回收不稳定,所以说适当。
  1. 懒加载和缓存机制:访问网络的耗时操作启动一个新线程来做,而不要在UI线程做。还有从网络上获取大量图片的时候,可以本地缓存,下次获取同样图片的时候,可以先从本地获取,本地无再从网络获取。

  2. 通过Android SDK中tools目录下的layoutopt命令查看你的布局是否需要优化;hierarchy viewer可以方便的查看Activity的布局,各个View的属性、measure、layout、draw的时间,如果耗时较多会用红色标记,否则显示绿色;利用android自带的性能跟踪工具TraceView查看跟踪函数调用,跟踪方法跟踪各部分的执行效率。

  3. 可以在方法执行前后各使用system.currentTimeMillis方法获取当前系统的时间(毫秒),两个时间之差可以计算出方法的执行时间,进而可以改进优化。

  4. 获取系统的内存信息

//获取系统内存总数  
Long total = Runtime.getRuntime().totalMemory();  
//获取剩余内存  
Long free = Runtime.getRuntime().freeMemory();  
//获取已经使用内存  
Long used = total – free;

(二)Android省电开发之CPU降频

转载自Matrixxu博客专栏

在Android系统的耗电量排行里,cpu的耗电占了比较大的一部分比例,也就是说,cpu的使用率和使用频率将直接或间接的影响电量的分配和使用,但很遗憾,android-sdk中没有为android的开发者提供类似cpu管理的功能,但是当下很多省电类应用或专业的cpu管理软件都提供了cpu的降频甚至是超频的功能,那么这样的功能是如何实现的,本文将详细说明在android环境下调整cpu频率的一些方法,仅供参考。

  • CPU的工作频率
    单位赫兹或者兆赫兹。CPU的工作频率越高,耗电量越大,反之亦然。
  • CPU的调控模式
    英文词为:Governor,解释为调速器,控制器。android的framework是基于Linux平台的,那么cpu的管理体系这块也跟linux基本上一样,其中包括cat命令,和一些文件的读写配置都是基本上差不多的。Linux在管理CPU方面,提供了如下几种调控模式,分别为:
    《Android省电开发》
调控模式效果
performance将CPU的工作频率调整到最大模式,让CPU充分工作
powersave将cpu的工作频率调整到节能模式,这个模式下的CPU频率最低
ondemand定期检查负载。当负荷超越了阈值,设置的CPU运行以最高的频率。当负载低于相同的阈值,设置的CPU运行在下一个的最低频率。导致更少的延迟比。ondemand从字面翻译是“根据需求,按照需要”,cpu在工作的时候频率会在一个最大值和最小值之间波动,当负载提高时,该调控期会自动提高cpu的频率,反之亦然。“Causes less latency than the conservative governor.”这句话的意思是,该模式跟conservative相比,会导致更少的延迟。ok,那让我们再看看conservative是如何解释的。
conservative该模式与ondemand的最大区别在于:conservative模式不会立刻在负载增加的情况下将cpu频率调整到最大,会调整到比目前频率稍微大的频段去工作。所以在某种极端情况下,该模式的延迟会大于ondemand。
usersapce将cpu的掌控权交给了用户态,也就是交给了应用程序,应用程序可以通过配置文件的方式修改cpu的频率信息。
public class CPUFreqSetting {  
    /** 
     * cpu cat命令大全 
     * cat [%cpuFreqPath%]/cpuinfo_cur_freq   (当前cpu频率) 
     * cat [%cpuFreqPath%]/cpuinfo_max_freq     (最大cpu频率) 
     * cat [%cpuFreqPath%]/cpuinfo_min_freq     (最小cpu频率) 
     * cat [%cpuFreqPath%]/related_cpus     (cpu数量标号,从0开始,如果是双核,结果为0,1) 
     * cat [%cpuFreqPath%]/scaling_available_frequencies    (cpu所有可用频率) 
     * cat [%cpuFreqPath%]/scaling_available_governors  (cpu所有可用调控模式) 
     * cat [%cpuFreqPath%]/scaling_available_governors  (cpu所有可用调控模式) 
     * cat [%cpuFreqPath%]/scaling_cur_freq     (?????) 
     * cat [%cpuFreqPath%]/scaling_driver   (?????) 
     * cat [%cpuFreqPath%]/scaling_governor (?????) 
     * cat [%cpuFreqPath%]/scaling_max_freq (?????) 
     * cat [%cpuFreqPath%]/scaling_min_freq (?????) 
     * cat [%cpuFreqPath%]/scaling_setspeed (?????) 
     * cat [%cpuFreqPath%]/cpuinfo_transition_latency   (?????) 
     */  
    private final String TAG = "SetCPU";  
    private final String cpuFreqPath = "/sys/devices/system/cpu/cpu0/cpufreq";  
  
    private final static String PERFORMANCE_GOVERNOR = "performance";  
    private final static String POWER_SAVE_GOVERNOR = "performance";  
    private final static String ONDEMAND_GOVERNOR = "performance";  
    private final static String CONSERVATIVE_GOVERNOR = "performance";  
    private final static String USERSAPCE_GOVERNOR = "performance";  
  
//  public void powerSaveGovernor() {  
//      List<String> governors = readCpuGovernors();  
//      if (governors.contains(object)) {  
//  
//      }  
//  }  
  
    /** 
     * 获得当前CPU调控模式 
     */  
    public void getCpuCurGovernor() {  
        try {  
            DataInputStream is = null;  
            Process process = Runtime.getRuntime().exec("cat " + cpuFreqPath + "/scaling_governor");  
            is = new DataInputStream(process.getInputStream());  
            String line = is.readLine();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * 重写CPU调控模式 
     * @param governor 
     * @return 
     */  
    private boolean writeCpuGovernor(String governor) {  
        DataOutputStream os = null;  
        byte[] buffer = new byte[256];  
        String command = "echo " + governor + " > " + cpuFreqPath + "/scaling_governor";  
        Log.i(TAG, "command: " + command);  
        try {  
            Process process = Runtime.getRuntime().exec("su");  
            os = new DataOutputStream(process.getOutputStream());  
            os.writeBytes(command + "\n");  
            os.writeBytes("exit\n");  
            os.flush();  
            process.waitFor();  
            Log.i(TAG, "exit value = " + process.exitValue());  
        } catch (IOException e) {  
            Log.i(TAG, "writeCpuGovernor: write CPU Governor(" + governor + ") failed!");  
            return false;  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        return true;  
    }  
  
    /** 
     * 获得CPU所有调控模式 
     * @return 
     */  
    private List<String> readCpuGovernors() {  
        List<String> governors = new ArrayList<String>();  
        DataInputStream is = null;  
        try {  
            Process process = Runtime.getRuntime().exec("cat " + cpuFreqPath + "/scaling_available_governors");  
            is = new DataInputStream(process.getInputStream());  
            String line = is.readLine();  
  
            String[] strs = line.split(" ");  
            for (int i = 0; i < strs.length; i++)  
                governors.add(strs[i]);  
        } catch (IOException e) {  
            Log.i(TAG, "readCpuGovernors: read CPU Governors failed!");  
        }  
        return governors;  
    }  
}

(三)Android省电开发之service

Android应用开发中,难免会遇到service开发。android中服务是运行在后台的东西,级别与activity差不多。既然说service是运行在后台的服务,那么它就是不可见的,没有界面的东西。Service和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的请求或者动作。你可以在服务中开一个线程,在线程中做耗时动作。

** 我们从三个方面来浅析一下service的省电开发**

  • ** 查看service是否存活以及降低优先级:**
    假如一个service工作完成,但是来不及关掉或者kill掉,用户又看不见,所以这个service将会一直在后台运行,势必耗电。所以我们可以降低某些不常用service进程的优先级,在系统内存吃紧的情况下, 进程优先级低的service容易被系统kill掉。除此之外,可以利用监听系统广播来判断service状态是否存活,死亡即可手动kill掉。

  • ** 用IntentService代替Service开发:**
    普通服务一旦启动之后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来。为了简单的创建一个异步的、会自动挺值得服务,Android专门提供了一个IntentService类。IntentService在运行完毕后自动停止,减少耗电量。

  • ** 后台执行的定时任务Alarm机制:**
    Service没必要一直在后台运行,这时候的定时任务显得很重要。
    Android的定时任务有两种实现方式,Timer类和Alarm机制。

  • Timer有一个明显的短板,不适合长期后台运行的定时任务。为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android手机就会在长时间不操作的情况下自动让CPU进入到睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。
  • Alarm机制则不存在这种情况,它具有唤醒CPU的功能,即可以保证每次需要执行定时任务的时候CPU能正常工作。

♬ PS:Alarm机制

从Android 4.4之后,Alarm任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能得到执行。这不是bug,而是系统在耗电性方面进行的优化。系统会自动检测目前有多少Alarm任务存在,然后将触发时间将近的几个任务放在一起执行,这就可以大幅度的减少CPU被唤醒的次数,从而有效延长电池的使用时间。

但是,Android提供了解决方案,使用AlarmManager的<code>setExact()</code>来代替<code>set()</code>方法,基本可以保证任务准时执行。

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread() {
            @Override
            public void run() {
                //耗时操作
            }
        }.run();
        AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
        Intent intent = new Intent(this, MyService.class);
        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
        return super.onStartCommand(intent, flags, startId);
    }
}
// 让定时任务的触发时间从系统开机算起,不会唤醒CPU
AlarmManager.ELAPSED_REALTIME;        
//让定时任务的触发时间从系统开机算起,会唤醒CPU
AlarmManager.ELAPSED_REALTIME_WAKEUP; 
 // 让定时任务的触发时间从1970年1月1日0点算起,不会唤醒CPU
AlarmManager.RTC;    
 // 让定时任务的触发时间从1970年1月1日0点算起,会唤醒CPU                
AlarmManager.RTC_WAKEUP;             
                
// 获取系统开机至今所经历时间的毫秒数                
SystemClock.elapsedRealtime();    
// 获取从1970年1月1日0点至今所经历时间的毫秒数    
SystemClock.currentThreadTimeMillis();

(四)Android省电开发之网络

除了常规的异步网络、多线程技术、本地缓存等等之外,Android省电开发还有一个重要的方面是:<code>WIFI</code>比蜂窝数据更省电:

  • a)尽量在Wi-Fi下传输数据,当然这是废话,不过可以考虑在有Wi-Fi的时候做预加载,比如应用中心的zip包、手Q web类应用的离线资源等;
  • b)非Wi-Fi下,尽量减少网络访问,每一次后台交互都要考虑是否必须。虽然WiFi接入方式已经占到移动互联网用户的50%,但是是有些手机设置为待机关闭WiFi连接,即便有Wi-Fi信号也只能切换到蜂窝数据;

一篇博文中的数据测试
灭屏情况:灭屏传输,高负载download的时候WiFi最省电(70mA),3G(270mA)和2G(280mA)相当,是WiFi的4倍左右;
亮屏情况:亮屏传输,高负载download的时候WiFi最省电(280mA),3G(360mA)和2G(370mA)相当,是WiFi的1.3倍左右;
所以在Android应用省电开发中,我们可以在获取网络方式的方面加以考虑。

(五)Android省电开发之Android L5.0(ART)登场

  • 1. 默认的ART运行模式
    安卓4.4系统中引入了全新的ART模式吗,相比之前流行已久的Dalvik模式有了很大的改变。
    《Android省电开发》

在Dalvik中,应用每次运行,字节码都需要通过即时编译器转换为机器码,这回拖慢应用的运行效率,而在ART环境中,应用在第一次安装时,字节码就会预先编译成机器码,使其成为了真正的本地应用。这个过程叫做预编译。这样的话,应用的启动和执行都会变得更加快速。但是ART的缺点是预编译完的机器人占用了更大的存储空间,应用的安装需要更长的时间。但是牺牲空间时间换取省电速度,在Android应用中还是可以接受的,毕竟性能的提升,运行速度的变快,体验更流畅,电池续航更久显得更重要。

  • 2.利用JobScheduler API

过去,如果开发人员想通过后台调取服务器数据,或完成某些处理工作,应用程序必须先监听是否有事件正在发生,并为自己设定一个唤醒时间,一旦应用程序开始运行,他需要检查各种环境条件,以确定是否具备条件让它完成工作,还是需要稍后再试,这种方式不仅复杂,而且容易出错,它会不断的浪费资源,比如当 一个应用程序被唤醒后,发现条件不符合就只能去睡觉并为下次唤醒再次设定时间,这是一个反复的过程。

这个问题,将引用<code>JobScheduler</code>来修复,它作为一个调度应用程序,负责当应用程序被唤醒时,提供适当的运行环境,所以开发者不用再让程序检测环境是否符合需求,开发人员只需要按照标准的流程来,调度程序会自动为唤醒的程序,准备好运行环境。应用程序可以使用这个调度程序,来唤醒他们,比如当设备连接到充电器后,调度程序将唤醒那些需要处理器工作的程序,让他们进行工作,或者在设备连接至WiFi网络的时候上传下载照片,更新内容等。该调度程序还支持一个时间窗口,以便它可以唤醒一组应用程序,这将使那些不需要精确唤醒时间,但每隔一两小时需要运行一次的程序能在同一时间点运行,这样就能让处理器保持更长时间的休眠。

<code>JobScheduler</code>的优势相当巨大,它不仅可以帮助手机节省电量, 实际由于不在需要监听,更改和设置报警,还可以帮助开发人员减少代码书写量。目前该JobScheduler类,已经加入Android L开发者预览版。

  • 3.各种省电新模式和新技术
    (1) 全新的Material Design风格用户界面
    新的用户界面更加简洁、色彩更加丰富。动画效果更加合理生动,同时加入实时阴影的3D视图,更多的使用卡片风格的显示效果。全平台风格也变得更为统一。

    (2) 新的通知消息系统
    允许用户管理通知中心中的通知消息。原先的Android通知栏几乎是处于无法控制的状态,所有的应用通知都会弹出,要想关闭只能进到每一个应用中去单独设置,或是使用第三方软件来实现统一管理。但是现在不用了,Android 5.0已经自带了通知管理界面。

    (3) 多任务界面有着全新的Google Now卡片风格
    通知中心融入更多的卡片式风格,即使是在锁屏状态下也可以进行多种功能操作。同时用户可以自定义通知的优先级别,使得用户不会错过任何重要的通知。还可以设置特定的通知权限,只有被允许的通知消息才会推送。同时还具有操作性,比如用户在游戏时有电话打入,不会以全屏显示,而是弹出可操作的通知卡片,用户可选择接听或拒接,不影响游戏继续进行。

    (4) Project Volta省电模式、BatterySaver省电模式
    Android 5.0的省电模式是通过<code>JobSchedulerAPI</code>以及自动调节屏幕亮度、刷新率来达到省电的效果,而且还使用了Project Volta量化每个应用的耗电量,在手机快没电的情况下主动降低CPU的主频或者关闭通讯模块,以获得更长的待机时间。还配备了一个<code>Battery Bistorian</code>电池历史记录功能,可让详细显示设备的耗电情况。

《Android省电开发》 梳理一下~

以上整理自网络~
如有错误还请大家多多指教啊哈哈

    原文作者:可一fly
    原文地址: https://www.jianshu.com/p/e77bbaf672ec
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞