`
yangguangfu
  • 浏览: 1536916 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android最佳实践 为性能而设计

阅读更多

1)避免创建对象
对象的创建从来不是免费的。虽然GC使得内存申请代价不再高昂,但是申请总是比不申请来得昂贵。如果你在一个用户接口循环中申请对象,你将会强行执行周期性的GC,在用户体验上出现一些小的“打嗝”,因此除非不得已,你应该避免创建对象实例,下面是一些例子可以帮助理解:
当你在一组输入数据中抽取字符串时,尝试返回源数据的子串,而非创建一个副本。你将会创建一个新的String对象,但是它会和数据共享字符数组char[]。
如果你有一个返回String的方法,而且你知道它的结果将会一直被追加到StringBuffer,改变你的签名和实现,使这个函数里面直接追加,避免创建临时对象。
一个更激进的主意是将多维数组切成与之平行的一维数组:
一个int数组比Integer数组要好,但也有一个公认的事实就是两个平行的int数组要比一个(int,int)对象数组要高效很多。对于其它原始数据类型亦如是。
如 果你需要实现一个存储一组对象(Foo,Bar)的容器,请记住两个平等的Foo[]和Bar[]数组通常元比一个定制对象数组要好(当然,对于此有个例 外,就是当你设计一个API供其它代码访问时;在那样的情况下,通常最好是为保证API的正确性而牺牲一点速度。但是在你的内部代码,你应该尽可能保持高 效)。
通常来说,避免创建临时对象,如果你可以的话。更少的对象创建意味着更小频率的GC,这对用户体验有直接的影响。
2)用Native方法
当处理字符串时,要毫不犹豫地使用诸如String.indexOf()、String.lastIndexOf()之类的专门方法,这些是典型的用C/C++代码实现的方法,它们可以轻易地比实现同样功能的Java循环快10-100倍。
对此建议的一反面是调用一个native方法要比调用一个解析的方法,不要将native方法用于琐碎的计算,如果可以避免的话。
优先使用Virtual而非Interface
假如你有一个HashMap对象,你可以声明它为一个HashMap或一个通用的Map:
Map myMap1 = new HashMap();
HashMap myMap2 = new HashMap();
哪一个更好?
一般的会说你该选择Map,因为它允许你改变其实现,对于通常的编程来说这是对的,但是对于嵌入式系统来说这并不是太妙。通过接口的引用来调用一个方法要比通过一个具体类型的引用调用virtual方法花多2倍的时间。
如 果你已经选择了一个HashMap,因为它正好适用你正在做的事情,那通过Map来调用就没有什么价值了。考虑到IDE可以为你重构代码,用Map来调用 就没有太大价值了,即使你不知道你代码将去向何方(但是,再一次的,公共的API是又是一个例外:好的API较少考虑性能)。
3)优先选择static而非virtual
如果你不必访问一个对象的字段,使你的方法成为static方法。它可以被更快地调用,因为它不需要一个虚拟方法表间接调用。同时它也是一个好的做法,因为从方法的签名可以看出调用这个方法不会改变对象的状态。
4)避免内部的Getter/Setter
在一些像C++的语言中,通常的做法是用getter(如:i=getCount())代替直接地访问字段(i=mCount),在C++这是一个很好的习惯,因为编译器通常能够内联这个访问,并且你需要限制或debug字段访问,你可以在任何时候增加代码。
在Android,这是一个坏主意。虚拟方法调用代价是昂贵的,实例字段查找代价更高。沿用面一般向对象编程实践在公开接口中提供gettter和setter是合理的,但在一个类中你应该直接访问字段。

Cache字段查找
访问对象字段要比访问本地变量慢得多,如下面这段:
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++)
dumpItems(items[i]);
(我们用一个显式的"this"来表明这是一个成员变量。)
有一个相似的指引就是,不要在for语句中的第二个从句中调用方法。例如下面这段代码将会在每次迭代中都会执行一次getCount(),这是一个巨大的浪费,你可以将它的值cache为一个int。
for (int i = 0; i < this.getCount(); i++)
dumpItems(this.getItem(i));
通常,如果你将要访问一个实例字段多次,一个好的习惯就是创建一个临时变量。例如:
protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {
if (isHorizontalScrollBarEnabled()) {
int size = mScrollBar.getSize(false);
if (size <= 0) {
size = mScrollBarSize;
}
mScrollBar.setBounds(0, height - size, width, height);
mScrollBar.setParams( computeHorizontalScrollRange(),
computeHorizontalScrollOffset(),
computeHorizontalScrollExtent(), false);
mScrollBar.draw(canvas);
}
}
这是对成员字段mScrollBar的四次分开查找,通过将mScrollBar缓存到本地变量,四次成员字段查找变成四次本地变量引用,这样更为高效。
同样地,方法参数作为本地变量拥有相同的性能特征。
声明常量为final
考虑在类开头的如下声明:
static int intVal = 42;
static String strVal = "Hello, world!";
编译器产生一个叫<clinit>的类初始化器方法,它在类首次使用时被执行。这个方法将42存到intVal,并为intVal从类文件字符串常量表中抽出一个引用,当这些值在后面被引用到时,它们以字段查找的方式被访问。
我们可以用final关键字改进之:
static final int intVal = 42;
static final String strVal = "Hello, world!";
这个类不再需要一个<clinit>方法,因为常量存到直接由VM处理的类文件静态字段初始化器,代码访问intVal将会直接使用integer值42,而对intVal的访问会用一个相对廉价的“字符串常量”指令来代替一个字段查找。
声明一个方法或类为final并不能直接获得性能上的好处,但它确实能起到某些优化作用。例如,假如编译器知道一个"getter"不能被一个子类重写,它能够内联这个方法调用。
你也可以将本地变量声明为final,然而这并无真正意义上的性能提升。对于要地变量,只有在使代码更清晰(或你不得不,如为了在匿名内部类中使用)时使用final。
小心使用增强的For循环语句
增 强的For语句可以用于实现了Iterable接口的Collection,对于这些对象,一个iterator被申请用来进行接口调用 hasNext()和next()。对于ArrayList,你最好直接遍历它,但对于其它collections,增强的For循环语句将会等同于显式 的迭代用法。
尽管如此,下面的代码展示了增强的For语句的可为人接受的用法:
public class Foo {
int mSplat;
static Foo mArray[] = new Foo[27];
public static void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; i++) {
sum += mArray[i].mSplat;
}
}
public static void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; i++) {
sum += localArray[i].mSplat;
}
}
public static void two() {
int sum = 0;
for (Foo a: mArray) {
sum += a.mSplat;
}
}
}
zero()在循环中每次迭代获取静态字段两次计算数组长度一次。
one()将所有东西存到本地变量,避免查找。
two()用到了增强的For循环语句,由编译器产生的代码拷贝数组引用和数组长度到本地变量,使之成为一个遍历数组元素的一个很好的选择。它确实在主循环中产生了一个额外的本地载入/存储,使得它比起one()有点慢并且长了4bytes。
总之,增强的For语句对于数组表现良好,但对iterable对象要小心使用,因为有额外的对象创建。
避免Enum类型
Enum非常方便,但不幸的是当考虑到时间和速度时就让人痛苦。例如这个:
public class Foo {
public enum Shrubbery { GROUND, CRAWLING, HANGING }
}
将 编译成一个900byte的.class文件,在首次使用是时,类初始化器在代表每个被枚举的值对象上激活<init>方法。每个对象都有其 静态字段,并且整个集合就存储在一个数组(一个称为“$values”的静态字段)上,对于仅仅的三个integer来说,那是太多的代码和数据了。
这个:
Shrubbery shrub = Shrubbery.GROUND;
导致了一次静态字段查找。如果“GROUND”是一个static final int编译器将会将它看作是一个常量并内联它。
相反地,当然,是运用enum你可以得到一个更优雅的API和一些编译时的值检查。因此,通常折衷的办法是:为API,你应该千方百计地使用enum,但是当考虑到性能时尝试避免使用它。
利用内部类使用包作用方域
考虑下面的类定义:
public class Foo {
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);
}
private class Inner {
void stuff() {
Foo.this.doStuff(Foo.this.mValue);
}
}
}
在这里我们要特别指出的是这里定义了一个内部类(Foo$Inner),它可以直接访问外部类的私有方法和私有实例字段,这是合法的,代码的执行的结果是如预期般的“Value is 27”。
问题在于,Foo$Inner是一个完全独立的类,这使得直接访问其私有方法是非法的,为了架起桥梁,编译器会产生如下两个虚拟方法

/*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方法时就会调用这些方法。这就意味着上面的代码清楚表明了你是通过访问器来访问成员字段 的,而非直接访问。前面我们讨论过访问器是比直接访问是要慢的,所以这是一个由于某种特定语言方言所导致的隐性性能打击。
我们可以通过声明由内部 类访问的字段和方法为具有包作用域而非私有作用域来解决这个问题。这样运行得更快并且移除了额外产生的方法(不幸的是,这也意味着这些字段可以被同包下的 其它类所访问,这个是违反了使所有的字段成为私有的标准OO做法的,再一次的,如果你是在设计一个公共的API的话,你可能要慎重地考虑这一优化策略)。
9)避免使用Float类型
在Pentium CPU发布之前,对于游戏作者来说做很多整型计算是很正常的事。有了Pentium之后,浮点计算联合处理器成了内置功能,你的游戏通过交错整型和浮点操作比只有整型计算时运行起来要快得多。在桌面系统上通常的可以自由的使用浮点数。
不幸的是,嵌入式处理器很少具有硬件浮点支持,所以所有的"float"和"double"操作都是在软件上进行。某些基本的浮点操作可能会花费数微秒。
还有,甚至对于整型数,一些芯片支持硬件乘法但缺少硬件除法,在这种情况下,整型除法和取模运算是在软件上执行的——如果你是在设计一个哈希表或做很多数学运算这就是你需要考虑的事情。

0
0
分享到:
评论

相关推荐

    android应用性能优化最佳实践读书笔记

    "Android应用性能优化最佳实践"这本书深入探讨了如何提升Android应用的性能,以下是一些核心知识点的总结: 1. **内存管理**:内存泄漏是Android应用性能问题的常见来源。开发者需要学会使用内存分析工具,如MAT ...

    android应用性能优化最佳实践

    本文将深入探讨一些Android应用性能优化的最佳实践,主要基于Java编程语言。 1. **内存管理** - **避免内存泄漏**:内存泄漏是导致应用性能下降的主要原因之一。开发者应确保及时释放不再使用的资源,防止内存持续...

    Android性能优化最佳实践(png图)

    布局优化,绘制优化,内存优化,启动优化,其他,稳定,省电优化,体积优化等

    Android应用性能优化

    本篇文章将围绕“Android应用性能优化”这一主题,详细探讨Android应用架构的设计原则、常见性能瓶颈及其查找方法,并提供一系列有效的解决方案。 #### 一、Android应用架构设计原则 1. **模块化与解耦**:通过...

    android-php开发最佳实践源码

    - **Android官方文档**:学习Android SDK的最新特性和最佳实践。 - **PHP官方文档**:掌握PHP语法和相关扩展的用法。 - **框架文档**:深入理解Hush和Zend Framework的架构和使用方法。 通过这个微博项目的源码...

    Android开发的UI设计最佳实践

    本主题将深入探讨Android开发的UI设计最佳实践,旨在帮助开发者创建出美观、易用且高效的用户界面。 一、理解Material Design Material Design是Google推出的一种设计语言,它是Android应用设计的基础。其核心原则...

    Android源码设计模式解析与实战.pdf+精彩绝伦的Android UI设计.pdf

    首先,设计模式是软件工程中的重要概念,它们是解决常见问题的最佳实践。在《Android源码设计模式解析与实战》中,读者可以深入理解如何在Android环境中应用各种设计模式,如单例模式、工厂模式、观察者模式、装饰器...

    animation,多个示例显示了android上动画的最佳实践。.zip

    `animation,多个示例显示了android上动画的最佳实践。.zip`这个开源项目提供了一系列的Android Studio项目,旨在帮助开发者深入理解和掌握Android动画的最佳实践。通过这些示例,我们可以学习到如何有效地在Android...

    Android ORM系列之GreenDao最佳实践

    GreenDao就是其中一款针对Android平台设计的高性能ORM框架,它通过简化数据库操作,提高了开发效率。本文将深入探讨在Android项目中如何有效地使用GreenDao进行数据库管理,实现最佳实践。 1. **GreenDao安装与配置...

    Android开发技巧与性能优化

    4.ANDROID 最佳实践 4.1 为性能设计: 4.2 为响应灵敏性设计 4.3 为无缝设计: 5. 多资源文件的引用 6. ANDROID 调试 LOGCAT 技巧 7. 用 ANDROID 运行最简单的C 程序 8. 开发技巧杂集 8.1 一些源于 CSS 的组合实现...

    深入理解android Telephony原理剖析与最佳实践

    书中的"最佳实践"部分为开发者提供了实际开发中遇到问题的解决方案和优化建议,比如如何有效地使用PhoneStateListener避免过度唤醒,如何处理网络切换时的数据连接问题,以及如何在不影响用户使用体验的前提下,优化...

    Android 网路编程最佳实践

    综上所述,Android网络编程的最佳实践涉及选择合适的网络库、理解HTTP协议、正确处理网络请求与响应,以及关注性能和安全性。在实际开发中,应根据项目需求和性能指标,灵活运用这些知识,以提供更优质的用户体验。

    Android和PHP开发最佳实践

    在Android和PHP开发最佳实践中,我们探讨的是如何高效地结合这两种技术来构建强大的移动应用程序。Android作为Google主导的开源移动操作系统,广泛应用于智能手机和平板电脑,而PHP则是一种流行的服务器端脚本语言,...

    深入Android应用开发 核心技术解析与最佳实践

    深入Android应用开发:核心技术解析与最佳实践以Android的源代码为主,SDK为辅,针对应用开发者的需求,对各种核心技术的使用方法、底层原理和实现细节进行了深入而详细的讲解,同时辅之以大量案例和最佳实践,为...

    performance-samples,多个示例显示了Android性能方面的最佳实践。.zip

    "performance-samples"项目,正如其标题所示,提供了多个示例,展示了如何在Android应用中实施性能最佳实践。这个开源项目通过一系列精心设计的示例,帮助开发者深入理解并应用Jetpack基准库,从而提升应用程序的...

    CoolWeather,使用Android最佳实践的天气应用程序。Android Jetpack,干净的架构。科特林语.zip

    本篇文章将深入探讨一款名为"CoolWeather"的开源项目,它是一个使用Android最佳实践和Jetpack组件构建的天气应用程序,全部采用Kotlin语言编写,展现了现代Android应用的开发趋势和技术栈。 首先,我们要理解“清洁...

    深入Android应用开发 核心技术解析与最佳实践.z01

    深入Android应用开发:核心技术解析与最佳实践以Android的源代码为主,SDK为辅,针对应用开发者的需求,对各种核心技术的使用方法、底层原理和实现细节进行了深入而详细的讲解,同时辅之以大量案例和最佳实践,为...

    Android移动性能实战试读文章

    虽然试读版只有100多页,但它为Android开发者提供了一个宝贵的起点,引导他们深入学习和实践性能优化技术。如果觉得内容有帮助,完整版的书籍将提供更深入的洞察和更多实际案例,值得购买以进一步提升开发技能。

Global site tag (gtag.js) - Google Analytics