Android性能优化
优化角度
RAM方面
- 内存泄露优化
- 避免OOM
- APP启动优化
- 线程优化
Res方面
- apk瘦身
- 布局优化
- 绘制优化
- ListView和Bitmap优化
其他
- ANR分析
- 电量优化
- 性能优化工具
内存泄露优化
- 单例模式的持有;
- 静态对象的持有
- 属性动画不被cancel,其持有view,view又持有activity导致内存泄露。
- 非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,或者在执行耗时操作,就会长期的维持着外部类的引用
- 以匿名内部类构建的AsyncTask和新线程会隐式地带有一个对外的引用,如果在activity结束时候没有执行完成,会造成内存泄露,建议使用静态内部类,在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源
- Context导致内存泄漏
根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收 - 静态View导致泄漏
使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收,解决办法是在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity) - BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap、系统服务等资源及时关闭,它们内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为null
- WebView导致的内存泄漏
WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题,通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉 - 集合中的对象未清理
集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的 - Bitmap导致内存泄漏
bitmap是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态变量持有大的bitmap对象 - 监听器未关闭
很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也需要及时移除
减少OOM
Android系统为每一个应用程序都设置了一个硬性的Dalvik Heap Size最大限制阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。ActivityManager.getMemoryClass()可以用来查询当前应用的Heap Size阈值。命令行也可以查看相关的内存信息
减少OOM方面:
其实避免内存泄露也是间接避免了OOM
- 减少内存分配量
- ArrayMap/SparseArray而不是HashMap等传统数据结构,通常的HashMap的实现方式更加消耗内存,因为它需要一个额外的实例对象来记录Mapping操作。另外,SparseArray更加高效在于他们避免了对key与value的autobox自动装箱,并且避免了装箱后的解箱。
- 避免在Android里面使用Enum
- Android内置字符串/颜色/图片/动画/样式以及简单布局的使用,可以在程序中直接饮用系统资源,减少apk体积,但要注意版本差异
APP启动优化
一般情况下我们会利用主题去防止出现白屏
需要尽可能减少Application的onCreate中所要做的事情,比如一些不重要的SDK延迟或者异步加载;
有几个方向:
1、利用开启APP时候提前展示的window,快速展示一个界面,给用户视觉上快速的感觉:onCreate函数开头将activity的theme设置为style中的主题,然后在oncreate函数运行过程中又切换成activity标签下配置的背景。
2、异步初始化三方组件
3、减少IO、网络等密集请求
线程优化
采用线程池,一是减少线程重复创建或销毁带来的性能损耗;
二是控制最大并发线程数,防止大量线程抢CPU造成卡顿
apk瘦身
为什么要瘦身:大体积下载转化率低,同时对服务器增加流量压力,此外会影响手机厂商预置应用
利用ProGuard压缩代码去除无用资源,通过移除不需要的代码,重命名类,域与方法等等对代码进行压缩,优化与混淆。使用ProGuard可以使得你的代码更加紧凑,这样能够减少mapping代码所需要的内存空间。
使用 XZ 或者 7-Zip 压缩Library
第三方开源库的瘦身,仅保留自己需要的部分
so的优化与配置,只保留一类so
动态下发一些资源:字库、so、换肤包等
布局优化
尽量减少布局层级,同层级时候使用功能较低的ViewGroup,比如FrameLayout和LinearLayout,但性能较低的layout拓展性差难以实现较复杂的布局,导致需要嵌套,这时候还是建议使用RelativeLayout毕竟嵌套也会降低性能。
实用策略:
- inlcude标签 便于布局重用
- merge标签 和include配合使用,在被include的布局根元素替换为merge标签,能够减少布局层级
- ViewStub实现需要时加载布局,setVisibility或者inflate之后,被内部引用布局替换掉,提高应用启动性能。
绘制优化
为了让屏幕的刷新帧率达到60fps,我们需要确保在时间16ms(1000/60Hz)内完成单次刷新的操作(包括measure、layout以及draw)
onDraw方法中不要创造大量临时对象,因为该方法会被频繁调用,大量GC伤不起。此外也不要使用耗时操作你懂的啦
减少overDraw的产生,尽量让一个像素只绘制一次,减少布局重叠。
除了布局精简,还可以去掉每行RelativeLayout的背景色;去掉每行TextView的背景色;必要的话去掉activity使用的Theme的背景色。
ListView优化
ListView只是一个代表,很多复杂的View都可以参考如下建议
使用ViewHolder并减少在getView中的复杂操作
根据滑动速度来变化view的操作,比如快速滑动时候不开启线程
采用硬件加速来优化view的绘制
Bitmap优化
- 采用BitmapFactory来对图片进行采样
inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。
decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。 - 使用更小的图片
尽量使用更小的图片不仅仅可以减少内存的使用,还可以避免出现大量的InflationException。假设有一张很大的图片被XML文件直接引用,很有可能在初始化视图的时候就会因为内存不足而发生InflationException,这个问题的根本原因其实是发生了OOM。 - 实现对象的复用
LRU缓存
ANR分析
避免ANR的核心西路是不要在主线程做耗时操作,ANR在activity中的规定出现时限是5秒,在广播中是10秒
出现原因有主线程无法响应触摸或者用户输入,线程之间等待死锁等等。
电量优化
为了减少电量的消耗,在蜂窝移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求
合理的使用一些传感器、谨慎的使用Wake Lock
检测可以利用battery-historian
其他建议
减少对象
减少枚举
常量使用static final修饰
使用Android特有的数据结构,如SparseArray和pair他们具备更好的性能
适当使用弱引用和软引用
采用内存缓存和磁盘缓存
尽量使用静态内部类
性能优化工具
Android Device Monitor(DDMS) was deprecated in Android Studio 3.1 and removed from Android Studio 3.2,此外,SYSTrace TraceView都被AndroidProfile取代了,不过特定场景上述工具还可用
性能分析工具分两个流派:
第一个流派是 instrument。获取一段时间内所有函数的调用过程,可以通过分析这段时间内的函数调用流程,再进一步分析待优化的点。
第二个流派是 sample。有选择性或者采用抽样的方式观察某些函数调用过程,可以通过这些有限的信息推测出流程中的可疑点,然后再继续细化分析。
-
TraceView属于instrument类型,试图收集某个阶段所有函数的运行信息,它希望在你并不知道哪个函数有问题的时候直接定位到关键函数;但可惜的是,收集所有信息 这个是不现实的,它的运行时开销严重干扰了运行环境.
-
SYSTrace属于sample类型,它的思想很朴素,在系统的一些关键链路(比如System Service,虚拟机,Binder驱动)插入一些信息(我这里称之为Label),通过Label的开始和结束来确定某个核心过程的执行时间,然后把这些Label信息收集起来得到系统关键路径的运行时间信息,进而得到整个系统的运行性能信息。Android Framework里面一些重要的模块都插入了Label信息(Java层的通过android.os.Trace类完成,native层通过ATrace宏完成),用户App中可以添加自定义的Label,这样就组成了一个完成的性能分析系统。
在 Android Studio 3.2 的 Profiler 中直接集成了几种性能分析工具,其中:Sample Java Methods 的功能类似于 Traceview 的 sample 类型。Trace Java Methods 的功能类似于 Traceview 的 instrument 类型。Trace System Calls 的功能类似于 systrace。SampleNative (API Level 26+) 的功能类似于 Simpleperf。坦白来说,Profiler 界面在某些方面不如这些工具自带的界面,支持配置的参数也不如命令行,不过 Profiler 的确大大降低了开发者的使用门槛。
-
CPU profiler 存在于Android profile中,用于分析CUP动态使用率,还可以分析函数调用关系,显示线程任务的CPU使用率,它是TraceView和SYSTrace的替代工具
-
Tracer for OpenGL ES,它可以记录和分析app每一帧的绘制过程,以及列出所有用到OpenGL ES的绘制函数和耗时,所以通过Tracer for OpenGL ES我们可以很容易的看出app的每一帧是怎么画出来的。已经被Graphics API Debugger取代
-
LayoutInspector 查看运行时的view结构层级,用来取代Hierarchy Viewer,同样取代了Pixel Perfect
-
Network Traffic tool If you need to view how and when your app transfers data over a network, use the Network Profiler
https://developer.android.google.cn/studio/profile/monitor.html
此外,优化过程中使用低端手机更易发现瓶颈