引用了大量API文档的内容,没有对超链接进行删改,有需要的朋友可以直接点超链接访问API文档。

上一篇关于主要内存的性能优化,这一篇主要关于编码。

Performance tips

This document primarily covers micro-optimizations that can improve overall app performance when combined, but it's unlikely that these changes will result in dramatic performance effects. Choosing the right algorithms and data structures should always be your priority, but is outside the scope of this document. You should use the tips in this document as general coding practices that you can incorporate into your habits for general code efficiency.

There are two basic rules for writing efficient code:

  • Don't do work that you don't need to do.
  • Don't allocate memory if you can avoid it.

One of the trickiest problems you'll face when micro-optimizing an Android app is that your app is certain to be running on multiple types of hardware. Different versions of the VM running on different processors running at different speeds. It's not even generally the case that you can simply say "device X is a factor F faster/slower than device Y", and scale your results from one device to others. In particular, measurement on the emulator tells you very little about performance on any device. There are also huge differences between devices with and without a JIT: the best code for a device with a JIT is not always the best code for a device without.

To ensure your app performs well across a wide variety of devices, ensure your code is efficient at all levels and aggressively optimize your performance.

https://developer.android.google.cn/training/articles/perf-tips


布局性能优化

避免创建不必要的对象

Avoid creating unnecessary objects

Object creation is never free. A generational garbage collector with per-thread allocation pools for temporary objects can make allocation cheaper, but allocating memory is always more expensive than not allocating memory.

As you allocate more objects in your app, you will force a periodic garbage collection, creating little "hiccups" in the user experience. The concurrent garbage collector introduced in Android 2.3 helps, but unnecessary work should always be avoided.

Thus, you should avoid creating object instances you don't need to. Some examples of things that can help:

  • If you have a method returning a string, and you know that its result will always be appended to a StringBuffer anyway, change your signature and implementation so that the function does the append directly, instead of creating a short-lived temporary object.
  • When extracting strings from a set of input data, try to return a substring of the original data, instead of creating a copy. You will create a new String object, but it will share the char[]with the data. (The trade-off being that if you're only using a small part of the original input, you'll be keeping it all around in memory anyway if you go this route.)

A somewhat more radical idea is to slice up multidimensional arrays into parallel single one-dimension arrays:

  • An array of ints is a much better than an array of Integer objects, but this also generalizes to the fact that two parallel arrays of ints are also a lot more efficient than an array of (int,int) objects. The same goes for any combination of primitive types.
  • If you need to implement a container that stores tuples of (Foo,Bar) objects, try to remember that two parallel Foo[] and Bar[] arrays are generally much better than a single array of custom (Foo,Bar) objects. (The exception to this, of course, is when you're designing an API for other code to access. In those cases, it's usually better to make a small compromise to the speed in order to achieve a good API design. But in your own internal code, you should try and be as efficient as possible.)

Generally speaking, avoid creating short-term temporary objects if you can. Fewer objects created mean less-frequent garbage collection, which has a direct impact on user experience.

下面来看一些我们可以避免创建对象的场景:

  1. 如果我们有一个需要拼接的字符串,那么可以优先考虑使用StringBuffer或者StringBuilder来进行拼接。
  2. 尽量使用基本数据类来代替封装数据类型,inttInteger要更加高效,其它数据类型也是一样。
  3. 如果我们明确地知道调用方会将这个返回的String再进行拼接操作的话,可以考虑返回一个StringBuffer对象来代替。
  4. 基本数据类型要优于对象数据类型,类似地,基本数据类型的数组也要优于对象数据类型的数组。两个平行的数组要比一个封装好的对象数组更加高效,举个例子,Foo[]和Bar[]这样的两个数组,使用起来Custom(Foo,Bar)这样的一个数组高效得多。

我们所要遵守的一个基本原则就是尽可能地少创建临时对象,越少的对象意味着越少的GC(garbage collection)操作,同时也就意味着越好的程序性能和用户体验。


静态优于抽象

If you don't need to access an object's fields, make your method static. Invocations will be about 15%-20% faster. It's also good practice, because you can tell from the method signature that calling the method can't alter the object's state.

如果你并不需要访问一个对象中的某些字段,只是想调用它的某个方法来去完成-项通用的功能,那么可以将这个方法设置成静态方法,这会让调用的速度提升15% 20%,同时也不用为了调用这个方法而去专门创建对象了,这样还满足了上面的一条原则。另外这也是一种好的编程习惯。 因为我们可以放心地调用静态方法,而不用担心调用这个方法后是否会改变对象的状态(静态方法内无法访问非静态字段)。

