(一)内存优化html
Android系统对每一个软件所能使用的RAM空间进行了限制(如:Nexus one 对每一个软件的内存限制是24M),同时Java语言自己比较消耗内存,dalvik虚拟机也要占用必定的内存空间。java
1) 了解JITandroid
即时编译(Just-in-time Compilation,JIT),又称动态转译(Dynamic Translation),是一种经过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术。即时编译前期的两个运行时理论是字节码编译和动态编译。Android原来Dalvik虚拟机是做为一种解释器实现,新版(Android2.2+)将换成JIT编译器实现。性能测试显示,在多项测试中新版本比旧版本提高了大约6倍。程序员
详细请参考 http://hi.baidu.com/cool_parkour/blog/item/2802b01586e22cd8a6ef3f6b.htmlweb
2) 避免建立没必要要的对象正则表达式
虽然gc为每一个线程都创建了临时对象池,可使建立对象的代价变得小一些,可是分配内存永远都比不分配内存的代价大。若是你在用户界面循环中分配对象内存,就会引起周期性的垃圾回收,用户就会以为界面像打嗝同样一顿一顿的。因此,除非必要,应尽量避免尽力对象的实例。下面的例子将帮助你理解这条原则:当你从用户输入的数据中截取一段字符串时,尽可能使用substring函数取得原始数据的一个子串,而不是为子串另外创建一份拷贝。这样你就有一 个新的String对象,它与原始数据共享一个char数组。 若是你有一个函数返回一个String对象,而你确切的知道这个字符串会被附加到一个StringBuffer,那么,请改变这个函数的参数和实现方式, 直接把结果附加到StringBuffer中,而不要再创建一个短命的临时对象。算法
一个更极端的例子是,把多维数组分红多个一维数组:数据库
int数组比Integer数组好,这也归纳了一个基本事实,两个平行的int数组比 (int,int)对象数组性能要好不少。同理,这试用于全部基本类型的组合。若是你想用一种容器存储(Foo,Bar)元组,尝试使用两个单独的 Foo[]数组和Bar[]数组,必定比(Foo,Bar)数组效率更高。(也有例外的状况,就是当你创建一个API,让别人调用它的时候。这时候你要注重对API接口的设计而牺牲一点儿速度。固然在API的内部,你仍要尽量的提升代码的效率)canvas
整体来讲,就是避免建立短命的临时对象。减小对象的建立就能减小垃圾收集,进而减小对用户体验的影响。数组
3) 静态方法代替虚拟方法
若是不须要访问某对象的字段,将方法设置为静态,调用会加速15%到20%。这也是一种好的作法,由于你能够从方法声明中看出调用该方法不须要更新此对象的状态。
4) 避免内部Getters/Setters
在源生语言像C++中,一般作法是用Getters(i=getCount())代替直接字段访问(i=mCount)。这是C++中一个好的习惯,由于编译器会内联这些访问,而且若是须要约束或者调试这些域的访问,你能够在任什么时候间添加代码。而在Android中,这不是一个好的作法。虚方法调用的代价比直接字段访问高昂许多。一般根据面向对象语言的实践,在公共接口中使用Getters和Setters是有道理的,但在一个字段常常被访问的类中宜采用直接访问。无JIT时,直接字段访问大约比调用getter访问快3倍。有JIT时(直接访问字段开销等同于局部变量访问),要快7倍。
5) 将成员缓存到本地
访问成员变量比访问本地变量慢得多,下面一段代码:
for
(
int
i =
0
; i <
this
.mCount; i++) {
dumpItem(
this
.mItems);
}
|
最好改为这样:
int
count =
this
.mCount;
Item[] items =
this
.mItems;
for
(
int
i =
0
; i < count; i++) {
dumpItems(items);
}
|
另外一个类似的原则是:永远不要在for的第二个条件中调用任何方法。以下面方法所示,在每次循环的时候都会调用getCount()方法,这样作比你在一个int先把结果保存起来开销大不少。
for
(
int
i =
0
; i <
this
.getCount(); i++) {
dumpItems(
this
.getItem(i));
}
|
一样若是你要屡次访问一个变量,也最好先为它创建一个本地变量,例如:
protected
void
drawHorizontalScrollBar(Canvas canvas,
int
width,
int
height) {
if
(isHorizontalScrollBarEnabled()) {
intsize = mScrollBar.getSize(
false
);
if
(size <=
0
) {
size = mScrollBarSize;
}
mScrollBar.setBounds(
0
, height - size, width, height);
mScrollBar.setParams(computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(),
false
);
mScrollBar.draw(canvas);
}
}
|
这里有4次访问成员变量mScrollBar,若是将它缓存到本地,4次成员变量访问就会变成4次效率更高的栈变量访问。
另外就是方法的参数与本地变量的效率相同。
1) 对常量使用static final修饰符
让咱们来看看这两段在类前面的声明:
static
int
intVal =
42
;
static
String strVal =
"Hello, world!"
;
|
必以其会生成一个叫作clinit的初始化类的方法,当类第一次被使用的时候这个方法会被执行。方法会将42赋给intVal,而后把一个指向类中常量表 的引用赋给strVal。当之后要用到这些值的时候,会在成员变量表中查找到他们。 下面咱们作些改进,使用“final”关键字:
static final int intVal = 42; static final String strVal = "Hello, world!";
如今,类再也不须要clinit方法,由于在成员变量初始化的时候,会将常量直接保存到类文件中。用到intVal的代码被直接替换成42,而使用strVal的会指向一个字符串常量,而不是使用成员变量。
将一个方法或类声明为final不会带来性能的提高,可是会帮助编译器优化代码。举例说,若是编译器知道一个getter方法不会被重载,那么编译器会对其采用内联调用。
你也能够将本地变量声明为final,一样,这也不会带来性能的提高。使用“final”只能使本地变量看起来更清晰些(可是也有些时候这是必须的,好比在使用匿名内部类的时候)。
2) 使用改进的For循环语法
改进for循环(有时被称为for-each循环)可以用于实现了iterable接口的集合类及数组中。在集合类中,迭代器让接口调用 hasNext()和next()方法。在ArrayList中,手写的计数循环迭代要快3倍(不管有没有JIT),但其余集合类中,改进的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;
}
}
public
void
two() {
int
sum =
0
;
for
(Foo a : mArray) {
sum += a.mSplat;
}
}
}
|
在zero()中,每次循环都会访问两次静态成员变量,取得一次数组的长度。
在one()中,将全部成员变量存储到本地变量。
two()使用了在java1.5中引入的foreach语法。编译器会将对数组的引用和数组的长度保存到本地变量中,这对访问数组元素很是好。 可是编译器还会在每次循环中产生一个额外的对本地变量的存储操做(对变量a的存取)这样会比one()多出4个字节,速度要稍微慢一些。
3) 避免使用浮点数
一般的经验是,在Android设备中,浮点数会比整型慢两倍,在缺乏FPU和JIT的G1上对比有FPU和JIT的Nexus One中确实如此(两种设备间算术运算的绝对速度差大约是10倍)从速度方面说,在现代硬件上,float和double之间没有任何不一样。更普遍的讲,double大2倍。在台式机上,因为不存在空间问题,double的优先级高于float。但即便是整型,有的芯片拥有硬件乘法,却缺乏除法。这种状况下,整型除法和求模运算是经过软件实现的,就像当你设计Hash表,或是作大量的算术那样,例如a/2能够换成a*0.5。
4) 了解并使用类库
选择Library中的代码而非本身重写,除了一般的那些缘由外,考虑到系统空闲时会用汇编代码调用来替代library方法,这可能比JIT中生成的等价的最好的Java代码还要好。
i. 当你在处理字串的时候,不要吝惜使用String.indexOf(),String.lastIndexOf()等特殊实现的方法。这些方法都是使用C/C++实现的,比起Java循环快10到100倍。
ii. System.arraycopy方法在有JIT的Nexus One上,自行编码的循环快9倍。
iii. android.text.format包下的Formatter类,提供了IP地址转换、文件大小转换等方法;DateFormat类,提供了各类时间转换,都是很是高效的方法。
详细请参考 http://developer.android.com/reference/android/text/format/package-summary.html
iv. TextUtils类
对于字符串处理Android为咱们提供了一个简单实用的TextUtils类,若是处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类,详细请参考http://developer.android.com/reference/android/text/TextUtils.html
v. 高性能MemoryFile类。
不少人抱怨Android处理底层I/O性能不是很理想,若是不想使用NDK则能够经过MemoryFile类实现高性能的文件读写操做。MemoryFile适用于哪些地方呢?对于I/O须要频繁操做的,主要是和外部存储相关的I/O操做,MemoryFile经过将 NAND或SD卡上的文件,分段映射到内存中进行修改处理,这样就用高速的RAM代替了ROM或SD卡,性能天然提升很多,对于Android手机而言同时还减小了电量消耗。该类实现的功能不是不少,直接从Object上继承,经过JNI的方式直接在C底层执行。
详细请参考 http://developer.android.com/reference/android/os/MemoryFile.html
在此,只简单列举几个经常使用的类和方法,更多的是要靠平时的积累和发现。多阅读Google给的帮助文档时颇有益的。
5) 合理利用本地方法
本地方法并非必定比Java高效。最起码,Java和native之间过渡的关联是有消耗的,而JIT并不能对此进行优化。当你分配本地资源时 (本地堆上的内存,文件说明符等),每每很难实时的回收这些资源。同时你也须要在各类结构中编译你的代码(而非依赖JIT)。甚至可能须要针对相同的架构 来编译出不一样的版本:针对ARM处理器的GI编译的本地代码,并不能充分利用Nexus One上的ARM,而针对Nexus One上ARM编译的本地代码不能在G1的ARM上运行。当你想部署程序到存在本地代码库的Android平台上时,本地代码才显得尤其有用,而并不是为了Java应用程序的提速。
6) 复杂算法尽可能用C完成
复杂算法尽可能用C或者C++完成,而后用JNI调用。可是若是是算法比较单间,没必要这么麻烦,毕竟JNI调用也会花必定的时间。请权衡。
7) 减小没必要要的全局变量
尽可能避免static成员变量引用资源耗费过多的实例,好比Context。Android提供了很健全的消息传递机制(Intent)和任务模型(Handler),能够经过传递或事件的方式,防止一些没必要要的全局变量。
8) 不要过多期望gc
Java的gc使用的是一个有向图,判断一个对象是否有效看的是其余的对象能到达这个对象的顶点,有向图的相对于链表、二叉树来讲开销是可想而知。因此不要过多期望gc。将不用的对象能够把它指向NULL,并注意代码质量。同时,显示让系统gc回收,例如图片处理时,
if(bitmap.isRecycled()==false) { //若是没有回收 bitmap.recycle(); }
9) 了解Java四种引用方式
JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
i. 强引用(StrongReference)
强引用是使用最广泛的引用。若是一个对象具备强引用,那垃圾回收器毫不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题。
ii. 软引用(SoftReference)
若是一个对象只具备软引用,则内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存。
iii. 弱引用(WeakReference)
在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。
iv. 虚引用(PhantomReference)
顾名思义,就是形同虚设。与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。了解并熟练掌握这4中引用方式,选择合适的对象应用方式,对内存的回收是颇有帮助的。
详细请参考 http://blog.csdn.net/feng88724/article/details/6590064
10) 使用实体类比接口好
假设你有一个HashMap对象,你能够将它声明为HashMap或者Map:
Map map1 = new HashMap(); HashMap map2 = new HashMap();
哪一个更好呢?
按照传统的观点Map会更好些,由于这样你能够改变他的具体实现类,只要这个类继承自Map接口。传统的观点对于传统的程序是正确的,可是它并不适合嵌入式系统。调用一个接口的引用会比调用实体类的引用多花费一倍的时间。若是HashMap彻底适合你的程序,那么使用Map就没有什么价值。若是有些地方你不能肯定,先避免使用Map,剩下的交给IDE提供的重构功能好了。(固然公共API是一个例外:一个好的API经常会牺牲一些性能)
11) 避免使用枚举
枚举变量很是方便,但不幸的是它会牺牲执行的速度和并大幅增长文件体积。例如:
public class Foo { public enum Shrubbery { GROUND, CRAWLING, HANGING } }
会产生一个900字节的.class文件(FooShubbery.class)。在它被首次调用时,这个类会调用初始化方法来准备每个枚举变量。每个枚举项都会被声明成一个静态变量,并被赋值。然后将这些静态变量放在一个名为”VALUES”的静态数组变量中。而这么一大堆代码,仅仅是为了使用三个 整数。
这样:Shrubbery shrub =Shrubbery.GROUND;会引发一个对静态变量的引用,若是这个静态变量是final int,那么编译器会直接内联这个常数。
一方面说,使用枚举变量可让你的API更出色,并能提供编译时的检查。因此在一般的时候你毫无疑问应该为公共API选择枚举变量。可是当性能方面有所限制的时候,你就应该避免这种作法了。
有些状况下,使用ordinal()方法获取枚举变量的整数值会更好一些,举例来讲:
for(int n =0; n < list.size(); n++) { if(list.items[n].e == MyEnum.VAL_X) { // do something } else if(list.items[n].e == MyEnum.VAL_Y) { // do something } }
替换为:
int valX = MyEnum.VAL_X.ordinal(); int valY = MyEnum.VAL_Y.ordinal(); int count = list.size(); MyItem items = list.items(); for(int n =0; n < count; n++) { intvalItem = items[n].e.ordinal(); if(valItem == valX) { // do something } else if(valItem == valY) { // do something } }
会使性能获得一些改善,但这并非最终的解决之道。
12) 在私有内部内中,考虑用包访问权限替代私有访问权限
public class Foo { public class Inner { public 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:"+value); } }
须要注意的关键是:咱们定义的一个私有内部类(FooInner),直接访问外部类中的一个私有方法和私有变量。这是合法的,代码也会打印出预期的“Valueis27”。但问题是,虚拟机认为从FooInner中直接访问Foo的私有成员是非法的,由于他们是两个不一样的类,尽管Java语言容许内部类访问外部类的私有成员,可是经过编译器生成几个综合方法来桥接这些间隙的。
/*package*/static int Foo.access$100(Foo foo) { return foo.mValue; } /*package*/static void Foo.access%200(Foo foo,int value) { foo.duStuff(value); }
内部类会在外部类中任何须要访问mValue字段或调用doStuff方法的地方调用这些静态方法。这意味着这些代码将直接存取成员变量表现为经过存取器方法访问。以前提到过存取器访问如何比直接访问慢,这例子说明,某些语言约会定致使不可见的性能问题。若是你在高性能的Hotspot中使用这些代码,能够经过声明被内部类访问的字段和成员为包访问权限,而非私有。但这也意味着这些字段会被其余处于同一个包中的类访问,所以在公共API中不宜采用。
13) 将与内部类一同使用的变量声明在包范围内
请看下面的类定义:
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); } }
这其中的关键是,咱们定义了一个内部类(FooInner),它需要访问外部类的私有域变量和函数。这是合法的,并且会打印出我们希望的结果Valueis27。问题是在技术上来讲(在幕后)FooInner是一个彻底独立的类,它要直接访问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); }
内部类在每次访问mValueg和gdoStuffg方法时,都会调用这些静态方法。就是说,上面的代码说明了一个问题,你是在经过接口方法访问这些成员变量和函数而不是直接调用它们。在前面咱们已经说过,使用接口方法(getter、setter)比直接访问速度要慢。因此这个例子就是在特定语法下面产生的一个“隐性的”性能障碍。经过将内部类访问的变量和函数声明由私有范围改成包范围,咱们能够避免这个问题。这样作可让代码运行更快,而且避免产生额外的静态方法。(遗憾的是,这些域和方法能够被同一个包内的其余类直接访问,这与经典的OO原则相违背。所以当你设计公共API的时候应该谨慎使用这条优化原则)。
14) 缓存
适量使用缓存,不要过量使用,由于内存有限,能保存路径地址的就不要存放图片数据,不常用的尽可能不要缓存,不用时就清空。
15) 关闭资源对象
对SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O操做等都应该记得显示关闭。
2. 视图优化
1) View优化
i. 减小没必要要的View以及View的嵌套层次。
好比实现一个listview中经常使用的layout,可使用RelativeLayout减小嵌套,要知道每一个View的对象会耗费1~2k内存,嵌套层次过多会引发频繁的gc,形成ANR。
ii. 经过HierarchyViewer查看布局结构
利用HierarchyViewer来查看View的结构:~/tools/hierarchyviewer,能很清楚地看到RelativeLayout下面的扁平结构,这样能加快dom的渲染速度。
详细请参考 http://developer.android.com/guide/developing/tools/hierarchy-viewer.html
iii. 经过Layoutopt优化布局
经过Android sdk中tools目录下的layoutopt 命令查看你的布局是否须要优化。详细请参考http://apps.hi.baidu.com/share/detail/34247942
2) 多线程解决复杂计算
占用CPU较多的数据操做尽量放在一个单独的线程中进行,经过handler等方式把执行的结果交于UI线程显示。特别是针对的网络访问,数据库查询,和复杂的算法。目前Android提供了AsyncTask,Hanlder、Message和Thread的组合。对于多线程的处理,若是并发的线程不少,同时有频繁的建立和释放,能够经过concurrent类的线程池解决线程建立的效率瓶颈。另外值得注意的是,应用开发中自定义View的时候,交互部分,千万不要写成线程不断刷新界面显示,而是根据TouchListener事件主动触发界面的更新。
3) 布局用Java完成比XML快
通常状况下对于Android程序布局每每使用XML文件来编写,这样能够提升开发效率,可是考虑到代码的安全性以及执行效率,能够经过Java代码执行建立,虽然Android编译过的XML是二进制的,可是加载XML解析器的效率对于资源占用仍是比较大的,Java处理效率比XML快得多,可是对于一个复杂界面的编写,可能须要一些套嵌考虑,若是你思惟灵活的话,使用Java代码来布局你的Android应用程序是一个更好的方法。
4) 对大型图片进行缩放
图片读取是OOM(Out of Memory)的常客,当在Android手机上直接读取4M的图片时,死神通常都会降临,因此致使每每本身手机拍摄的照片都不能直接读取。对大型图片进行缩放处理图片时咱们常常会用到BitmapFactory类,android系统中读取位图Bitmap时分给虚拟机中图片的堆栈大小只有8M。用BitmapFactory解码一张图片时,有时也会遇到该错误。这每每是因为图片过大形成的。这时咱们须要分配更少的内存空间来存储。BitmapFactory.Options.inSampleSize设置恰当的inSampleSize可使BitmapFactory分配更少的空间以消除该错误。Android提供了一种动态计算的,以下:
读取图片以前先查看其大小:
BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
使用获得的图片原始宽高计算适合本身的smaplesize
BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 4 ;// 4就表明容量变为之前容量的1/4 Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
对于过期的Bitmap对象必定要及时recycle,而且把此对象赋值为null。
bitmap.recycle(); bitmap = null;
5) 合理使用ViewStub
ViewStub 是一个隐藏的,不占用内存空间的视图对象,它能够在运行时延迟加载布局资源文件。当ViewStub可见,或者调用 inflate()函数时,才会加载这个布局资源文件。 该ViewStub在加载视图时在父容器中替换它自己。所以,ViewStub会一直存在于视图中,直到调用setVisibility(int) 或者inflate()为止。ViewStub的布局参数会随着加载的视图数一同被添加到ViewStub父容器。一样,你也能够经过使用 inflatedId属性来定义或重命名要加载的视图对象的Id值。因此咱们可使用ViewStub延迟加载某些比较复杂的layout,动态加载 View,采用ViewStub 避免一些不常常的视图长期握住引用。
详细请参考http://developer.android.com/reference/android/view/ViewStub.html
6) 针对ListView的性能优化
i. 复用convertView。
ii. 在getItemView中,判断convertView是否为空,若是不为空,可复用。若是couvertview中的view须要添加 listerner,代码必定要在if(convertView==null){}以外。
iii. 异步加载图片,item中若是包含有web image,那么最好异步加载。
iv. 快速滑动时不显示图片
当快速滑动列表时(SCROLL_STATE_FLING),item中的图片或获取须要消耗资源的view,能够不显示出来;而处于其余两种状 态(SCROLL_STATE_IDLE 和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来。
v. item尽量的减小使用的控件和布局的层次;背景色与cacheColorHint设置相同颜色;ListView中item的布局相当重要,必须尽可 能的减小使用的控件,布局。 RelativeLayout是绝对的利器,经过它能够减小布局的层次。同时要尽量的复用控件,这样能够减小 ListView的内存使用,减小滑动时gc次数。ListView的背景色与cacheColorHint设置相同颜色,能够提升滑动时的渲染性能。
vi. getView优化
ListView中getView是性能是关键,这里要尽量的优化。getView方法中要重用view;getView方法中不能作复杂的逻辑计算,特别是数据库和网络访问操做,不然会严重影响滑动时的性能。优化以下所示:
@Override public View getView(int position, View convertView, ViewGroup parent) { Log.d("MyAdapter", "Position:" + position + "---" + String.valueOf(System.currentTimeMillis())); final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflater.inflate(R.layout.list_item_icon_text, null); ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon); ((TextView) v.findViewById(R.id.text)).setText(mData[position]); return v; }
建议改成:
@Override public View getView(int position, View convertView, ViewGroup parent) { Log.d("Adapter", "Position:" + position + " : " + String.valueOf(System.currentTimeMillis())); ViewHolder holder; if (convertView == null) { final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_item_icon_text, null); holder = new ViewHolder(); holder.icon = (ImageView) convertView.findViewById(R.id.icon); holder.text = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.icon.setImageResource(R.drawable.icon); holder.text.setText(mData[position]); return convertView; } static class ViewHolder { ImageView icon; TextView text; } }
以上是Google IO大会上给出的优化建议,通过尝试ListView确实流畅了许多。使用1000条记录,经测试第一种方式耗时:25211ms,第二种方式耗时:16528ms。
7) 其余
i. 分辨率适配
-ldpi,-mdpi, -hdpi配置不一样精度资源,系统会根据设备自适应,包括drawable, layout,style等不一样资源。
ii. 尽可能使用dp(density independent pixel)开发,不用px(pixel)。
iii. 多用wrap_content, fill_parent
iv. 抛弃AbsoluteLayout
v. 使用9patch(经过~/tools/draw9patch.bat启动应用程序),png格式
vi. 采用<merge> 优化布局层数;采用<include >来共享布局。
vii. 将Acitivity中的Window的背景图设置为空。getWindow().setBackgroundDrawable(null);android的默认背景是否是为空。
viii. View中设置缓存属性.setDrawingCache为true。
3. 网络优化
1) 避免频繁网络请求
访问server端时,创建链接自己比传输须要跟多的时间,如非必要,不要将一交互能够作的事情分红屡次交互(这须要与Server端协调好)。有效管理Service 后台服务就至关于一个持续运行的Acitivity,若是开发的程序后台都会一个service不停的去服务器上更新数据,在不更新数据的时候就让它sleep,这种方式是很是耗电的,一般状况下,可使用AlarmManager来定时启动服务。以下所示,第30分钟执行一次。
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, MyService.class); PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); long interval = DateUtils.MINUTE_IN_MILLIS * 30; long firstWake = System.currentTimeMillis() + interval; am.setRepeating(AlarmManager.RTC,firstWake, interval, pendingIntent);
2) 数据压缩
传输数据通过压缩 目前大部门网站都支持GZIP压缩,因此在进行大数据量下载时,尽可能使用GZIP方式下载,能够减小网络流量,通常是压缩前数据大小的30%左右。
HttpGet request = new HttpGet("http://example.com/gzipcontent"); HttpResponse resp = new DefaultHttpClient().execute(request); HttpEntity entity = response.getEntity(); InputStream compressed = entity.getContent(); InputStream rawData = new GZIPInputStream(compressed);
3) 使用线程池
线程池,分为核心线程池和普通线程池,下载图片等耗时任务放置在普通线程池,避免耗时任务阻塞线程池后,致使全部异步任务都必须等待。
4) 选择合适的数据格式传输形式
其中Tree Parse 是DOM解析 Event/Stream是SAX方式解析。
很明显,使用流的方式解析效率要高一些,由于DOM解析是在对整个文档读取完后,再根据节点层次等再组织起来。而流的方式是边读取数据边解析,数据读取完后,解析也就完毕了。在数据格式方面,JSON和Protobuf效率明显比XML好不少,XML和JSON你们都很熟悉。从上面的图中能够得出结论就是尽可能使用SAX等边读取边解析的方式来解析数据,针对移动设备,最好能使用JSON之类的轻量级数据格式为佳。
1) 其余
设置链接超时时间和响应超时时间。Http请求按照业务需求,分为是否能够缓存和不可缓存,那么在无网络的环境中,仍然经过缓存的HttpResponse浏览部分数据,实现离线阅读。
2. 数据库相关
1) 相对于封装过的ContentProvider而言,使用原始SQL语句执行效率高,好比使用方法rawQuery、execSQL的执行效率比较高。
2) 对于须要一次性修改多个数据时,能够考虑使用SQLite的事务方式批量处理。
3) 批量插入多行数据使用InsertHelper或者bulkInsert方法
4) 有些能用文件操做的,尽可能采用文件操做,文件操做的速度比数据库的操做要快10倍左右。
3. 性能测试
对于Android平台上软件的性能测试能够经过如下几种方法来分析效率瓶颈,目前Google在Android软件开发过程当中已经引入了多种测试工具包,好比Unit测试工程,调试类,还有模拟器的Dev Tools均可以直接反应执行性能。
1) 在模拟器上的Dev Tools能够激活屏幕显示当前的FPS,CPU使用率,能够帮助咱们测试一些3D图形界面的性能。
2) 通常涉及到网络应用的程序,在效率上和网速有不少关系,这里须要屡次的调试才能实际了解。
3) 对于逻辑算法的效率执行,咱们使用Android上最广泛的,计算执行时间来查看:
long start = System.currentTimeMillis(); // do something long duration = System.currentTimeMillis() - start;
最终duration保存着实际处理该方法须要的毫秒数。
4) gc效率跟踪,若是你执行的应用比较简单,能够在DDMS中查看下Logcat的VM释放内存状况,大概模拟下那些地方能够缓存数据或改进算法的。
5) 线程的使用和同步,Android平台上给咱们提供了丰富的多任务同步方法,但在深层上并无过多的好比自旋锁等高级应用,不 过对于Service和 appWidget而言,他们实际的产品中都应该以多线程的方式处理,以释放CPU时间,对于线程和堆内存的查看这些均可以在DDMS中看到。
6) 利用traceview和monkey等工具测试应用。
7) 利用layoutopt和ninepatch等工具优化视图。