Android 性能优化:05.CPU优化

一、CPU 优化的核心原理

从 CPU 角度看,程序执行速度由三个因子决定:

程序执行时间 = 指令数 × 每条指令的平均时钟周期(CPI) × 时钟周期时间

优化任何一个因子都能提升速度:

优化方向 具体手段
减少指令数 更优算法、更简洁代码、多线程并发、预加载减少运行时指令
降低 CPI 减少 IO 等待、优化缓存命中率、选择更高效的编程语言/编译器
降低时钟周期 避免 CPU 降频(控制发热)、利用高性能核心

关键认知 :当手机发热时,CPU 会通过降频来减少发热。因此,避免长时间高负荷使用 CPU,不仅是优化性能,也是防止性能倒退的重要手段。


二、CPU 性能分析工具

1. Perfetto / System Trace

Perfetto 是 Systrace 的继任者,是 Android 10+ 的系统级追踪工具,能查看 Frame Timeline、CPU 调度、Binder、I/O 等全链路信息。

使用方式

bash 复制代码
# 抓取系统 trace
adb shell perfetto -o /data/misc/perfetto-traces/trace_file.perfetto-trace -t 10s \
  sched freq idle gfx view wm am ss

分析要点

  • 查看 Frames 行,红色 F 表示丢帧
  • 分析主线程和 RenderThread 的耗时分布
  • 查看 CPU 核心调度情况

2. Android Studio CPU Profiler

支持三种模式:

模式 适用场景 性能开销
System Trace 系统级分析,查看线程调度、渲染流程
Java Method Trace 精确分析 Java 方法耗时 高(5-10倍)
C/C++ Function Trace Native 代码分析

分析视图

  • Call Chart:查看调用关系,系统 API(黄)、应用代码(绿)、第三方(蓝)
  • Flame Chart:聚合多次调用,识别热点函数
  • Top Down:从顶层方法逐层展开,看耗时分布
  • Bottom Up:从底层方法向上追溯调用方

优化时重点关注 Incl Cpu Time(含子函数总耗时)和 Calls+Recur(调用次数)。

3. TraceView(埋点分析)

通过代码埋点精确追踪特定方法:

java 复制代码
Debug.startMethodTracing("myapp");
// 需要分析的代码
Debug.stopMethodTracing();

生成的 .trace 文件可用 Android Studio 打开。适合精确分析特定函数,但运行时开销很大。

4. 芯片级工具(深度优化)

  • Snapdragon Profiler:高通官方工具,支持 150+ 硬件性能计数器,实时监控 CPU/GPU/DSP
  • ARM Performance Studio:含 Streamline(系统级 CPU/GPU 分析)、Frame Advisor 等

三、CPU 优化具体手段

1. 线程池优化

合理配置线程池是 CPU 优化的基础

java 复制代码
// CPU 密集型任务:线程数 = CPU核心数 + 1
int cpuCount = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cpuCount + 1);

// IO 密集型任务:线程数可以更多
ExecutorService ioPool = Executors.newFixedThreadPool(cpuCount * 2 + 1);

最佳实践

  • 合并网络库和图片库的线程池,避免创建过多线程
  • 禁止直接使用 new Thread(),统一通过线程工厂管理
  • 配合 Lint 检查线程使用规范

2. 减少 CPU 闲置 --- 预加载策略

大部分应用 CPU 不会持续高负载,利用闲置时间预加载数据/View,可减少核心场景的指令数。

检测 CPU 闲置的推荐方案 (Native 的 times 函数):

cpp 复制代码
#include <sys/times.h>
#include <unistd.h>

bool isCpuIdle() {
    struct tms buf;
    clock_t utime, stime;
    static clock_t lastUtime = 0, lastStime = 0;
    
    times(&buf);
    utime = buf.tms_utime;
    stime = buf.tms_stime;
    
    long ticksPerSec = sysconf(_SC_CLK_TCK);
    double userUsage = (double)(utime - lastUtime) / ticksPerSec;
    double sysUsage = (double)(stime - lastStime) / ticksPerSec;
    
    lastUtime = utime;
    lastStime = stime;
    
    return (userUsage + sysUsage) < THRESHOLD; // 如 5%
}

