AndroidTrainingCHS学习笔记 -- 代码性能优化建议

来源:互联网 发布:洛阳市博物馆 知乎 编辑:程序博客网 时间:2024/05/23 01:14

本文主要介绍了一些细节优化技巧当这些小技巧综合使用时虽然不能大幅度提高性能,但是对于提升性能还是有很大作用的.我们可以将本文所提到的小技巧作为平时写代码的习惯.(也可以通过选择合适的算法和数据结构来提高性能.)

通常来说高效代码满足以下两个规则 :

  1. 不要做冗余动作.
  2. 如果能避免尽量不要分配内存.

1. 避免创建不必要的对象

虽然GC可以回收不用的对象,但是分配和会回收这些对象也是需要效果资源的.因此尽量避免创建不必要的内存对象. 举例如下 :

  • 如果需要返回一个String对象,并且你知道最终会被连接到一个StringBuilder上,尽量避免直接连接,创建一个临时变量来进行这个操作.

  • 当回去String对象的一部分时直接返回subString 而不是创建一个新的String.

通常来说, 需要避免创建更多的对象. 更少的对象意味着更少的GC动作.GC对用户体验有比较直接的影响.

2. 选择 static而不是 Virtual.

如果一个方法不需要访问对象的值域, 请保证这个方法是static 类型的.这样方法调用将快 15%~20%.

3. 常量声明为 static final

先看下面的声明

static int intValue = 42;static String stringValue = "Hello world";

编译器会在类首次被使用到的时候,使用初始化 <clinit> 方法来初始化上面的值,之后使用都需要先查找然后再返回. 我们可以使用static final来提高性能.

static final int intValue = 42;static final String stringValue = "Hello world";

此时就没有上面的查找动作了

4. 避免内部的 Getters/Setters

使用Getters/Setters访问比起直接访问变量要消耗更多的资源.因此更加合理的做法是 :

  • 在面向对象时使用Getter/Setter.
  • 在类的内部应该直接访问变量.

没有JIT(Just In Time Compiler)时, 直接访问的速度是调用getter 的 3 倍.
有JIT时是 7 倍.

5. 使用增强for循环写法

static class Foo {int mSplat;} Foo[] mArray = ...// 方式一 : 每次都访问数组长度.public void zero() {    int sum = 0;    for (int i = 0; i < mArray.length; ++i) {        sum += mArray[i].mSplat;    }} // 方式二 : 提前取出数组长度.public void one() {    int sum = 0;    Foo[] localArray = mArray;    int len = localArray.length;    for (int i = 0; i < len; ++i) {        sum += localArray[i].mSplat;    }} // 方式三 : 增强for循环public void two() {    int sum = 0;    for (Foo a : mArray) {        sum += a.mSplat;    }}
  • zero() 方法是最慢的.
  • one() 稍微快些.
  • two() 在没有JIT时是最快的.可是经过JIT后和one()方法差不多快.

    尽量使用增强for循环但是对于ArrayList请使用one()

6. 使用包级访问而不是内部类的私有访问

参考下面一段代码

public class Foo{    // 内部类.    private class Inner{        // 在该方法中访问外部类的变量和方法.        void stuff(){            Foo.this.doStuff(Foo.this.mValue);        }    }    // 外部类私有变量    private int mValue;    // 外部类私有方法    private void doStuff(int value){        System.out.println("Value is : " + value);    }    public void run(){        Inner in = new Inner();        mValue = 27 ;        in.stuff();    }} 

这里我们定义了一个内部类(Foo$Inner),它直接访问了外部类的私有方法私有变量. 这是合法的.
但是, Foo 和 Inner 是不同的类. 因此为了让Inner访问Foo的私有方法和变量,编译器会生成一些仿造函数

/*package 访问权限*/ static int Foo.access$100(Foo foo) {    return foo.mValue;} /*package 访问权限*/ static void Foo.access$200(Foo foo, int value) {    foo.doStuff(value);}

每当内部类需要访问外部类中的mValue成员或需要调用doStuff()函数时, 它都会调用这些静态方法。这样的话就和使用Getter/Setter进行访问是一样的.速度会慢一些.
开发建议 :
可以考虑将内部类访问的方法 声明称包访问权限.而不是私有访问.但是这样在同一个包中的其他类也可以直接访问这些域, 因此在公开的API中尽量不要这样.只能选择以性能换安全.

7. 避免使用float类型.

在Android系统中float的存取速度是int类型的 0.5 倍.因此尽量有限使用int.

8. 尽量使用库函数.

尽量使用System.arraycopy()等一些封装好的库函数, 他的效率是手动写copy实现的9倍多.

9. 谨慎使用native方法

当你需要把已经存在的native code迁移到Android, 请谨慎使用JNI。 如果你要使用JNI,请学习JNI Tips

10. 关于性能的误区

在没有做JIT之前, 使用一种确切的数据类型确实要比抽象的数据类型速度要更有效率。(例如, 使用HashMap要比Map效率更高. ) 有误传效率要高一倍,实际上只是6%左右。而且,在JIT之后,他们直接并没有大多差异。

11. 关于测量

上面文档中出现的数据是Android的实际运行效果。 我们可以用Traceview 来测量, 但是测量的数据是没有经过JIT优化的,所以实际的效果应该是要比测量的数据稍微好些。
关于如何测量与调试, 还可以参考下面两篇文章:

  • Profiling with Traceview and dmtracedump
  • Analyzing UI Performance with Systrace
0 0
原创粉丝点击