性能优化系列——代码优化

概述

代码优化是每个程序员应有的一项能力,要在日常的开发中不断地提升自我的编码能力,在一点一滴中进步,不断地积累不断地完善自己。对于代码的优化,Android官网的建议是:

  • 不要做冗余的工作。
  • 尽量避免次数过多的内存分配操作。

剔除冗余的代码

当程序中多处出现相同代码时(包括相同的代码及常量),应进行整合,提取出公共的方法,公共的工具类。

避免创建非必要的对象

对象的创建创建需要分配内存,对象的销毁需要垃圾回收,这些操作都是有成本的,会或多或少的影响应用的性能。当一个对象可以复用时要尽量的去复用之前的对象。尤其是一些比较大的对象,像bitmap对象,频繁的创建与销毁,对系统来说是一个不小的符合,过于频繁的创建与回收,会造成内存的抖动,严重时可能会导致应用的崩溃。

在使用String对象时,出现字符串连接操作时应尽量使用StringBuilder/StringBuffer来代替,由于String的拼接是生成新的对象,而旧的对象就会被回收,这就涉及到了对象的创建(内存的分配)及垃圾回收的操作,如果这种情况较多的话是很影响程序性能的。

注意Getters/Setters的写法

我们在开发中使用get/set方法来屏蔽外界对我们所定义对象的感知,从而达到封装的效果,相信我们大多数人都是这样使用的。这样使用会产生性能问题。根据Android官网的介绍,在没有JIT编译器时,直接访问的速度时调用get方法的3倍;在JIT编译时,直接访问是调用get方法的7倍。从这里可以看出直接调用一个类的变量速度是最快的,而且快了好几倍。好在当项目中使用ProGuard的话,它会对get/set方法进行内联操作,从而达到直接访问的效果。

熟练使用java中的四种引用方式

对java四种引用方式的介绍请参考这篇文章:垃圾回收器及内存分配策略

熟练使用这四种引用你就可以处理大多数的你存泄露问题了。如一个静态的dialog引用了一个activity的context,这就造成了内存的泄露,这时就可以使用弱引用来引用这个context,而不是使用强引用。使用弱引用之后当activity被销毁后,在GC是dialog持有的context对象就回被释放,就不会造成内存的泄露了。

Context的使用

相信在开发中我们经常与context打交道,正确的使用context是非常必要的,能够避免一些内存泄露,避免一些程序报错。

种类

context可以分为以下几种:

  • Application:Android程序种全局唯一的一个context实例。
  • Activity/Service:这两个都是ContextWrapper的子类,每个Activity和Service都是一个context。因此每添加一个Activity或者Service就回增加一个context
  • ContentProvider:它不是context的子类,当时它创建时系统会传入一个context实例。如果ContentProvider和调用者处于同一个进程中,getContext()返回应用全局唯一的Context实例。如果是其他进程调用的ContentProvider,那么ContentProvider将持有自身所在进程的Context实例。
  • Broadcast Receiver:和ContentProvider类似,它也不是context的子类,是由系统传入的context,但是这个context是经过功能剪裁的,它不能调用registerReceiver()和宾得Service()方法。

不同context的功能不同,总结如下:

功能ApplicationActivityServiceBroadcastReceiverContentProvider
显示DialogNOYESNONONO
启动ActivityNO【1】YESNO【1】NO【1】NO【1】
实现Layout InflationNO【2】YESNO【2】NO【2】NO【2】
启动ServiceYESYESYESYESYES
绑定ServiceYESYESYESYESNO
发送BroadcastYESYESYESYESYES
注册BroadcastYESYESYESYESNO【3】
加载资源ResourceYESYESYESYESYES

NO【1】:是可以启动Activity,但是不建议这么做,因为如果这样启动,会在新的Task中创建Activity,而不是在原先的Task中创建Activity。
NO【2】:也是不建议这样做,在非Activity中进行Layout Inflation,会使用系统默认的主题,而不是使用应用中设置的主题。
NO【3】:表示在Android4.2及以上的系统上,如果注册的BroadcastReceiver是null时是可以的,用来获取sticky广播的当前值。

选择合适的数据结构

对于不同的数据选择合适的数据结构是非常必要的,我们要对java中的ArrayList,Linked List,HashSet和HashSet等要非常的了解,只有深入了解了,才能灵活的去运用。通常情况下我们使用Android中特有的稀疏数组SparseArray来代替HashMap。特定场合能提高应用的性能。他的核心是二分查找算法。目前SparseArray有以下四类:

  1. SparseIntArray 用来代替HashMap<Integer,Integer>
  2. SparseBooleanArray 用来代替HashMap<Integer,Boolean>
  3. SparseLongArray 用来代替HashMap<Integer,Long>
  4. SparseArray 用来代替HashMap<Integer,String>

由于SparseArray采用的是二分查找算法,因此在插入数据时不可避免的会按照Key值的大小进行插入;SparseArray对删除操作做了优化,它并不会立即删除这个元素,而是通过设置标志位(DELETED)的方式,后面尝试重用。

正确的使用内部类

非静态内部类可能会引起内存的泄露,究其原因是非静态内部类持有外部类的引用,同时可能存在非静态内部类与外部类生命周期不同步的情况,当非静态内部类的生命期比外部类要长时,就回产生内存的泄露(因为非静态内部类持有外部类的引用,导致外部类无法释放),最常见的就是Handler的使用,一不注意就回产生内存泄露。

解决方法:

  1. 当预料会有生命周期不同步的情况时,尽量避免使用非静态内部类。
  2. 将有可能产生内存泄露的非静态内部类声明为静态内部类(静态内部类不持有外部类的引用),可以避免外部类的泄露。

尽可能地使用局部变量

局部变量是保存在栈中的,访问速度较快,而其他变量如:实例变量,静态变量等是创建在堆里的,其访问速度是很慢的。所以在方法中传过来的变量尽量变为局部变量再使用,此外栈中创建的变量,随着方法的结束而回收,不需要额外的垃圾回收。

再循环内或者频繁调用的方法内避免重复创建对象的引用

如:

for (int i = 0; i < count; i++)
{
    Object obj = new Object();    
}

这样会创建count份Object对象的引用,当count值很大时,这也是一笔不小的内存开销。

最后不断的重构代码

重构代码是一项长期的工作,随着阅历的不断成长,你就会发现之前的代码还会存在着不少的提升空间。

最后确保自己永远走在前进的路上,不断学习,不断突破自我。

其他后续补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值