Administrator
发布于 2026-08-25 / 29 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 13 篇:性能监控框架:从埋点到线上 APM 的闭环

性能优化不能只靠本地 Profile。用户手里的设备千奇百怪:低端机、老旧系统、弱网环境、后台被杀。你只有在线上真实环境中采集数据,才能发现真正的问题。

这篇会讲如何搭建 Android 性能监控体系:从指标选择、本地采集、上报策略,到最终形成 APM 闭环。


一、APM 应该监控什么?

不是指标越多越好。初级阶段盯住这几个核心指标:

维度

核心指标

启动

冷启动时长、热启动时长、首屏渲染时间

流畅度

FPS、卡顿帧数、卡顿堆栈

内存

Java Heap、Native Heap、OOM 次数

网络

请求耗时、失败率、慢请求 Top10

崩溃

Java Crash、Native Crash、ANR

耗电

后台任务时长、定位/GPS 使用次数


二、本地采集:关键指标怎么算?

2.1 启动时间

Application 启动:

class MyApp : Application() {
    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        // 记录进程启动时间
        ProcessStart.record()
    }

    override fun onCreate() {
        super.onCreate()
        // Application 初始化完成
        ProcessStart.appOnCreateEnd()
    }
}

Activity 首帧:

class MainActivity : AppCompatActivity() {
    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) {
            ProcessStart.firstFrame()
        }
    }
}

2.2 FPS 与卡顿

用 Choreographer 采集帧耗时:

Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
    private var lastFrameTime = 0L

    override fun doFrame(frameTimeNanos: Long) {
        if (lastFrameTime != 0L) {
            val diffMs = (frameTimeNanos - lastFrameTime) / 1_000_000
            if (diffMs > 16.6f) {
                recordJank(diffMs)
            }
        }
        lastFrameTime = frameTimeNanos
        Choreographer.getInstance().postFrameCallback(this)
    }
})

2.3 内存

val runtime = Runtime.getRuntime()
val usedMem = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
val maxMem = runtime.maxMemory() / 1024 / 1024

val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memInfo)
val availMem = memInfo.availMem / 1024 / 1024

2.4 网络请求耗时

用 OkHttp 拦截器统一采集:

class MetricsInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val start = SystemClock.elapsedRealtime()
        val request = chain.request()
        val response = chain.proceed(request)
        val cost = SystemClock.elapsedRealtime() - start

        NetworkReporter.report(
            url = request.url.toString(),
            code = response.code,
            cost = cost
        )
        return response
    }
}

三、埋点设计:少而精

3.1 事件埋点 vs 指标埋点

类型

例子

特点

事件埋点

点击按钮、进入页面

离散、可分析用户路径

指标埋点

启动时长、FPS

连续、可聚合统计

性能监控以指标埋点为主,事件埋点为辅。

3.2 采样策略

全量上报会拖垮 App 和后端。合理采样:

object Sampler {
    fun shouldReport(event: String): Boolean {
        return when (event) {
            "app_launch" -> true          // 启动全量
            "fps" -> Random.nextFloat() < 0.1f  // FPS 10% 采样
            "network" -> Random.nextFloat() < 0.05f // 网络 5% 采样
            else -> Random.nextFloat() < 0.01f
        }
    }
}

3.3 用户维度聚合

上报时带上:

  • 设备型号、系统版本。

  • App 版本、渠道。

  • 网络类型(WiFi/4G/5G)。

  • 用户分桶 ID(用于 A/B 实验)。

这样可以在 APM 后台按维度下钻。


四、上报策略:离线缓存 + 批量上传

4.1 本地缓存

class MetricsCache(private val file: File) {

    fun append(event: MetricEvent) {
        file.appendText(Json.encodeToString(event) + "\n")
    }

    fun readAndClear(): List<MetricEvent> {
        if (!file.exists()) return emptyList()
        val lines = file.readLines()
        file.delete()
        return lines.map { Json.decodeFromString(it) }
    }
}

4.2 批量上报

fun flushCache() {
    val events = cache.readAndClear()
    if (events.isEmpty()) return

    viewModelScope.launch(Dispatchers.IO) {
        try {
            api.reportBatch(events)
        } catch (e: Exception) {
            // 失败则重新写回缓存
            events.forEach { cache.append(it) }
        }
    }
}

触发时机:

  • App 切换到后台。

  • 缓存达到一定条数(如 50 条)。

  • 每 5 分钟一次。

  • 用户手动退出。


五、从数据到行动:APM 闭环

采集数据只是第一步,更重要的是根据数据优化。

5.1 建立基线

先定义每个指标的 P50、P90、P99 基线:

指标

P50 目标

P90 目标

P99 目标

冷启动

< 800ms

< 1.2s

< 2s

FPS

58

55

50

接口耗时

< 200ms

< 500ms

< 1s

5.2 告警与归因

APM 后台需要:

  • 指标异常时自动告警(如 P99 启动时间环比上涨 20%)。

  • 按版本、机型、地域下钻。

  • 卡顿堆栈自动聚类,定位高频问题函数。

5.3 闭环流程

采集 → 上报 → 聚合 → 告警 → 定位 → 修复 → 验证 → 采集

没有闭环的 APM 只是数据 dashboard。


六、开源与商业方案

方案

类型

适用场景

Firebase Performance

商业

快速接入,中小项目

APMobile / 听云

商业

国内合规、大型项目

Elastic APM

开源

自建平台

自研 SDK

自定义

高度定制、数据敏感

建议:先用商业方案跑通流程,再决定是否自研。


小结

性能监控框架是连接线上和研发的桥梁:

  1. 指标不在多,关键是启动、FPS、内存、网络、崩溃。

  2. 本地采集用 Choreographer、Runtime、OkHttp 拦截器。

  3. 上报要采样、批量、离线缓存。

  4. APM 必须形成闭环:采集 → 告警 → 定位 → 修复 → 验证。

  5. 小型项目先用商业方案,大型项目再考虑自研。


下篇预告

下一篇《第 14 篇:Android 安全:反编译、加固、签名与数据保护》会讲 Android 安全:

  1. APK 反编译有多容易。

  2. 代码混淆、VMP、加固的基本原理。

  3. 本地数据、密钥、通信的安全实践。


参考阅读

  • Android 官方 Profiling 文档

  • Firebase Performance Monitoring 文档

  • 《Android 性能优化:权威指南》

  • 上一篇文章:第 12 篇《Native 集成:JNI、NDK、C++ 与 Kotlin 的协作》


评论