优化角度
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
此外,优化过程中使用低端手机更易发现瓶颈