对常量使用static final修饰符

Use static final for constants

Consider the following declaration at the top of a class:

static int intVal = 42;
static String strVal = "Hello, world!";

The compiler generates a class initializer method, called <clinit>, that is executed when the class is first used. The method stores the value 42 into intVal, and extracts a reference from the classfile string constant table for strVal. When these values are referenced later on, they are accessed with field lookups.

We can improve matters with the "final" keyword:

static final int intVal = 42;
static final String strVal = "Hello, world!";

The class no longer requires a <clinit> method, because the constants go into static field initializers in the dex file. Code that refers to intVal will use the integer value 42 directly, and accesses to strVal will use a relatively inexpensive "string constant" instruction instead of a field lookup.

Note: This optimization applies only to primitive types and String constants, not arbitrary reference types. Still, it's good practice to declare constants static final whenever possible.

static int intVal = 42;
static String strVal=“Hello, world!”;

编译器会为上述代码生成一个初始化方法,称为<clinit>方法,该方法会在定义类第一次被使用的时候调用。然后这个方法会将42的值赋值到intVal当中,并从字符串常量表中提取一个引用赋值到strVal上。当赋值完成后,我们就可以通过字段搜寻的方式来去访问具体的值了。

static final int intVal = 42;
static final String strVal = “Hello, world!” ;

修改之后,定义类不再需要< clinit>方法, 所有的常量都会在dex文件的初始化器中初始化。当调用intVal时可以直接指向42的值, 而调用strVal时会用一种相对轻量级的字符串常量方式,而不是字段搜寻的方式。

这种优化方式只对基本数据类型以及String类型的常量有效,对于其它数椐类型的常量是无效的。用static final声明常量是-种非常好的习惯。


避免在内部调用GettersSetters方法

在Android,上这个技巧就不再是那么的受推崇了,因为字段搜寻要比方法调用效率高得多,我们直接访问某个字段可能要比通过getters方法来去访问这个字段快3到7倍。

不过我们肯定不能仅仅因为效率的原因就将封装这个技巧给抛弃了,编写代码还是要按照面向对象思维的。

但是我们可以在能优化的地方进行优化,比如说避免在内部调用getters/setters方法。


使用增强的For循环语法

The enhanced for loop (also sometimes known as "for-each" loop) can be used for collections that implement the Iterable interface and for arrays. With collections, an iterator is allocated to make interface calls to hasNext() and next(). With an ArrayList, a hand-written counted loop is about 3x faster (with or without JIT), but for other collections the enhanced for loop syntax will be exactly equivalent to explicit iterator usage.

There are several alternatives for iterating through an array:

static class Foo {int splat;
}Foo[] array = ...public void zero() {int sum = 0;for (int i = 0; i < array.length; ++i) {sum += array[i].splat;}
}public void one() {int sum = 0;Foo[] localArray = array;int len = localArray.length;for (int i = 0; i < len; ++i) {sum += localArray[i].splat;}
}public void two() {int sum = 0;for (Foo a : array) {sum += a.splat;}
}

zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.

one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.

two() is fastest for devices without a JIT, and indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.

So, you should use the enhanced for loop by default, but consider a hand-written counted loop for performance-critical ArrayList iteration.

Tip: Also see Josh Bloch's Effective Java, item 46.

增强型for循环(他被称为for-each循环)可以用于去遍历实现Iterable接口的集合以及数组,用Collections,会构造一个Iterator接口去调用hasNext[]和next(),用一个Arraylist,手写计数循环要快3倍(有或没有JIT),但其他集合的增强的for循环的语法是完全等价的显式送代器的使用。

?API文档提供了三种迭代数组的方法
三种方法的分析结果:

  • zero()方法是最慢, 因为它是把mArray. length写在循环当中的, 也就是说每循环- 次都需要重新计算- 次mArray的长度。
  • one()方法则相对快得多, 因为它使用了一个局部变量len来记录数组的长度,这样就省去了每次循环时字段搜寻的时间。
  • two()方法在没有JIT (Just In Time Compiler) 的设备上是运行最快的,而在有JIT的设备上运行效率和one()方法不相上下,唯一需要注意的是这种写法需要JDK 1.5之后才支持。
  • 所以,在默认情况下,我们应该使用增强for, 但在遍历ArrayList时, 我们还是使用手写循环的方式

考虑包而不是私有的内部类访问

Consider package instead of private access with private inner classes