注意:Android 8.0+ 不再支持读取 /proc/stat,因此推荐使用 times 函数方案。

3. 减少 CPU 等待 --- IO 任务分离

IO 操作由 DMA 执行,CPU 会等待或切换。将 IO 从主线程分离:

复制代码
优化前:主线程 → IO读取 → 数据处理 → UI更新
优化后:主线程 → 默认数据展示 → 异步IO → IO完成 → UI更新

核心原则:将主流程拆得足够细,先执行不依赖 IO 的处理,数据到达后再刷新。

4. 锁优化

锁竞争会导致线程阻塞和 CPU 空转:

优化策略 说明
缩小锁粒度 只锁必要代码块,不要锁整个方法
减少锁持有时间 锁内不做耗时操作
使用读写锁 读多写少场景用 ReentrantReadWriteLock
无锁数据结构 ConcurrentHashMapAtomicInteger
避免锁嵌套 防止死锁和优先级反转

5. 算法与数据结构优化

  • 选择时间复杂度更低的算法
  • 避免在循环中创建对象(减少 GC 压力)
  • 使用 SparseArray 替代 HashMap<Integer, Object>
  • 字符串拼接用 StringBuilder,避免 String.format(耗时高)

6. 减少 GC 压力

频繁 GC 会抢占 CPU 资源:

  • 对象池复用(如 Message.obtain()
  • 避免内存泄漏(使用 LeakCanary 检测)
  • 大对象使用 BitmapFactory.Options 进行采样加载
  • 使用 ArrayMap / SparseArray 替代 HashMap

四、任务调度优化

1. 提升核心线程优先级

通过 Process.setThreadPriority() 调整线程优先级(范围 -20 到 19,-20 最高):

java 复制代码
// 提升主线程优先级
Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_URGENT_DISPLAY);

// 提升渲染线程优先级
int renderTid = getRenderThreadTid(); // 遍历 /proc/pid/task 获取
if (renderTid != -1) {
    Process.setThreadPriority(renderTid, -19);
}

原则:提高核心线程优先级,降低非核心线程优先级,两者配合才能高效提升速度。

2. 核心线程绑定 CPU 大核

现代手机 CPU 采用大小核架构,将关键线程绑定到大核可显著提升性能:

java 复制代码
// 获取最大频率的 CPU 核心
private int getMaxFreqCPUIndex() {
    int cores = Runtime.getRuntime().availableProcessors();
    int maxFreq = -1, maxIndex = 0;
    for (int i = 0; i < cores; i++) {
        File file = new File("/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq");
        // 读取频率,找出最大值
    }
    return maxIndex;
}
cpp 复制代码
// Native 层绑定线程到指定核心
#include <sched.h>

extern "C" JNIEXPORT jint JNICALL
Java_bindMaxFreqCore(JNIEnv *env, jobject, jint cpuIndex, jint tid) {
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(cpuIndex, &mask);
    return sched_setaffinity(tid, sizeof(mask), &mask); // 0 表示成功
}

适用场景

  • 渲染线程绑定大核,提升帧率稳定性
  • 游戏主线程绑定大核,减少卡顿
  • 启动阶段关键线程绑定大核,加速启动

3. Input 链路的系统级优化

对于极致流畅度要求,可在系统层面优化:

  • irq 绑定大核:将输入中断处理绑定到高性能核心
  • InputDispatcher 优先派发:对焦点应用优先派发事件
  • 触屏固件提频:在触摸事件发生时临时提升 CPU 频率
  • 不等 VSync 直接触发 :对于点击事件,直接触发 doFrame 而非等待下一个 VSync

五、编译期优化

1. R8 Full Mode(AGP 8.0+ 默认启用)

R8 作为 ProGuard 的继任者,在 Full Mode 下可进行更深度的优化:

  • 值假设优化
  • 空值传播
  • 死代码消除
  • 内联优化

配置

