Android 性能监控系列一(原理篇)

欢迎关注微信公众号:BaronTalk,获取更多精彩好文!java

一. 前言

性能问题是致使 App 用户流失的罪魁祸首之一,若是用户在使用咱们 App 的时候遇到诸如页面卡顿、响应速度慢、发热严重、流量电量消耗大等问题的时候,极可能就会卸载掉咱们的 App。而每每获取用户的成本是高昂的,所以由于性能问题致使用户流失的状况是咱们要极力避免的,作很差这一点是咱们开发人员的失职。git

去年咱们团队完成了整个项目架构方面的重构(有兴趣的同窗能够参考我以前的文章安居客 Android 项目架构演进 Android 模块化探索与实践 ),目前已经可以很好的支撑咱们的业务,并对团队的开发效率也有了必定的提高、项目质量也有了大幅的进步。github

可是项目上线后,到底有没有性能问题?用户体验到底怎么样?在用户的使用场景中到底会遇到哪些性能问题?咱们项目的性能短板又在哪里?这些问题的答案咱们都不得而知,所以开发一套完善的性能监控体系势在必行。咱们团队在今年开始着手开发本身的性能监控组件 APM,但愿经过它来采集线上性能数据,找到性能短板,针对性的优化用户体验。api

APM 全称 Application Performance Management & Monitoring (应用性能管理/监控)服务器

后面我会经过一系列的文章来介绍 APM 的原理、框架设计与实现等等。本篇就是这个系列的第一篇,主要从实现原理方面来介绍 APM。按照目前的计划,这个系列大体会从以下几个方面来展开:微信

  • 原理篇:主要介绍 APM 的实现原理;
  • 设计篇:介绍整个 APM 框架设计;
  • 实现篇-Gradle Plugin:介绍 Gradle 插件在 APM 项目中的应用,以及如何开发一个 Gradle Plugin;
  • 实现篇-Javassist/ASM:Javassist、ASM 等字节码操做库的介绍,以及如何使用它们在编译时插入代码来采集各项性能数据;
  • 实现篇-数据存储及上报:介绍 APM 框架的存储上报机制及实现过程;
  • 发布集成:最后会介绍如何将库发布到 jCenter() 以及如何在生产项目中集成。

这里要向你们交代一点是,以前的文章为了极力作到将复杂的问题用通俗易懂的方式解释清楚,又要面面俱到,每每篇幅过长;诸如以前写过的RxJava系列6(从微观角度解读RxJava源码) 神兵利器Dagger2 安居客 Android 项目架构演进 Android 模块化探索与实践 写给 Android 应用工程师的 Binder 原理剖析等文章,篇幅一般都在 8000~10000字以上,通篇阅读下来可能须要近半个小时的时间,不太符合当下碎片化阅读的需求;所以在后面的写做上会控制篇幅,尽可能控制在 10 分钟之内的长度。markdown

这也是我为何会将 APM 做为一个系列来介绍的缘由,同时这也能保证后面在介绍 APM 的时候可以深刻到实现细节,避免泛泛而谈。架构

二. Android APM 的基本原理

市场上有不少商业化的 APM 平台,好比著名的 NewRelic,还有国内的 听云、OneAPM 等等。这些平台的工做流程基本都是一致的:框架

  1. 首先在客户端(Android、iOS、Web等)采集数据;
  2. 接着将采集到的数据整理上报到服务器;
  3. 服务器接收到数据后建模、存储、挖掘分析,让后将数据可视化,供用户使用。

以下图: 模块化

APM 工做流程

咱们介绍的 Android APM 框架其实就是在 Android 平台上应用的一个数据采集上报 SDK。主要包含三大模块:

  1. 数据采集
  2. 数据存储
  3. 数据上报

其中数据采集是整个 APM 框架的核心。

数据采集咱们能够经过手动埋点的方式,但这种方式工做量巨大、不灵活,并且没法覆盖到全部场景;所以只能经过自动化的方式来采集数据。在应用构建期间,经过修改字节码的方式来进行字节码插桩就是实现自动化的方案之一。

三. Android 打包流程及字节码插桩原理

在谈字节码插桩的原理以前,首先咱们看看 Android 的打包流程,以下图:

Android 打包流程

从上面这张打包流程图咱们能够看到,一个 App 的全部 class 文件,包括第三方的 class 文件都会通过 dex 的过程打包成一个或者多个 dex 文件。

这其中涉及到两个很关键的环节:

  1. javac:将 .java 格式的源代码文件编译成 class 文件;
  2. dex: 将 class 格式的文件打包汇总,组成一个或者多个 dex 文件。

咱们想要对字节码进行修改,只须要在 javac 以后 dex 以前遍历全部的字节码文件,并按照必定的规则过滤修改就行了,这里即是字节码插桩的入口。

那么咱们到底如何介入打包过程,在 class 转换为 dex 文件的时候实现对字节码的修改呢?

答案是 transform api

Android Gradle Plugin 1.5.0 及以上版本,Google 官方提供了 transform api 做为字节码插桩的入口。咱们只须要实现一个自定义的 Gradle Plugin,而后在编译阶段去修改字节码文件。对于 Gradle Plugin 的具体实现后面的文章再作详细讲解。

四. 修改字节码

找到了插桩入口,接下来就要对字节码进行修改。对于字节码的修改,比较经常使用的框架有 Javassist 和 ASM。

  1. Javassist 是一个开源的分析、编辑和建立 Java 字节码的类库,它提供了源码级别的 API 以及字节码级别的 API,源码级别的 API,直接使用 Java 编码的形式,而不须要深刻了解虚拟机指令,就能动态改变类的结构或者动态生成类。

  2. ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者加强既有类的功能。ASM 能够直接产生二进制 class 文件,也能够在类被加载入 Java 虚拟机以前动态改变类行为。

ASM 和 Javassit 相比,API 贴近底层,比较难使用,须要对 Java 字节码和虚拟机方面有必定程度的了解。ASM 的优势就在于性能上的优点,且更加灵活;Javassist 的实现中大量使用的反射,因此性能偏低。

简单的说就是 ASM 虽然难以使用,可是功能强大效率高。是不少无痕埋点、APM框架的首选方案。

ASM 的具体时候咱们放到这个系列后面的文章介绍。

五. 总结

Android APM 的原理其实很是简单,用一句话总结就是:

依据打包原理,在 class 转换为 dex 的过程当中,调用 gradle transform api 遍历 class 文件,借助 Javassist、ASM 等框架修改字节码,插入咱们本身的代码实现性能数据的统计。

以上全部过程都是在编译期完成的。

其实 Android 上的无痕埋点也是一样的原理,区别只不过是咱们 hook 的点不一样,采集的数据不一样,所以掌握了 APM 的实现原理一样能够实现无痕埋点系统。

原理很简单,难的是实现细节。好比如何插桩采集到页面帧率、流量、耗电量等等。这些具体细节咱们放到后面一一介绍。至于为何放到后面……由于不少东西本身没作过我也不知道啊……🤣

若是你喜欢个人文章,就关注下个人公众号 BaronTalk知乎专栏 或者在 GitHub 上添个 Star 吧!

相关文章
相关标签/搜索