Android中的性能优分为以下几个方面:布局优化、网络优化、安装包优化、内存优化。
一.布局优化
布局优化的本质就是减少View的层级。常见的布局优化方案如下:
- 在LinearLayout和RelativeLayout都可以完成布局的情况下优先选择RelativeLayout,可以减少View的层级,但是注意相同组件可能RelativeLayout绘制时间长
- 将常用的布局组件抽取出来使用 < include > 标签
- 通过 < ViewStub > 标签来加载不常用的布局
- 使用 < Merge > 标签来减少布局的嵌套层次
二.网络优化:
常见的网络优化方案如下
- 尽量减少网络请求,能够合并的就尽量合并
- 避免DNS解析,根据域名查询可能会耗费上百毫秒的时间,也可能存在DNS劫持的风险。可以根据业务需求采用增加动态更新IP的方式,或者在IP方式访问失败时切换到域名访问方式。
- 大量数据的加载采用分页的方式
- 网络数据传输采用GZIP压缩
- 加入网络数据的缓存,避免频繁请求网络
- 上传图片时,在必要的时候压缩图片
三.安装包优化
安装包优化的核心就是减少apk的体积,常见的方案如下:
- 使用混淆,可以在一定程度上减少apk体积,但实际效果微乎其微
- 减少应用中不必要的资源文件,比如图片,在不影响APP效果的情况下尽量压缩图片,有一定的效果
- 在使用了SO库的时候优先保留v7版本的SO库,删掉其他版本的SO库。原因是在2018年,v7版本的SO库可以满足市面上绝大多数的要求,可能八九年前的手机满足不了,但我们也没必要去适配老掉牙的手机。实际开发中减少apk体积的效果是十分显著的,如果你使用了很多SO库,比方说一个版本的SO库一共10M,那么只保留v7版本,删掉armeabi和v8版本的SO库,一共可以减少20M的体积。
四.Android内存优化
Android的内存优化在我看来分为两点:避免内存泄漏、扩大内存,其实就是开源节流。
其实内存泄漏的本质就是较长生命周期的对象引用了较短生命周期的对象。
1.常见的内存泄漏:
-
静态变量导致的内存泄漏
详情:一个静态变量又是非静态内部类会一直持有对外部类的引用,导致外部类Activity无法被回收。
解决办法:将内部类设为静态内部类或独立出来;使用context.getApplicationContext()
。 -
单例模式导致的内存泄漏
详情:单例传入参数this来自Activity,使得持有对Activity的引用。
解决办法:传参context.getApplicationContext()
。 -
属性动画导致的内存泄漏
详情:没有在onDestroy()
中停止无限循环的属性动画,使得View持有了Activity。
解决办法:在Activity.onDestroy()
中调用Animator.cancel()
停止动画。 -
Handler导致的内存泄漏
详情:Message持有对Handler的引用,而非静态内部类的Handler又隐式持有对外部类Activity的引用,使得引用关系会保持至消息得到处理,从而阻止了Activity的回收。
解决办法:使用静态内部类+WeakReference弱引用;当外部类结束生命周期时清空消息队列。 -
线程导致的内存泄漏
详情:AsyncTask/Runnable以匿名内部类的方式存在,会隐式持有对所在Activity的引用。
解决办法:将AsyncTask和Runnable设为静态内部类或独立出来;在线程内部采用弱引用保存Context引用。 - 资源未关闭导致的内存泄漏
详情:未及时注销资源导致内存泄漏,如BraodcastReceiver、File、Cursor、Stream、Bitmap等。
解决办法:在Activity销毁的时候要及时关闭或者注销。例如:
① BraodcastReceiver:调用unregisterReceiver()
注销;
② Cursor,Stream、File:调用close()
关闭;
③Bitmap:调用recycle()
释放内存(2.3版本后无需手动)。 - Adapter导致的内存泄漏
详情:不使用缓存而只依靠getView()
每次重新实例化Item,会给gc制造压力。
解决办法:在构造Adapter时使用缓存的convertView。
- WebView导致的内存泄漏。
详情:WebView比较特殊,即使是调用了它的destroy方法,依然会导致内存泄漏。
解决办法:其实避免WebView导致内存泄漏的最好方法就是让WebView所在的Activity处于另一个进程中,当这个Activity结束时杀死当前WebView所处的进程即可,我记得阿里钉钉的WebView就是另外开启的一个进程,应该也是采用这种方法避免内存泄漏。
2.扩大内存
为什么要扩大我们的内存呢?有时候我们实际开发中不可避免的要使用很多第三方商业的SDK,这些SDK其实有好有坏,大厂的SDK可能内存泄漏会少一些,但一些小厂的SDK质量也就不太靠谱一些。那应对这种我们无法改变的情况,最好的办法就是扩大内存。
扩大内存通常有两种方法:
- 一个是在清单文件中的Application下添加largeHeap="true"这个属性,另一个就是同一个应用开启多个进程来扩大一个应用的总内存空间。
- 第二种方法其实就很常见了,比方说我使用过个推的SDK,个推的Service其实就是处在另外一个单独的进程中。
Android中的内存优化总的来说就是开源和节流,开源就是扩大内存,节流就是避免内存泄漏。
3.检测、分析内存泄漏的工具
- MemoryMonitor:随时间变化,内存占用的变化情况
- MAT:输入HRPOF文件,输出分析结果
a. Histogram:查看不同类型对象及其大小
b.DominateTree:对象占用内存及其引用关系
c.MAT使用教程 - LeakCanary:实时监测内存泄漏的库(LeakCanary原理)
五数据结构优化
- 比如HashMap和ArrayMap,优先使用ArrayMap;
- 优先使用基本类型,而非包装类
- 减少占内存较大的枚举的使用
- 采用三级缓存机制:LRUCache
- 图片压缩:inSampleSize、RGB_565替换RGB_8888