一、为什么我们需要自己动手做性能分析

咱们平时开发APP,最怕的就是用户说“卡”“闪退”“耗电快”。这些问题往往不是代码写错了,而是性能没调好。Android SDK里自带了一整套性能分析工具,能帮我们找到那些肉眼看不见的瓶颈。比如一个列表滑动不流畅,用眼睛看代码可能看不出问题,但用Profile工具一跑,就能发现是某个方法执行太久占用了主线程。所以,学会用这些工具,就像给APP做了个全身体检,哪里有问题一目了然。

二、Android SDK自带的“体检中心” – Profiler家族

Android Studio自带的Profiler是一个集成面板,可以实时监控CPU、内存、网络和能耗。咱们不用装任何第三方插件,直接在Studio里点击底部的“Profiler”标签就能打开。

2.1 CPU Profiler:找到拖慢主线程的元凶

CPU Profiler会记录每个线程执行的方法耗时。比如你的APP打开一个页面要2秒,用CPU Profiler一看,发现主线程里有个图片解码操作占了1.5秒。这时候你就知道该把解码放到子线程了。

怎么用?
点击CPU Profiler上的“Record”按钮,操作一下APP,然后停止。它会生成一个火焰图(Flame Chart),每个色块代表一个方法,宽度表示耗时。越宽的方法越值得优化。

2.2 Memory Profiler:揪出偷偷吃内存的对象

内存泄漏是Android的老大难。Memory Profiler可以实时看堆内存的使用情况,还能手动触发GC(垃圾回收)并抓取堆转储文件(Heap Dump)。抓下来之后,你就能看到每个类有多少实例,哪个对象占了多少内存。

举个例子: 如果你发现Activity的实例数量比预期多,而且没有被释放,大概率是发生了泄漏。比如某个内部类持有了Activity的引用,导致Activity无法被回收。

2.3 Network Profiler:观察请求的每一个字节

Network Profiler会列出每个网络请求的发送时间、响应时间、数据大小。如果发现某个请求特别慢,可以看看是不是DNS解析、或者请求体太大。它还能显示响应体的JSON内容,帮你快速定位接口问题。

2.4 Energy Profiler:打败耗电怪兽

Energy Profiler监控CPU、网络、GPS等硬件模块的使用情况。如果发现某个后台任务频繁唤醒CPU,就可以改成批量执行或者使用WorkManager,减少耗电。

三、实战:用Memory Profiler查找内存泄漏

下面咱们用一个具体例子来演示。假设你有一个页面,每次打开都会创建很多对象,但是退出页面后这些对象没有被回收。咱们用Memory Profiler一步步排查。

技术栈:Kotlin + Android SDK

3.1 先制造一个泄漏场景

咱们写一个简单的Activity,里面有个静态变量持有了Activity的引用:

// 以下代码演示一个典型的内存泄漏
class LeakActivity : AppCompatActivity() {
    // 静态变量持有Activity的Context,导致Activity无法被回收
    companion object {
        private var leakedView: View? = null
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_leak)

        // 把一个View赋值给静态变量,这个View引用了Activity的Context
        leakedView = findViewById<View>(R.id.my_view)
        // 假设这里做一些初始化操作
    }

    override fun onDestroy() {
        super.onDestroy()
        // 注意:这里没有将leakedView置空,所以Activity泄漏了
    }
}

3.2 打开Memory Profiler并录制操作

  1. 在Android Studio中运行APP,进入到LeakActivity。
  2. 点击Profiler标签,选择Memory。
  3. 点击“Record heap dump”按钮(蓝色垃圾桶图标旁边)抓取当前堆快照。
  4. 按返回键退出LeakActivity,等待几秒让GC有机会运行。
  5. 再次抓取一个堆快照。

3.3 分析堆快照

对比两个堆快照,你会看到LeakActivity的实例数量仍然是1,没有被回收。展开堆栈信息,能看到leakedView引用了Activity。

解决办法: 在onDestroy里将leakedView置为null:

override fun onDestroy() {
    super.onDestroy()
    // 清理静态引用,避免泄漏
    leakedView = null
}

3.4 使用Android Profiler的“GC”按钮

如果不想手动抓堆快照,可以点击Profiler上的“Garbage Collect”按钮(垃圾桶图标),然后看内存曲线是否下降。如果曲线不降,说明有对象被泄漏了。

四、用Traceview和Systrace做更深层的分析

除了Profiler,Android SDK还提供了两个更底层的工具:Traceview和Systrace。

4.1 Traceview:方法级别的精准计时

