安卓开发大军浩浩荡荡, 经过近十年的发展, Android 技术优化日异月新, 如今 Android 9.0 已经发布, Android 系统性能也已经非常流畅, 可以在体验上完全媲美 iOS. 但是, 到了各大厂商手里, 改源码, 自定义系统, 使得 Android 原生系统变得鱼龙混杂, 然后到了不同层次的开发工程师手里, 因为技术水平的参差不齐, 即使很多手机在跑分软件性能非常高, 打开应用依然存在卡顿现象. 另外, 随着产品内容迭代, 功能越来越复杂, UI 页面也越来越丰富, 也成为流畅运行的一种阻碍. 综上所述, 对 App 进行性能优化已成为开发者该有的一种综合素质, 也是开发者能够完成高质量应用程序作品的保证.
在 Android 应用优化方面, 我们主要从以下 4 个方面进行优化:
稳定 (内存溢出, 崩溃)
流畅 (卡顿)
耗损 (耗电, 流量, 网络)
安装包 (APK 瘦身)
内存优化
由于 Android 应用的沙箱机制, 每个应用所分配的内存大小是有限度的, 内存太低就会触发 LMK(Low Memory Killer) 机制, 进而会出现闪退现象. 如果要对内存进行优化, 就需要先搞懂 java 的内存是如何分配和回收的, 关于这方面, 可以重点参考下面的内容: Java 垃圾回收器的 GC 机制, 看这一篇就够了 Android 内存泄漏常见案例及分析 Android 应用内存泄漏的定位, 分析与解决策略
分析工具
Memory Monitor 工具
Memory Monitor 是 Android Studio 自带的一个内存监视工具, 它可以很好地帮助我们进行内存实时分析. 通过点击 Android Studio 右下角的 Memory Monitor 标签, 打开工具可以看见较浅蓝色代表 free 的内存, 而深色的部分代表使用的内存从内存变换的走势图变换, 可以判断关于内存的使用状态, 例如当内存持续增高时, 可能发生内存泄漏; 当内存突然减少时, 可能发生 GC 等.
Memory Analyzer 工具
MAT 是一个快速, 功能丰富的 Java Heap 分析工具, 通过分析 Java 进程的内存快照 HPROF 分析, 从众多的对象中分析, 快速计算出在内存中对象占用的大小, 查看哪些对象不能被垃圾收集器回收, 并可以通过视图直观地查看可能造成这种结果的对象.
LeakCanary 工具
LeakCanary 是一个内存监测工具, 该工具是 Square 公司出品的, 所谓 Square 出品必属精品, LeakCanary 的官方地址为 https://github.com/square/leakcanar, 我们可以在 Gradle 里引用它.
Android Lint 工具
Android Lint 是 Android Sutido 种集成的一个 Android 代码提示工具, 它可以给布局, 代码提供非常强大的帮助. 如果在布局文件中写了三层冗余的 LinearLayout 布局, 就会在编辑器右边看到提示. 当然这个是一个简单的举例, Lint 的功能非常强大, 大家应该养成写完代码查看 Lint 的习惯, 这不仅让你及时发现代码种隐藏的一些问题, 更能让你养成良好的代码风格, 要知道, 这些 Lint 提示可都是 Google 大牛们汗水合智慧的结晶.
其他建议
在 Android 应用开发中, 影响稳定性的原因很多, 比如内存使用不合理, 代码异常场景考虑不周全, 代码逻辑不合理等, 都会对应用的稳定性造成影响. 其中最常见的两个场景是: Crash 和 ANR, 这两个错误将会使得程序无法使用. 所以做好 Crash 监控, 把崩溃信息, 异常信息收集记录起来, 以便后续分析; 合理使用主线程处理业务, 不要在主线程中做耗时操作, 防止 ANR 程序无响应发生. 具体可以参考下面的文章链接: Android 系统稳定性问题总结
交互优化
交互是与用户体验最直接的方面, 交互场景大概可以分为四个部分: UI 绘制, 应用启动, 页面跳转, 事件响应. 对于上面四个方面, 大致可以从以下两个方面来进行优化:
界面绘制: 主要原因是绘制的层级深, 页面复杂, 刷新不合理, 由于这些原因导致卡顿的场景更多出现在 UI 和启动后的初始界面以及跳转到页面的绘制上.
数据处理: 导致这种卡顿场景的原因是数据处理量太大, 一般分为三种情况, 一是数据在处理 UI 线程, 二是数据处理占用 CPU 高, 导致主线程拿不到时间片, 三是内存增加导致 GC 频繁, 从而引起卡顿.
我们知道, Android 的绘制需要经过 onMeasure,onLayout,onDraw 等几个步骤, 所以布局的层级越深, 元素越多, 耗时也就越长. 还有就是 Android 系统每隔 16ms 发出 VSYNC 信号, 触发对 UI 进行渲染, 如果每次渲染都成功, 这样就能够达到流畅的画面所需的 60FPS. 如果某个操作花费的时间是 24ms , 系统在得到 VSYNC 信号时就无法正常进行正常渲染, 这样就发生了丢帧现象.
之所以出现卡顿现象, 是因为有两个原因:
绘制任务太重, 绘制一帧内容耗时太长
主线程太忙, 根据系统传递过来的 VSYNC 信号来时还没准备好数据导致丢帧
基于问题产生的原因, 我们可以从以下几个方面进行优化:
布局优化
在 Android 种系统对 View 进行测量, 布局和绘制时, 都是通过对 View 数的遍历来进行操作的. 如果一个 View 数的高度太高就会严重影响测量, 布局和绘制的速度. Google 也在其 API 文档中建议 View 高度不宜哦过 10 层. 现在版本种 Google 使用 RelativeLayout 替代 LineraLayout 作为默认根布局, 目的就是降低 LineraLayout 嵌套产生布局树的高度, 从而提高 UI 渲染的效率. 在布局优化方面, 我们可以从以下几个方面进行优化:
布局复用, 使用 < include > 标签重用 layout;
提高显示速度, 使用 < ViewStub > 延迟 View 加载;
减少层级, 使用 < merge > 标签替换父级布局;
注意使用 wrap_content, 会增加 measure 计算成本;
删除控件中无用属性;
渲染优化
过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次. 在多层次重叠的 UI 结构中, 如果不可见的 UI 也在做绘制的操作, 就会导致某些像素区域被绘制了多次, 从而浪费了多余的 CPU 以及 GPU 资源. 我们可以通过开启手机的过渡绘制功能来检测页面是否被过度绘制.
为了避免过度绘制, 我们可以从以下几个方面进行优化:
布局上的优化, 移除 xml 中非必须的背景, 移除 Windows 默认的背景, 按需显示占位背景图片.
自定义 View 优化, 使用 canvas.clipRect() 来帮助系统识别那些可见的区域, 只有在这个区域内才会被绘制.
启动优化
应用一般都有闪屏页, 优化闪屏页的 UI 布局, 可以通过 Profile GPU Rendering 检测丢帧情况. 也可以通过启动加载逻辑优化. 可以采用分布加载, 异步加载, 延期加载策略来提高应用启动速度. 数据准备. 数据初始化分析, 加载数据可以考虑用线程初始化等策略.
刷新优化
Android 开发中, 通常是异步操作页面的, 因此需要可以从刷新优化上来优化应用, 主要有两个原则:
减少刷新次数;
缩小刷新区域;
动画优化
在实现动画效果时, 需要根据不同场景选择合适的动画框架来实现. 有些情况下, 可以用硬件加速方式来提供流畅度.
耗电优化
在移动设备中, 电池的重要性不言而喻, 没有电什么都干不成. 对于操作系统和设备开发商来说, 耗电优化一致没有停止, 去追求更长的待机时间, 而对于一款应用来说, 并不是可以忽略电量使用问题, 特别是那些被归为 "电池杀手" 的应用, 最终的结果是被卸载. 因此, 应用开发者在实现需求的同时, 需要尽量减少电量的消耗.
在 Android5.0 以前, 在应用中测试电量消耗比较麻烦, 也不准确, 5.0 之后专门引入了一个获取设备上电量消耗信息的 API, 即 Battery Historian.Battery Historian 是一款由 Google 提供的 Android 系统电量分析工具, 和 Systrace 一样, 是一款图形化数据分析工具, 直观地展示出手机的电量消耗过程, 通过输入电量分析文件, 显示消耗情况, 最后提供一些可供参考电量优化的方法.
网络优化
对于网络的优化, 可以从以下几个方面着手进行:
图片网络优化
例如, 针对网络情况, 返回不同的图片数据, 一种是高清大图, 一种是正常图片, 一种是缩略小图. 当用户处于 Wi-Fi 下给控件设置高清大图, 当 4g 或者 3g 模式下加载正常图片, 当弱网条件下加载缩略图.
网络数据优化
移动端获取网络数据优化可以从以下几点着手:
连接复用: 节省连接建立时间, 如开启 keep-alive. 对于 Android 来说默认情况下 HttpURLConnection 和 HttpClient 都开启了 keep-alive. 只是 2.2 之前 HttpURLConnection 存在影响连接池的 Bug, 具体可见: Android HttpURLConnection 及 HttpClient 选择
请求合并: 即将多个请求合并为一个进行请求, 比较常见的就是网页中的 CSS Image Sprites. 如果某个页面内请求过多, 也可以考虑做一定的请求合并.
减少请求数据的大小: 对于 post 请求, body 可以做 gzip 压缩的, header 也可以做数据压缩. 返回数据的 body 也可以做 gzip 压缩, body 数据体积可以缩小到原来的 30% 左右.
异常拦截优化
在获取数据的流程中, 访问接口和解析数据时都有可能会出错, 我们可以通过拦截器在这两层拦截错误.
在访问接口时, 我们不用设置拦截器, 因为一旦出现错误, Retrofit 会自动抛出异常. 比如, 常见请求异常 404,500,503 等等.
在解析数据时, 我们设置一个拦截器, 判断 Result 里面的 code 是否为成功, 如果不成功, 则要根据与服务器约定好的错误码来抛出对应的异常. 比如, token 失效, 禁用同账号登陆多台设备, 缺少参数, 参数传递异常等等.
APK 瘦身
应用安装包大小对应用使用没有影响, 但应用的安装包越大, 用户下载的门槛越高, 特别是在移动网络情况下, 用户在下载应用时, 对安装包大小的要求更高, 因此, 减小安装包大小可以让更多用户愿意下载和体验产品.
在 Android Studio 工具栏里, 打开 build->Analyze APK, 选择要分析的 APK 包 , 可以看到 apk 的相关信息, 如下所示:
Android 的 apk 主要有以下信息构成:
assets 文件夹. 存放一些配置文件, 资源文件, assets 不会自动生成对应的 ID, 而是通过 AssetManager 类的接口获取.
res.res 是 resource 的缩写, 这个目录存放资源文件, 会自动生成对应的 ID 并映射到 .R 文件中, 访问直接使用资源 ID.
META-INF. 保存应用的签名信息, 签名信息可以验证 APK 文件的完整性.
AndroidManifest.xml. 这个文件用来描述 Android 应用的配置信息, 一些组件的注册信息, 可使用权限等.
classes.dex.Dalvik 字节码程序, 让 Dalvik 虚拟机可执行, 一般情况下, Android 应用在打包时通过 Android SDK 中的 dx 工具将 Java 字节码转换为 Dalvik 字节码.
resources.arsc. 记录着资源文件和资源 ID 之间的映射关系, 用来根据资源 ID 寻找资源.
基于上面的组成部分, 那么优化也可以从以下几个方面着手:
代码混淆. 使用 proGuard 代码混淆器工具, 它包括压缩, 优化, 混淆等功能.
资源优化. 比如使用 Android Lint 删除冗余资源, 资源文件最少化等.
图片优化. 比如利用 AAPT 工具对 PNG 格式的图片做压缩处理, 降低图片色彩位数等.
避免重复功能的库, 使用 webP 图片格式等.
插件化, 比如功能模块放在服务器上, 按需下载, 可以减少安装包大小.
最后
如果你看到了这里, 觉得文章写得不错就给个赞呗? 如果你觉得那里值得改进的, 请给我留言. 一定会认真查询, 修正不足. 谢谢.
最后文末放上一个技术交流群: Android 架构设计 (185873940)
PS: 群内有许多技术大牛, 有任何问题, 欢迎广大网友一起来交流, 群内还不定期免费分享高阶 Android 学习视频资料和面试资料包~
再推荐一篇文章, 具体的架构视频, 面试专题, 学习笔记都在这篇文章中:"寒冬未过", 阿里 P9 架构分享 Android 必备技术点, 让你 offer 拿到手软!
偷偷说一句: 群里高手如云, 欢迎大家加群和大佬们一起交流讨论啊~
来源: http://www.jianshu.com/p/6012949aaaf3