Android 性能优化的一些知识

性能优化的一些知识

本文参考多处分别是

书籍:

《重构-改善既有代码的设计》

Android应用性能优化》

Java程序性能优化》

《大话数据结构》

《深入理解Java虚拟机》

博客:

http://www.cnblogs.com/xwdreamer/archive/2012/05/14/2499339.html

http://www.androidside.com/docs/guide/practices/design/performance.html

一、先重构

1. 重构可能会使软件运行的更慢,但是它也使得软件的性能优化更清晰。

2. 重构能够很好的帮助复查代码,清除冗余的code,改善代码逻辑。

二、优化的注意点

1. 优化不是应用开发的首要任务,提供良好的用户体验,专注代码的可维护性,才是首要任务。

2. 一般情况,代码优化应该最后才做。

3. 往往优化最好的结果是结合多种不同的技术,而不是只依赖于其中之一。

4. 选择合适的策略算法,以及合适的数据结构是性能优化应该首先考虑的事情。

5. 不要做可以不做的事情,不要分配可以不分配的内存。

三、基本的代码优化

1. 不同强度的引用

i. Strong Reference:

1. 代码中普遍存在的引用。

2. 只要存在,垃圾回收器就不会回收。

ii. SoftReference

1. 描述一些还有用,但是不是必须的对象。

2. 在系统发生内存溢出异常之前,将其引用对象回收。

iii. WeakReference

1. 比SoftReference更弱一些的。

2. 引用所指向对象的回收发生在下一次垃圾回收之前。

iv. 虚引用(PhantomReference)

1. 比WeakReference更弱的引用,存在与否对其引用对象是否被回收完全没有影响。

2. 选择恰当的数据结构

i. HashTable

1. 线程安全。

2. 不允许null为 entryvalue或者 key

ii. HashMap(哈希表)

1. 通过key快速定位Value,通过Hash(key)%len获得位置(散列技术便是存储位置与key建立一种关系,可以通过key直接定位到value上)。

2. 实现原理:链表的数组。

3. HashMap可以为null value或者null key

4. 非线程安全。

5. 解决冲突:链地址法。

iii. HashSet,为快速查找而设计的数据结果。判断有无重复,因为Set接口本身就是数学概念中集合的意思。

1. 内部实现是Value为同一ObjectHashMap实现的。

2. 不是同步的。

3. 不能保证元素的排列顺序。

4. hashCode来决定对象在HashSet中的位置。

5. 集合元素可以是null,但是只能放入一个null

6. HashSet会判断equalshashCode来判断是否相等。

iv. LinkedHashSet,

1. 同样是根据元素的hashCode来确定函数的存储位置。

2. 同时使用链表来维护其中元素的位置,当遍历的时候,以元素添加到集合中的顺序访问集合元素。

v. 数组:简单固定大小的数据可以考虑首先使用数组。

vi. ArrayList内部实现使用数组,遍历数据较为高效,不适合用于频繁的增删。

1. 底层是数组实现。

2. 非线程安全。

3. 增量是1.5倍。

vii. Vector

1. 底层是数组实现的。

2. 线程安全的。

3. 扩展是2倍增量。

viii. WeakHashMap

1. HashTable based with Weak keys.

2. 当某个key不再正常使用的时候(除了此map里,再无外部引用的时候)回收此条目。

ix. LinkedList

1.内部由双向链表实现,增删数据较为高效。

2. 非线程安全。

选择数据结构时注意:

i. 是否有必要选择线程安全的。

ii. 是否选择了逻辑意义恰当的数据结构。

iii. 系统Api是否有合适的实现。

iv. 缓存频繁使用的对象。

3. 使用系统提供的更好实现

i. LruCache(Leasted Recently Used)

1. 有限数目的强引用的LinkedHashMap

2. Cache中的item当被使用的时候,移动到队列的头部。

3. 当Cache满的时候,回收队尾的item,丢弃最近最少使用的item

