Android App开发除了NDK之外,使用的都是Java语言,而Java语言是一种基于虚拟机JVM运行的语言,所以相比C/C++语言来说,效率是比较低的。Java需要占用大量内存来换取执行速度,而不定期的GC机制,直接导致Android界面的卡顿现象。
相比Apple的iOS,Android要面对无数不同的硬件组合,分辨率,驱动等,导致App质量参差不齐。
性能优化,简而言之,就是提高我们程序的性能,让我们的应用更快,更少使用CPU资源,更少使用内存。
本文从以下几个方面浅谈一下Android的性能优化问题:
版本较低的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的核心类。
异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillnStackTrace()的本地(Native)方法,fillnStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。
不要重复初始化变量,默认情况下,调用类的构造函数时,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%。
用位操作代替乘除法;
用StringBuilder代替拼接操作;
有算法进行复杂度的改进(必要的时候可以空间换时间);
合理利用浮点数,浮点数比整形慢两倍。
网络优化、异步线程、图片使用缓存等。
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”/>
viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。
5. 通过Android SDK中tools目录下的layoutopt命令查看你的布局是否需要优化。
6. 将Activity中的window的背景图设置为空.getWindow().setBackgroundDrawable(null);android默认的背景不为空。
7. View中设置缓存属性.setDrawingCache为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();
使用事务好处是原子提交和更优性能。
4. 可以建立索引,增加检索的速度。(当某字段数据更新频率较低,查询频率较高,经常有范围查询(>,<, =, >=, <=)或orderby、group by发生时建议使用索引; 经常同时存取多列,且每列都含有重复值可考虑建立复合索引)。
5. 批量插入、更新使用原子操作,采用事务等方式。
6. 查询时返回更少的结果集及更少的字段。
7. 少用cursor.getColumnIndex(可以在建表的时候用static变量记住某列的index,直接调用相应index而不是每次查询。)
8. 优化sql语句字符串等,语句的拼接使用StringBuilder代替String。
1. 采用JNI技术,将耗时间的处理放到c/c++层处理。适当的采用NDK编程可以提高效率,但是内存回收不稳定,所以说适当。
2. 懒加载和缓存机制:访问网络的耗时操作启动一个新线程来做,而不要在UI线程做。还有从网络上获取大量图片的时候,可以本地缓存,下次获取同样图片的时候,可以先从本地获取,本地无再从网络获取。
3. 通过Android SDK中tools目录下的layoutopt命令查看你的布局是否需要优化;hierarchy viewer可以方便的查看Activity的布局,各个View的属性、measure、layout、draw的时间,如果耗时较多会用红色标记,否则显示绿色;利用android自带的性能跟踪工具TraceView查看跟踪函数调用,跟踪方法跟踪各部分的执行效率。
4. 可以在方法执行前后各使用system.currentTimeMillis方法获取当前系统的时间(毫秒),两个时间之差可以计算出方法的执行时间,进而可以改进优化。
5. 获取系统的内存信息
//获取系统内存总数 Long total = Runtime.getRuntime().totalMemory(); //获取剩余内存 Long free = Runtime.getRuntime().freeMemory(); //获取已经使用内存 Long used = total – free。
http://www.trinea.cn/android/performance/(非常好的文章)
http://rayleeya.iteye.com/blog/1961005
http://developer.android.com/training/articles/perf-tips.html (java)
http://rayleeya.iteye.com/blog/1961005 (java)
http://www.trinea.cn/android/java-android-performance/ (java)