0%

Android性能优化

关于Android性能优化的文章,网上有很多,涉及的点也很多,包括内存方面,界面方面,布局方面,代码编写方面。
基本遵循的规则有两个:

  1. 不做多余的工作
  2. 尽量少分配内存

除去编写代码时需注意的规范之外,在应用运行中也会有很多需要优化的地方,这部分的性能优化需要结合性能分析,有很多种性能分析的工具,对性能作系统的观测与研究。

性能分析方面会另外开辟,这里只是记录在优化上面的方式,还需花时间慢慢做到细化。不能说大而全,但也算是切中要点,以养成良好的习惯,在平时码代码的时候能够尽量避开,仅供所有人参考。

日志

  • 应保守使用Log
  • 正确使用Log级别。
    • ERROR:用于严重错误场景。
    • WARNNING:用于记录非预期场景发生时的信息,例如处理非法消息类型时使用。
    • INFO:用于记录关键路径的关键信息。
    • DEBUG:用于调试程序。DEBUG级别的日志最好不要出现在release版本当中,使用if(DEBUG)开关控制。
    • VERBOSE:用于临时调试。

UI相关

  • 采用硬件加速,在androidmanifest.xml中application添加android:hardwareAccelerated="true"。不过这个需要在android 3.0才可以使用。android4.0这个选项是默认开启的。
  • 使用merge优化布局层数,采用include共享布局。
  • View中设置缓存属性.setDrawingCache为true。
  • png图片采用压缩工具压缩。
  • 没有透明度要求的图片,应使用jpg,加快decode/compress速度。
  • 不要通过静态变量缓存Drawable对象。
  • 不要将同一个Drawable对象设置给多个控件。
  • 对于复杂的窗口,不要在顶层使用RelativeLayout、LinearLayout等multi-pass layout
  • 谨慎使用requestLayout(),会执行measure, layout, draw等操作。
  • 文字颜色尽量不设置透明度。
  • 自绘的View,invalidate应当计算最小的重绘区域,避免重绘整个View。
  • 使用EditText时,最好指定inputType,追求更高体验。
  • 不要在**onAnimationEnd()**中把自己从view tree中移除,AC下这个View还会继续绘制最后一帧,移除会导致parent的dispatchDraw出现崩溃。
  • View的框架事件中不能修改布局,否则会导致显示/响应错乱甚至崩溃,如:onMessure、onLayout、onDraw、onTouchEvent、dispatchKeyEvent、onInterupteXxx等。
  • View的框架事件中不宜创建对象,因其触发频度高,可能引起频繁GC进而影响性能。
  • 按钮尽量都设为singleLine(true),以防止按钮文本过长时出现折行。
  • 对于字体使用sp还是dp,看实际需要。Android官方推荐使用sp,设置为sp的文字会随系统文字大小变化而变化,dp则不会。
  • ListView中使用convertView复用控件;背景色与cacheColorHint设置相同颜色,提高滑动时渲染性能;item尽可能减少布局。

数据/内存相关

  • 避免在UI线程执行耗时操作
  • 尽量不在主线程做I/O、联网操作。
  • Service.onCreate()/onStart(),BroadcastReceiver.onReceive()都是在UI线程执行,因此不要在这些方法里面执行耗时操作。
  • 优先使用线程池管理线程。
  • 注意Cursor,文件I/O的关闭。
  • Message必须采用Message.Obtain()获取,Android2.x存在bug,使用new Message()获取的Message对象将会泄漏。
  • Activity的Context在Activity范围内使用,即UI相关的操作,其他情况使用Application Context。
  • 使用View.post()接口时,应确保当前对象处于View Tree上。
  • 有些能用文件操作的,尽量采用文件操作,文件操作的速度比数据库的操作要快10倍左右。

编码相关

  • static final声明常量。

  • 移位代替乘除运算。

  • 使用StringBufferStringBuilder替代String的构造操作。

  • StringBuilder没有同步保护,因此比StringBuffer要快;如果构建的字符串是局部变量,则没有线程安全问题,用StringBuilder效率高。

  • 字符串比较使用"html".equals(targetString)这种方法,避免空指针异常;

  • 谨慎使用synchronized;调用一个synchronized方法比没有synchronized的方法要慢。

  • 慎用反射。通过反射调用方法,访问成员变量要慢很多。

  • 尽量避免创建对象,使用引用。

  • 使用clone()方法避免构造一个新的对象。例如数组也是一个对象,可以用clone()方法拷贝数组。

  • 变量引用新对象之前先把变量置null;特别注意,在变量引用的旧对象占用大量内存时,及时把变量置null使得GC有机会及时把内存回收。

  • 如果不需要访问对象的属性,把方法声明为static,这样方法的调用会快15%~20%。

  • 如果不希望方法被重载,把方法声明为final

  • 类的内部不要用getter/setter访问成员变量,Android下虚函数的调用代价高昂。把成员设为public,直接访问。

  • 使用增强for循环foreachfor (obj : objs)

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

  • 容器使用完先用clear()或removeAllElements()清空然后再置null;数组直接置null就可以,而不用每个数组元素都置null。

  • 少使用浮点数浮点数整形慢两倍。

  • 尽量原生数据类型替代包装类。使用int替代Interger

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

  • 少用inner class,在主类实现相应的Listener接口,处理event;inner class和anonymous class都是另外一个类,运行时都要加载到内存。但这是以牺牲main class的重用性为代价。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class MyActivity extends Activity { 
    protected void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.content_layout_id);
    final Button button = (Button) findViewById(R.id.button_id);
    button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
    // Perform action on click
    }
    });
    }
    }

    // 编译时会多出一个类来,如果改成如下实现,则只有一个类。
    public class MyActivity extends Activity implements View.OnClickListener
    // …
    button.setOnClickListener(this);
  • Vector,HashTable和Stack的访问都是同步的,因此每次访问都有同步开销;不过不需要同步访问,考虑HashMap,ArrayList。

  • 尽量使用索引去操作容器。例如用ArrayList. remove(int index)比ArrayList. remove(Object object)要快,因为它不用去查找索引。

  • 尽量使用有快速hashcode()方法的类型做hashtable的key,推荐Number类型,例如Integer, Short等,避免使用String类型的key。

  • 创建容器(Map、List、Set等)时指定适当的capacity,以减少扩容时的性能和内存损耗。