Consider the following class definition:

public class Foo {private class Inner {void stuff() {Foo.this.doStuff(Foo.this.mValue);}}private int mValue;public void run() {Inner in = new Inner();mValue = 27;in.stuff();}private void doStuff(int value) {System.out.println("Value is " + value);}
}

What's important here is that we define a private inner class (Foo$Inner) that directly accesses a private method and a private instance field in the outer class. This is legal, and the code prints "Value is 27" as expected.

The problem is that the VM considers direct access to Foo's private members from Foo$Inner to be illegal because Foo and Foo$Inner are different classes, even though the Java language allows an inner class to access an outer class' private members. To bridge the gap, the compiler generates a couple of synthetic methods:

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

The inner class code calls these static methods whenever it needs to access the mValue field or invoke the doStuff() method in the outer class. What this means is that the code above really boils down to a case where you're accessing member fields through accessor methods. Earlier we talked about how accessors are slower than direct field accesses, so this is an example of a certain language idiom resulting in an "invisible" performance hit.

If you're using code like this in a performance hotspot, you can avoid the overhead by declaring fields and methods accessed by inner classes to have package access, rather than private access. Unfortunately this means the fields can be accessed directly by other classes in the same package, so you shouldn't use this in public API.

看下面的代码:

public class Foo {private class Inner {void stuff() {Foo.this.doStuff(Foo.this.mValue);}}private int mValue;public void run() {Inner in = new Inner();mValue = 27;in.stuff();}private void doStuff(int value) {System.out.println("Value is " + value);}
}

可以通过声明被内部类访问的字段和成员为包访问权限,而非私有。但这也意味着这些字段会被其他处于同一个包中的类访问,因此在公共API中不宜采用。


避免使用浮点数

Avoid using floating-point

As a rule of thumb, floating-point is about 2x slower than integer on Android-powered devices.

In speed terms, there's no difference between float and double on the more modern hardware. Space-wise, double is 2x larger. As with desktop machines, assuming space isn't an issue, you should prefer double to float.

Also, even for integers, some processors have hardware multiply but lack hardware divide. In such cases, integer division and modulus operations are performed in software—something to think about if you're designing a hash table or doing lots of math.

通常的经验是,在Android设 备中,浮点数会比整型慢2倍。

  • 从速度方面说,在现代硬件上, float和double之间没有任何不同。 更广泛的讲,double大2倍。 在台式机上,由于不存在空间问题,double的优先 级高于float。
  • 但即使是整型,有的芯片拥有硬件乘法,却缺少除法。这种情况下,整型除法和求模运算是通过软件实现的,就像当你设计Hash表,或是做大量的算术那样。

多使用系统封装好的API

Know and use the libraries

In addition to all the usual reasons to prefer library code over rolling your own, bear in mind that the system is at liberty to replace calls to library methods with hand-coded assembler, which may be better than the best code the JIT can produce for the equivalent Java. The typical example here is String.indexOf() and related APIs, which Dalvik replaces with an inlined intrinsic. Similarly, the System.arraycopy() method is about 9x faster than a hand-coded loop on a Nexus One with the JIT.

Tip: Also see Josh Bloch's Effective Java, item 47.

系统的API在很多时候比我们自己写的代码要快得多,它们的很多功能都是通过底层的汇编模式执行的。
如果我们要实现一个数组拷贝的功能,使用循环的方式来对数组中的每一个元素一一进行赋值当然是可行的,但是如果我们直接使用系统中提供的System.arraycopy()方法将会让执行效率快9倍以上。


使用实现类比接口好

比如:

Map map1 = new HashMap();
HashMap map2 = new HashMap();

调用一个接口的引用会比调用实体类的引用多花一倍的时间。


将成员变量缓存到本地

访问成员变量比访问本地变量慢得多

for(int i=0;i<this.mCount;i++)dumpItem(this.mItems[i]);

再改成这样:

int count = this.mCount;
Item[] items = this.mItems;
for(int i = 0;i<count;i++)dumpItem(items[i]);
//(使用“this”是为了表明这些是成员变量)

另一个相似原则是:

  • 永远不要在for的第二个条件中调用任何方法。比如在每次循环的时候都会调用getCount()方法,这样做比你在一个int先把结果保存起来开销大很多。

访问成员变量时,如果将它缓存到本地,成员变量的访问就会变成效率更高的栈变量访问。