Traceview可以记录每个方法的执行时间,包括调用次数、CPU时间、实际耗时。它比CPU Profiler更细致,适合分析某个方法到底慢在哪里。

使用方式: 在代码中埋点,或者用DDMS(现在已整合到Android Studio)启动跟踪。

// 用Debug类手动开始和结束跟踪
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 开始跟踪,生成文件到/sdcard/下
        Debug.startMethodTracing("my_app_trace")
        // ... 你的业务逻辑
        Debug.stopMethodTracing() // 停止跟踪
    }
}

生成的.trace文件可以直接拖到Android Studio里打开,或者用命令行工具systrace.py分析。

4.2 Systrace:系统级别的性能追踪

Systrace可以监控整个Android系统的行为,包括CPU调度、磁盘I/O、SurfaceFlinger等。如果你发现APP卡顿但代码逻辑没问题,可能是系统资源竞争导致的。

怎么用? 在命令行中运行:

# 生成一个5秒的系统跟踪文件
python systrace.py -t 5 -o my_trace.html

这个HTML文件可以在浏览器里打开,查看每个时间段CPU、GPU、内存等状态。比如你发现某个时间点CPU利用率骤降,可能是线程被其他进程抢占。

五、其他实用的小工具和技巧

除了上面几个,Android SDK里还有一些“隐藏”功能,特别适合日常开发。

5.1 StrictMode:开发阶段的警察

StrictMode可以在开发阶段自动检测在主线程上做了耗时操作(比如网络请求、文件读写),一旦发现就在Logcat里打印WARNING。你只需要在Application的onCreate里开启:

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        // 开启StrictMode的线程策略
        StrictMode.setThreadPolicy(
            StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()   // 检测磁盘读
                .detectDiskWrites()  // 检测磁盘写
                .detectNetwork()     // 检测网络请求
                .penaltyLog()        // 违规时打印日志
                .build()
        )
        // 开启虚拟机策略(检测内存泄漏等)
        StrictMode.setVmPolicy(
            StrictMode.VmPolicy.Builder()
                .detectActivityLeaks() // 检测Activity泄漏
                .detectLeakedClosableObjects() // 检测未关闭的资源
                .penaltyLog()
                .build()
        )
    }
}

这样你每次测试APP时,如果主线程里执行了网络请求,Logcat会直接显示一条红色警告,提醒你赶紧改。

5.2 使用dumpsys获取系统状态

通过adb命令adb shell dumpsys可以获取大量系统信息,例如内存、电量、进程状态。比如想看当前APP的内存详情:

# 查看指定包名的内存信息
adb shell dumpsys meminfo com.example.myapp

输出会显示Java Heap、Native Heap、Code、Stack等各个区域的大小,帮助判断是否有异常增长。

5.3 巧用Logcat过滤

性能问题往往伴随着大量日志。你可以用adb logcat -v time *:W只显示WARNING级别以上的日志,避免被无关信息干扰。

六、总结与注意事项

6.1 应用场景

  • 日常开发与自测: 用StrictMode和Profiler快速发现潜在问题。
  • 线上问题排查: 借助堆快照和Trace文件定位用户反馈的卡顿、闪退。
  • 版本迭代优化: 对比新旧版本的内存、CPU曲线,确保没有退化。

6.2 技术优缺点

优点:

  • Android SDK自带,无需额外安装。
  • 工具链完善,从方法级到系统级全覆盖。
  • 与Android Studio深度集成,操作直观。

缺点:

  • 实时监控会对性能有影响(尤其是CPU Profiler),生产环境不宜使用。
  • 堆快照分析需要一定经验,新手可能看不懂引用链。
  • 不同厂商的定制系统可能屏蔽了部分API(如Systrace需要root或target SDK兼容)。

6.3 注意事项

  • 不要在正式版本中开启StrictMode,否则影响用户体验。
  • 分析堆快照时,要注意区分“短暂存活的对象”和“泄漏对象”,最好触发多次GC后再抓取。
  • CPU Profiler的火焰图在高并发线程下可能不准确,可以用Traceview作为补充。
  • 注意权限:Systrace需要开发者选项中的“系统跟踪”或adb授权。

6.4 文章总结

借助Android SDK自带的性能分析工具,我们可以像医生一样给APP做诊断。从Profiler的实时面板到Traceview的方法计时,再到StrictMode的日常防护,这些工具帮助我们写出更流畅、更省电的应用。关键是养成“写代码-测性能-调优”的习惯,把性能分析融入到日常开发流程中,而不是等到用户投诉才想起。希望本篇博客能帮你迈出第一步,下次遇到卡顿别慌,打开Profiler就完事了。