性能优化
Android 需要注意的性能优化点
- 小心使用Service,因为当serivce启动后,系统倾向于将该service所依赖的进程保留,这样会导致比较消耗内存。Android官方推荐尽量使用IntentService,它在任务结束后会自动停止,避免了额外的内存消耗或Serivce内存泄漏。
- 当界面不可见时释放内存。通过重写onTrimMemory方法就可以。
- 谨慎使用Bitmap,当对图像质量要求不是特别高的情况,尽量做一下压缩处理,或者改用RGB_565图像,它没有透明度,但是占用的空间基本会少很多。
- 对内存的开销要有一个了解
- 枚举的开销是静态常量的两倍以上,尽量使用静态常量。
- 任何一个Java类都要占用大概500个字节的内存空间。
- 任何一个实例要消耗12到16字节的空间。
- 在使用HashMap时,即使只设置了一个基本数据类型也会消耗32字节,而不是4字节。
- 过多的使用抽象也会带来内存开销。
- 避免使用依赖注入框架,比如Anotation,在搜寻代码中的注解的时候,可能会消耗很长的时间,而且会加载一些用不到的对象到内存,且很久才会释放。
- Proguard简化代码。它不仅有混淆的作用,而且会对类、方法、字段等重命名,重命名之后会比之前简短很多,这样内存占用也会更少。
- 使用多个进程。其实这个大多数情况是不太好的,搞不好还会有额外的内存开销。比如音乐播放,可以将播放的service放到一个单独的进程中,这样当前进程结束后可以释放掉一些UI占用的资源,如果放到一个进程中就不会释放这些资源。
谈谈你做过的性能优化
我所做的优化主要是以下几个方面:
- 启动优化
- 绘制优化
- 内存优化
- 稳定性优化
- Hybrid 优化
启动优化:
首先你得知道这块是不是需要优化,这个主要通过2个途径:
- 直观感觉启动时间是不是过于长,是否有卡顿或白屏的感觉
- 监控应用的启动时间:我的方法是监控从 Application onCreate 方法第1行开始监控,到第1个 Activity onResume 方法执行完成时所用的时间。
怎样去优化:
在应用开发中往往会加载很多的第三方库,而这些库往往是需要在应用启动时初始化的,有的是在 Application 启动的时候初始化,有的是在首个 Activity 启动的时候初始化。我们需要做的就是尽可能减少该部分的耗时,能 delay 启动的可以 delay 一下,如果不可以的话做一些异步化,减少主线程的时间占用。
绘制优化:
绘制优化总体来说就是卡顿优化,也就是减少应用的卡顿感,使我们感受到卡顿感的场景有以下几种,UI 的刷新绘制、页面的跳转、事件的响应。
而引起卡顿感的因素又分为以下几个方面:
- 界面绘制层级深、页面复杂、刷新不合理。
- 数据处理:导致这种卡顿有以下几个场景:
- 数据处理在 UI 线程
- 数据处理占用 CPU 高,导致主线程拿不到时间片
- 内存增加导致 GC 频繁
检测方法:
- Profile GPU Rendering:检测渲染性能。
- TraceView:分析函数的调用过程,
- Systrace:UI 性能分析
- Hierarchy Viewer:布局优化
- Show GPU Overdraw:检测过度绘制
优化方法:
布局上:
- 减少代码层级
- 减少同一层级控件数量。
- 一个控件的属性越少,解析越愉快,删除控件中的无用属性。
- 如果层级相同 LinearLayout 要优于 RelativeLayout,因为 RL 会对子 View 做2次测量。
- 如果 LinearLayout 有 weight 属性,也需要2次测量,但由于没有依赖关系,仍然比 RL 的效率高。
- ViewStub 提高显示速度,如果某个布局中多个元素可能只会显示一部分,而另一部分不会显示,应该选用 ViwStub,它可以实现延迟加载布局布局。
- 尽量少用 wrap_content,它会增加布局的计算成本,如果已知宽度为固定值时,尽量写固定值。
- 使用 <merge/> 标签减少布局嵌套层级。
内存优化:
内存导致卡顿原因:
为了整个系统的内存控制需要,Android 系统为每一个应用程序都设置一个硬性的 Dalvik Heap Size 最大限制阈值,这个阈值在不同的设备上会因为 RAM 的大小不同而有所差异,如果应用占用内存空间已经接近这个阈值时,再尝试分配内存的话,就很容易引起 OOM 错误。 如果到达临界值时,系统会进行 GC 操作,如果占用内存经常在阈值徘徊的话,会导致频繁 GC ,就会引起卡顿。
内存泄漏:
指应用中不会再使用的对象,但垃圾回收时没有辨认出来,不能及时回收。
场景:
- 资源性对象未关闭
- 注册对象未注销
- 静态变量持有大数据对象
- 非静态内部类的静态实例
- Handler 导致内存泄漏
- 容器中的对象没有清理造成内存泄漏
- WebView
检测工具:LeakCanary、Android Profiler
优化方法:
- AutoBoxing
- 内存复用(系统资源、ListView item 复用、对象池、bitmap)
- 使用最优的数据类型
- 少用枚举类型
- 图片内存优化
稳定性优化:
稳定性优化,主要是减少 crash 和 ANR
监控:
可以自己监控也可以交给第3方平台来监控,现在很多平台都可以做到该功能(有盟、百度统计、腾讯Bugly)。
这个有很多的第三方工具都可以帮助监控,我们公司用的是腾讯的 Bulgy,它可以同时监控到 Java 层和 Native 层的 Crash。
ANR 发生的原因:
- 事件:5s
- 广播:10s
- 服务:20s
Hybrid 优化:
首先了解网页渲染需要做的工作:
- 下载 html、css、js
- 渲染 DOM、CSS
- 执行 JS
- 下载图片
缺点:流程比较繁琐,而且速度较慢。
优化:
整体逻辑:资源离线包化。
- 首先走浏览器缓存。
- 服务端将资源打包成 zip 包,并加密处理,上传到 CDN。
- 客户端也会将一部分不会改变的资源直接打到 apk 中,请求的时候会首先找 apk 内的包,没有的话再找离线包。
- 客户端在启动页面,通过请求服务端接口下发离线包的版本号来控制是否升级新的离线包。
- 将启动页面和 WebView 页面放到同一个页面,这样起到一个预加载的作用。
- 图片懒加载
- 并添加调试功能,可动态关闭离线功能。
优化后在主流机型测试性能提升 80% 左右。
遇到的问题:
问题1:首页会打包到apk中,并用 file 协议请求,但跨域请求失败。
原因:
如果服务端不允许跨域,则浏览器会驳回请求,无法读取服务端数据,导致 Ajax 跨域失败。
Webview 设置 setAllowUniversalAccessFromFileURLs(true) 以允许 file 协议的页面内部可以向其他源发起 http/https 请求。
问题2:区分正式与测试环境
客户端在 url 参数后面加测试环境和正式环境的标识。
问题3:4.4版本的未用离线包资源
发现 shouldInterceptRequest 这个方法是区别于不同版本的,需要同时监控2个方法。