安卓性能优化——提高编码性能的技巧相关推荐

  1. java 代码性能优化_Java代码性能优化的几个小技巧

    Java代码性能优化的几个小技巧 时间:2017-08-07     来源:华清远见JAVA学院 代码优化是程序员必须懂得一门学问,所以不管是程序员还是准程序员,养成良好的代码优化习惯都是必须要养成的 ...

  2. 服务器性能优化之网络性能优化

    hi ,大家好,今天分享一篇后台服务器性能优化之网络性能优化,希望大家对Linux网络有更深的理解. 曾几何时,一切都是那么简单.网卡很慢,只有一个队列.当数据包到达时,网卡通过DMA复制数据包并发送 ...

  3. 服务器性能优化和Mysql性能优化

    服务器性能优化和Mysql性能优化 影响性能的几个因素 服务器硬件 CPU 内存 IO子系统 服务器系统 CentOS系统参数优化 sysctlconf 优化 limitconf参数优化 磁盘调度策略 ...

  4. 电脑性能,如何提高电脑性能 方法介绍【图文教程】

    在这个信息传播飞速的当下,网络是人们必不可少的传播工具,网络传播信息的途径就是通过电脑,电脑如今已经是每家每户的宠儿了,几乎每个人都有自己的电脑.然而电脑与电脑之间也有差异,它们最大的不同就是性能方面 ...

  5. 细说接口性能优化的11个小技巧

    前言 接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题. 该问题说简单也简单,说复杂也复杂. 有时候,只需加个索引就能解决问题. 有时候,需要做代码重构. ...

  6. 详细的聊聊接口性能优化的11个小技巧 不收藏对不起我

    前言 接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题. 该问题说简单也简单,说复杂也复杂. 有时候,只需加个索引就能解决问题. 有时候,需要做代码重构. ...

  7. 聊聊接口性能优化的11个小技巧

    点击下方"IT牧场",选择"设为星标" 前言 接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题. 该问题说简单也简 ...

  8. Web性能优化实践——应用层性能优化

    随着公司项目的进一步推广,用户数量的增加,已经面临着单台服务器不能负载的问题. 这次的优化由于时间关系主要分两步走,首先优化应用层代码以提高单台服务器的负载和吞吐率.之后再进行分表,引入队列.MemC ...

  9. 性能优化指南:性能优化的一般性原则与方法

    作为一个程序员,性能优化是常有的事情,不管是桌面应用还是web应用,不管是前端还是后端,不管是单点应用还是分布式系统.本文从以下几个方面来思考这个问题:性能优化的一般性原则,性能优化的层次,性能优化的 ...

最新文章

  1. 修复mysql数据库供应商_修复MYSQL数据库
  2. DiscuzX 论坛首页 和 分 区设置版块横排
  3. 【codeforces 796A】Buying A House
  4. MATLAB一维数组的创建与元素提取
  5. java安装的时候无效参数,spring-boot 打成jar把后 启动时指定参数无效
  6. POJ1067 HDU1527 取石子游戏【博弈】
  7. 六款WEB上传组件性能测试与比较
  8. MYSQL登录和常见命令
  9. 关于单链表结构体定义结点时 LNode *LinkList的理解
  10. Pycharm Debug调试(纯干货)
  11. mysql 内联 外联_sql中的内联和外联(简单用法)
  12. 洛谷 P3939 数颜色(主席树)
  13. 从小白的角度理解二项分布、几何分布和泊松分布
  14. setDaemon python守护进程,队列通信子线程
  15. OkHttp相关知识(三)
  16. K8S组件介绍及安装
  17. 【转】工作3个月后个人对测绘专业的重新反思(重新思考武大测绘学科就业问题和读研问题)...
  18. 将电脑本地视频转成rtsp和rtmp视频流
  19. 微信小程序 简单易用 下拉框组件
  20. mastercam2017后处理升级_如何升级Mastercam 9.1版后处理?

热门文章

  1. 基于SpringBoot的健身房管理系统(提供源码)
  2. 2.4G无线音频传输方案
  3. 智慧养老智能化解决方案为老人圆居家养老的梦-新导智能
  4. 空格键删字符解决办法
  5. No cached version available for offline mode解决办法
  6. 利用Synonyms库对论文进行降重
  7. VGA图像显示总结(彩条显示、方块移动、字符显示、基于ROM的图片显示)
  8. ios英语口语800句应用源码
  9. 【Android App】给三维的地球仪贴上动物贴纸实战(附源码和演示 超详细必看)
  10. char[]字符数组使用toString变为乱码的原因(其实不是乱码,是“类名+@+地址”)