性能优化应该是每个客户端开发人员要重视的问题。在Android硬件设备性能不那么好的时候,性能优化是很迫切的;但是随着硬件设备的不断升级强大,性能化在大部分项目中很少受到重视。下面从几个方面来简单介绍下Android应用的性能优化。
1.代码优化
代码优化说起来好像大家有会去做。Android 官方关于编写高效代码的两个基本准则是:1)不要做冗余的工作。2)尽量避免次数过多的内存分配操作。
1.1 数据结构的选择
选择正确合适的数据结构是很重要,Java常用的数据结构有ArrayList、LinkedList、HashMap和HashSet等。我们需要对它们的特点与区别有深入的了解才能编程的时候做出最合理的选择。
1.2 Handler 和内部类的正确用法
Handler使用不当会引起内存泄漏,这个大家应该都知道。具体的原因是Handler会绑定到Android应用的主线程的Looper上,这个Looper生命周期是伴随着应用的,如果Looper没有处理完Handler发出的消息,那么Handler对象就不会被释放。通常的写法是把Handler作为一个Activity的内部类,如果Handler不是静态的内部类,他就会持有外部类的引用,Handler不被释放时,它所持有的外部类也不会被回收。
一般的解决方法有两种:
1)在子线程中使用Handler,自己创建一个Looper,这时候Looper对象的生命周期就是可以控制的。
2)Handler声明为静态内部类,静态内类不会持有外部类的引用。这个方法时比较常见,因为代价更小一些。
1.3 正确使用Context
Context的种类:
1)Application,Android应用中默认是单例类,在Activity或者Service中通过getApplication取到,通过context.getApplicationContext()取到应用全局唯一的Context。
2)Activity/Service,这两个类都是ContextWrapper的子类,可以通过getBaseContext()取到。不同的Activity/Service的Context是独立的,不会复用。
3)BroadcastReceiver,BroadcastReceiver本身并不是Context的子类,是在执行onReceive()方法时由Android框架传入一个Context实例。这个Context是阉割了部分功能,不能调用registerReceiver()跟bindService()两个方法。具体的大家可以看下源码的机制。
4)ContentProvider,ContentProvider也不是Context的子类,在创建时由系统传入一个Context实例,可以通过getContext()取到,如果调用者跟ContentProvider在一个应用进程中,返回的是应用全局唯一的Context,如果是其他进程调用的话,ContentProvider持有自身所在进程的Context。
Context最常见的错误使用是实现单例模式时使用Context。这个Context应该使用全局唯一的Context。下图反应了Context提供的功能:
1.4 掌握Java的四种引用方式
Java的四种引用方式:
1)强引用,Java里最常见的应用,也是对象的默认引用方式。如果一个对象具有强引用,垃圾回收不会对其进行回收操作,内存不足直接OOM。
2)软引用,当内存充足时不会被回收,内存不足会被回收。软引用可以用来实现内存敏感的高速缓存。可以配合ReferenceQueue使用,被垃圾回收后JVM会把它加入到关联的引用队列中。
3)弱引用,只有弱引用的对象的生命周期更短,不论当前内存是否充足,垃圾回收机制扫描到弱引用的对象都会回收。也可以配合ReferenceQueue使用,被垃圾回收后JVM会把它加入到关联的引用队列中。
4)虚引用,对象会按原来的方式被回收,虚引用本质上是一个标记。虚引用必须跟ReferenceQueue配合使用,被垃圾回收后JVM会把它加入到关联的引用队列中。
1.5 其他的代码优化
避免创建非必要的对象。
对常量使用static final修饰
避免内部的getter/setter
合理的代码重构
2 图片优化
图片的加载与显示几乎是每个应用都避免不了的问题,对于图片重度依赖的应用,图片的处理会影响到整个App的用户体验。
2.1 图片格式
Android应用中能使用的编码格式有三种:JPEG、PNG、WebP,图片格式可以通过查看Bitmap的CompressFormat枚举值来确定。
1)JPEG,广泛使用的有损压缩,三通道,RPG,不支持透明与多帧,通过控制压缩比可以调整图片大小。
2)PNG,是一种无损压缩图片格式,四通道,ARPG,PNG占用空间比较大,应用瘦身可以对PNG进行处理减小其体积。
3)WebP,google在2010年发布的,支持有损、无损压缩,支持完整透明通道,也支持多帧动画,是一种比较理想的格式。既保证图片质量,又要限制图片大小,WebP是首选。微信、微博、淘宝等都使用WebP。
2.2 图片压缩
1)无损压缩,ImageOptim。
2)有损压缩,ImageAlpha、TinyPng。
3)转为WebP,Android4.0以上可以天然支持WebP。
4)尽量使用NinePatch格式的PNG图。
2.3 图片缓存。
这个可以使用Fresco等类似的图片框架,当然你也可以自己实现类似的三级缓存机制。
3 电量优化
手机耗电量是评价应用的重要指标之一。
3.1 BroadcastReceiver
为了减少应用的耗电量,代码实现中应该避免无用操作的执行。比如,应用推到后台后一切的页面刷新都是没有意义的。在onPause后BroadcastReceiver就应该停止,一些提示功能也要停止。
3.2 数据传输
常见的传输方式:蓝牙、wifi、蜂窝网络。我么你应该严格限制后台数据传输,避免无用的传输,控制数据传输的频度。
3.3 位置服务
这个跟BroadcastReceiver有点类似:
1)应该注意有没有及时注销位置监听,onPause时禁用位置监听。
2)合理设置位置监听的频率。
3)GPS、网络定位、被动定位的选择。三者精度依次下降,但是耗电量依次减小。
3.4 AlarmManager
这个是系统级的服务,闹钟耗电量比较大,应该保证相邻的两次调用的时间间隔足够,不需要时要及时取消。
3.5 WekeLock
WekeLock是为了保持设备唤醒提供的API,WekeLock使用完后要及时释放,而且要尽早释放。
4 布局优化
4.1 include标签共享布局。
4.2 ViewStub实现延时加载。
4.3 merge标签减少布局层次。
4.4 尽量使用CompoundDrawable
4.5 使用Lint
Lint除了可以对Java代码进行静态检查,还是可以检查应用的布局是否存在可优化的地方。
5 网络优化
5.1 避免DNS解析
使用ip直连的方式代替DNS解析。这个应该很少出现。
5.2 合并网络请求
5.3 数据预加载
5.4 避免轮询
尽量使用推送来实现
5.5 优化重连机制
可以设计一个最大的重连次数。
5.6 离线缓存
5.7 压缩数据包
5.8 不同的网络使用不同的超时策略
5.9 使用内容分发网络CDN