4. public LruCache(int maxSize) 创建的时候可以定义缓存的最大长度。

ii. SparseArray

1. 稀疏矩阵的实现原理。

2. 当键是整数的时候,可以避免创建很多对象。

iii. 对于float的操作使用FloatMath代替Math。

4. 启用多线程支持

i. 将耗时的操作放到其他线程里。

1. 访问网络连接。

2. 数据库操作。

3. 文件读写。

4. 访问系统服务。

5. 访问其他应用信息。

6. 大规模逻辑。

ii. 最小化Synchronized语句块。(例如单例模式的双重锁)

iii.Android内置的类

1.AsyncTask。

2.查数据:CursorLoader:这个便是专门用来从后台线程里取数据,到主线程刷新。

3.AsyncQueryHandler支持异步查,更改,删除。

iii.Background thread 设置Background Priority非UI线程。

5. 改变数据的所属域

i. Activity 所有的数据改成Application所有。(参见LauncherApplication实现)

ii. 循环内部避免创建使用try catch,可以放到循环外面。

iii. 尽可能小的Synchronized语句块。

iv. 在static块里调用Class.forName,Class.getMethod准备操作的方法,只在执行的地方做执行的操作

6. 减少必要的开销

i. onStart,onStop注册和注销一些广播。

ii. onResume里做一些恢复操作代替后台操作。(秒表在后台的时候,可以不用去计算时间和刷新UI,只需要在onResume的时候计算一次即可。

iii. 广播的注册是否有必要写在AndroidManifest(写在AndroidManifest中会自动启动应用,而在code中的注册只有在运行时有效)

7. 必要的清理

i. 资源的关闭。(Cursor,File,网络,BroadcastReceive).Unregister Oberser

ii. 系统特别要求的Bitmap,TypedArray.相应的recycle方法。

iii. 大对象,大数组在不在使用以后,手动赋null

iv. 大规模的UI必要时刻可以remove掉。

8. 响应系统倡议

i. Application/Activity/Service/Fragment都有相应的低内存回调onLowMemory

9. 使用最小数据类型

i. 处理大量数据的时候,使用可以满足要求的最小数据类型。(空间和时间的考量)

10. Static

i. 尽可能少的使用static变量,避免内存泄露(在<clinit>的时候执行)。

ii. 尽可能多的使用static方法,可以避免虚函数的开销。static方法大概可以提高20%的性能。(工具类一般都为static方法)

iii.如果可以,使用static final来定义常量(字面常量在编译的时候完成)。

11.POJO对象类方法舍去

i. 直接使用public字段代替get,set方法,使用方法要检测序函数相关。(  使用直接调用3倍快于使用get方法)在JIT环境中7倍速度快与访问方法。

12. 调用native方法

13. 发布前去掉调试信息

i. Logs

ii. Assert

iii. 调试代码

14.使用foreach。

15.尽量避免使用float相关的,float要相对慢,可以使用double,虽然浪费点空间。

16.Bitmap

i.缩放:BitmapFactory.Options改变inSampleSize参数进行缩放。

参考自:http://sinfrancis.iteye.com/blog/1146664

17.内部类会隐式的关联外部类对象。

18.try catch不当使用。(在一个try catch中有两个可能抛出异常的情况,有可能在上一个抛出之后,下一个得不到执行,对于close这样的调用,就有可能会造成泄漏)

19.Activity使用

i.超越Activity的引用

a.static的变量直接或间接引用到Activity。解决:有无必要使用static修饰,传递其他类型的Conext如ApplicatonContext,使用弱引用。

b.考虑把这些数据放到Application中。

c.额外线程使用当前Context,考虑使用ApplicationContext,或者额外线程使用弱引用。

ii.转屏重启

a.转屏是否有必要重启。

b.是否有必要让其能够转屏。

iii.启动的四种类型

a.standard:每次加载会产生一个Activity

b.Singletop,要启动的类在栈顶,则无需重新启动。也就是说从当前屏启动当前屏不会重写创建。

c.singleTask会在栈中搜寻相应的Activity,如果找到,则回复到原来的Activity,并且把这个Activity之上的所有Activity出栈。AsingletaskActivity,启动序列A->B->C->A,这个时候就会恢复到A的状态,并且将BC出栈,当back的时候,会发现,直接从A退出。

d.SingleInstance独有一个栈,所以总在栈顶。

20.字符串

a.    String s = "a" + "b" + "c";优于StringBuffer s = new StringBuffer("a");s.append("b");s.append("c");

b.    多变的字符串使用StringBuffer/StringBuilder优于String。

c.    不会逃逸线程的地方优先使用StringBuilder.

四、UI优化

1. 不阻塞主线程, 耗时的操作放在其他线程里进行,不阻塞主线程的UI响应。

2. Cache频繁使用并且创建代价比较大的。

3. 选择恰当的布局,移除冗余的layout结构。

4. 使用<merge />来代替根为<FrameLayout>的标签。

5. <ViewStub>延迟加载UI。

6. 使用preload的系统资源。

7. 提供不同大小的资源图档代替一些图片操作算法(HomeWallpaper有缩略图和原图两种)。

8. 为不同的图档搭配适应不同屏幕密度。

9. 对于尺寸小的屏幕考虑减少非必要的UI元素。

10. ListView中优化:

a.item滑动的时刻不去做刷新 ,ideo的时刻进行刷新。

b.重用convertView.(这是一种flyweight的思想)

c.给ConvertView设置ViewHolder的tag。(ViewHolder的使用省略了findViewById的开销)

11. 对于图文加载,先加载文字和默认图,然后再异步加载更新图片。

12. 使用TextView可以实现图片+文字。

13.overdraw:theme的背景图片以及imageView的背景图片双重设置的可能引起双重绘制。比较浪费。

14.measure,layout,draw的地方应该避免对象的分配,因为这些地方时常频繁调用。

15.layout  宽而广的设计优于窄而深。

16.使用include复用UI。

17.可视优先加载策略(如Launcher会首先加载桌面当前页元素再去加载第一屏widget/shortcut).

18.延迟加载(对于Launcher的所有应用View来说,我们先加载我们所处于的页面和左右共三个页面,而不是加载所有的页面,当滑动一个页面的时候,清除远离的页面,并加载接近的页面)

19.采用点9图。

五、数据库优化

1. 插入多条数据时使用mSQLiteDatabase.compileStatement

2. 显式使用一次性事务。

3. 只获取必要的查询数据。

六、使用设计模式

1. 单例

i. 懒汉式

ii. 饥汉式

2. 空对象

i. 减少不必要的object==null的判定。

ii. 代码更优美

3. 原型模式

i. 使用clone代替new

ii. 能够获取当时的状态。

iii. 默认的clone是浅复制,所以应该自己重写。

iv. 复制的过程是二进制拷贝的过程,不会执行构造器方法。

4.享元模式

i.系统中存在大量相似对象。

七、借助于工具

1. TraceView

i. 图形化的性能工具,能够反映执行方法或子方法的时间花费。

ii. 获取各函数花费时间。

2. Layoutopt

i. 分析布局文件,提出修改建议。

3. Hierarchyviewer

i. 查看和分析应用布局。

ii. 测量和绘制时间耗费。

4. Android Lint

i. 发现代码以及资源的潜在问题,实在强大

5.Mat

i.查询对象的个数情况。

ii.列出gc root引用列出引用点。

iii.可以根据不同的包,classloader,不同的数据类型列出shallowsize和retainedsize。

iv.可以通过不同时刻的hprof来进行个数,占用空间的比较。

6.StrictMode

i.基于线程的检查:主要查询主线程中的耗时操作。

a.磁盘读写。

b.网络访问。

c.自定义的耗时操作。

ii.基于DVM的检查

a.Sqlite泄漏

b.未关闭对象。

c.Activity泄漏.

iii.不同方式的问题提醒:dialog,logs。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值