groovy 复制代码
android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
        }
    }
}

2. Baseline Profiles

通过提供预编译的 Profile,让 ART 在应用安装时就完成 AOT 编译,减少运行时 JIT 开销:

kotlin 复制代码
// 使用 Macrobenchmark 生成 Baseline Profile
baselineProfile {
    managedDevice = "pixel6Api31"
    // 运行关键用户场景
}

效果 :启动时间可缩短 20%-60%

3. 启动 Profile(Startup Profiles)

针对应用启动路径的专项优化,将启动链路中的方法提前编译为机器码。


六、度量与监控

1. 本地度量 --- Macrobenchmark

kotlin 复制代码
@LargeTest
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = "com.example.app",
        metrics = listOf(StartupTimingMetric(), FrameTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.COLD
    ) {
        pressHome()
        startActivityAndWait()
    }
}

2. 线上监控 --- Android Vitals

关注 Google Play 控制台中的核心指标:

  • ANR 率:主线程阻塞超过 5 秒
  • Slow frames:渲染超过 16ms 的帧占比
  • Frozen frames:渲染超过 700ms 的帧
  • Excessive wakeups:过度唤醒次数

3. 自定义 CPU 监控

java 复制代码
// 读取 /proc/pid/stat 计算进程 CPU 使用率
private float getProcessCpuUsage() {
    // 读取进程 utime + stime
    // 读取 /proc/stat 获取系统总时间
    // 计算 (进程时间差 / 系统时间差) * 100%
}

七、CPU 优化 Checklist

优先级 优化项 预期收益
P0 启用 R8 Full Mode + Baseline Profiles 启动提速 20%-60%,ANR 减少
P0 规范线程使用,统一线程池管理 减少线程竞争和上下文切换
P1 主线程 IO 分离 + 异步初始化 减少主线程阻塞,提升流畅度
P1 锁粒度优化 + 无锁数据结构 减少线程等待,提升并发效率
P1 核心线程(渲染/主线程)绑定大核 提升关键路径执行速度
P2 CPU 闲置时预加载 提升页面切换/二次启动速度
P2 算法优化 + 对象池复用 减少指令数和 GC 压力

总结

Android CPU 优化的核心思路可以概括为:

  1. 先观测:用 Perfetto / CPU Profiler 定位瓶颈,找到热点函数和调度问题
  2. 再治理:从线程管理、锁优化、IO 分离、算法优化等层面减少 CPU 负担
  3. 深调度:通过优先级调整和绑核,让关键任务跑在最佳资源上
  4. 前置化:利用 R8、Baseline Profiles 在编译期就获得性能收益
  5. 防退化:用 Macrobenchmark 做 CI 回归,用 Android Vitals 监控线上质量

CPU 优化不是一次性工作,而是需要融入日常开发流程的持续实践。

相关推荐
见山是山-见水是水1 小时前
网络请求性能优化落地指南:让原生界面在模拟器里稳定运行
网络协议·华为·性能优化·harmonyos
石头猫灯2 小时前
一次 CMS 被黑实录:完整事件思路梳理
android·学习
zhonyu鱼2 小时前
Escrcpy:用电脑键盘鼠标操作安卓手机的跨平台投屏工具
android·计算机外设·电脑
千里马学框架2 小时前
安卓开机性能优化:如何安全高效地裁剪 SystemService
android·智能手机·framework·wms·手机·性能·车载
_ZHOURUI_H_2 小时前
Unity EasyECS:并不是所有字段都适合 SoA,Unity 项目中应该怎样拆数据
游戏·unity·性能优化·架构·游戏引擎
Android打工仔2 小时前
一次 Android 拍照后卡顿的 Perfetto 定位与优化实践
android·性能优化
OpenFDE开源桌面2 小时前
实操:如何将安卓 App与Linux系统应用级融合(附代码)
android·linux
xiangxiongfly9152 小时前
Android VideoView总结
android·videoview
杉氧3 小时前
从 Modifier 到 Flexbox:React Native 布局与样式设计哲学
android·前端·react native