简介《android性能优化》是一份面向 Android 应用开发、系统调优与测试人员的专题文档重点讨论 PAD 和车机场景下的性能瓶颈与优化路径帮助读者从开机、交互到后台资源管理建立系统化排查思路。压缩包共 1 个 docx 文件大小约 10.32MB内容按开机速度、响应速度、内存管理、系统稳定性、数据传输、数据流量、存储容量和模块功耗八大方向组织并涉及桌面、相机等 14 类集成 APK 的冷热启动测试、锁屏解锁耗时统计、Benchmark 跑分、Monkey 与 CTS 兼容性验证以及 WIFI、蓝牙、SD 卡读写和 adb 调试命令等细节。已有 686 人学习下载适合需要形成性能测试清单、定位 ANR/Crash 与内存泄漏问题或为车机、PAD 项目制定专项优化方案的读者参考。1. 从两台设备的差异说起性能优化先划边界再谈工具多数人一提 Android 性能优化脑子里冒出来的是几条命令、几个虚拟机参数。真接手量产项目才会发现难的不是工具是边界一台插电工作的车机和一台靠电池跑的平板优化清单本来就不该是同一份。车机不靠电池供电功耗优先级自然往后排但对启动速度和长时间运行的稳定性要求比普通消费电子高一截平板的 BSP 是以 Bin 内嵌打包的形式交付模块级功耗基本无解只能拿不同 BSP 版本做对比确认是否达标再加上部分型号不支持电话短信移动数据流量这一块直接可以划掉只剩 WIFI 流量要管。想清楚哪些能做、哪些做不了剩下的工作才谈得上排期。这里要展开的是一套覆盖开机速度、响应速度、内存管理、系统稳定性、数据传输、数据流量、存储容量、模块功耗八个模块的完整优化方案适合正在做毕设、课设或需要交付一份系统级性能方案的人直接参考。2. 优化范围与指标口径八大模块的优先级划分与基线采集性能优化最容易翻车的地方不是技术难度是前面没把优化到什么程度算完成讲清楚。八个模块一起铺开每个都做一点最后哪个都拿不出数据。我一般的做法是先按设备形态裁剪范围再给每个模块定一个能被复现的量化口径最后把采集动作固化成脚本。这样后面无论谁接手拿到的都是同一套尺子量出来的数。2.1 车机与 PAD 的取舍先划掉做不了的事车机和平板虽然共用一套 Android 基线但优化侧重完全不同这一点必须在方案第一页就写死。车机由车辆供电续航不是问题所以模块功耗只做不劣化的守线不作为主攻方向反过来车机的启动时间和长时间运行的稳定性权重最高因为它一上车就要可用中途卡死或重启的代价远高于手机。平板这边BSP 以 Bin 内嵌方式打包意味着我们拿不到 BSP 内部模块的细粒度控制权模块功耗只能退化成版本间对比即刷不同 BSP 版本、在相同场景下记录电流曲线判断新版本是否比旧版本更差。至于不支持电话短信的型号移动数据流量这一整块直接不做只保留 WIFI 流量治理。这样裁剪完八个模块的优先级就出来了开机速度、响应速度、内存管理、系统稳定性是四个主攻方向数据传输、数据流量、存储容量是三块常规治理模块功耗在车机上守线、在平板上做版本对比。方案里把这层写清楚评审时就不会有人追问为什么功耗优化只做这么点。2.2 八大模块的量化口径与验收线口径不统一优化结果就没法对比。比如开机快了 3 秒这句话如果不说明是从按下电源键算起还是从 kernel 打印第一行算起换个人测就是另一个数。八个模块我通常按下面这套口径落地每一项都要求同一台机器、同一版本、同一场景、连续 5 次取中位数。模块量化指标采集方式参考验收线开机速度上电到桌面可点击秒logcat events 缓冲区时间戳相比基线版本降低 15% 以上响应速度14 个集成应用冷/热启动耗时毫秒am start -W 的 TotalTime冷启动中位数不超过 1200ms内存管理常驻进程 PSS 总和、单进程 PSS 增长趋势procrank -u / dumpsys meminfo8 小时压测 PSS 无单调上涨系统稳定性每千次操作 ANR 数、Crash 数Monkey 结果 dropbox 统计ANR 率低于 0.1%无必现 Crash数据传输WIFI 重连耗时秒、SD 卡顺序读写MB/s脚本计时 dd重连不超过 5s写入不低于基线数据流量待机 24 小时后台流量MB/proc/net/dev 差值待机零业务流量存储容量/data 可用空间、冗余文件占比du -sh 排序连续使用 7 天可用空间不塌陷模块功耗典型场景电流mA外部电流表 batteryinfo不高于对比 BSP 版本这张表的价值在于每个数字后面都跟着采集方式换个同事来测也能测出同一个结果。毕设或课设里如果只写提升了性能答辩时基本会被追问到答不上来把口径写在方案里等于提前把问题堵死。2.3 基线采集把 adb 命令体系固化成一套脚本采集动作一定要脚本化。手工敲命令一是慢二是每次敲的顺序不同抓下来的状态就不一致后面做对比全是玄学。Android 自带的 adb 体系足够覆盖绝大部分采集需求关键在于组合方式先用 wait-for-device 保证设备就绪再按构建信息 → 进程内存 → CPU → 日志 → 崩溃现场的顺序抓最后统一落到一个带时间戳的目录里。# baseline.sh一次抓全避免多次插拔设备导致状态漂移 set -e STAMP$(date %Y%m%d_%H%M%S) OUTbaseline/$STAMP mkdir -p $OUT adb wait-for-device # 阻塞直到设备可用避免抓到空数据 adb shell getprop ro.build.display.id $OUT/build.txt # 记录构建号对比时先对齐版本 adb shell procrank -u $OUT/procrank.txt # 各进程 VSS/RSS/PSS/USS含未映射部分 adb shell dumpsys meminfo $OUT/meminfo_all.txt # 全局内存明细 adb shell dumpsys cpuinfo $OUT/cpuinfo.txt # 各进程 CPU 占用占比 adb shell top -n 1 -m 10 $OUT/top10.txt # 只刷一次取占用最高的 10 个进程 adb shell logcat -v time -d *:W $OUT/logcat_w.txt # 只留 warning 及以上避免文件爆炸 adb pull /data/anr/ $OUT/anr/ 2/dev/null || true # ANR 现场可能不存在 adb pull /data/tombstones/ $OUT/tombstones/ 2/dev/null || true adb pull /data/system/dropbox/ $OUT/dropbox/ 2/dev/null || true echo collected - $OUT脚本里几个参数值得单独说。procrank -u会按进程独自占用的物理内存降序排列-u表示把未映射unmapped部分也算进去排查泄漏时这个视图比ps有用得多。logcat -d是取完即走不加-d会一直挂着刷屏脚本就卡住了。*:W是过滤级别只保留 warning 及以上日常采集如果全量抓单次文件能到几十兆对比时根本翻不动。top -n 1只刷新一次就返回适合脚本要看趋势就换vmstat -n 3每 3 秒一行观察进程、内存、IO 和 CPU 的变化。关于内存新手最容易在 VSS、RSS、PSS、USS 四个值上犯迷糊。VSS 是虚拟耗用内存包含共享库数字最大但参考价值最低RSS 是实际使用的物理内存同样包含共享库多个进程共享同一个库时会被重复计算PSS 按比例分摊共享库是横向比较进程内存时最该看的指标USS 是进程独自占用的物理内存不包含共享库判断单进程是否泄漏时最准。方案里统一约定横向比较看 PSS纵向看增长看 USSprocrank输出的就是这几个值。3. 开机速度与响应速度从 kernel 到桌面图标的启动链路裁剪开机慢这件事最怕上来就改代码。正确的顺序是先量出时间花在哪一段再决定砍什么。Android 的启动链路可以切成四段bootloader、kernel、init 与 zygote/system_server、桌面可交互。前半段靠日志时间戳就能定位后半段要靠 events 缓冲区里的 boot_progress 打点。量完之后你会发现真正吃时间的往往不是驱动本身而是一堆调试期留下的东西在量产版本里还开着。3.1 四段启动链路拆解与日志裁剪量化起点用 events 缓冲区它记录的是系统级事件比普通 logcat 干净得多。常用的几个打点是boot_progress_start进入 Android 用户空间、boot_progress_system_runsystem_server 启动、boot_progress_pms_start和boot_progress_pms_ready包管理扫描开始与完成、boot_progress_enable_screen可交互。把相邻两个时间戳一减就知道时间花在包扫描上还是花在服务启动上。# 抓一次开机过程的阶段打点按时间排序后人工比对区间 adb logcat -b events -d | grep -E boot_progress|sf_stop_bootanim裁剪日志是第一件要做的事也是性价比最高的一件。kernel 侧通过命令行参数把打印等级压下去同时在 defconfig 里关掉一批只为调试存在的选项init 侧去掉不必要的on boot动作和调试服务Android 框架侧关掉量产不需要的服务和日志开关。注意这里的取舍逻辑删的是不影响功能、只影响观测的东西任何涉及状态同步或异常上报的日志都要保留否则现场出问题时就成了黑匣子。# 量产 defconfig 裁剪示例保留 PRINTK 与 DYNAMIC_DEBUG关掉重调试项 CONFIG_PRINTKy CONFIG_DYNAMIC_DEBUGy # CONFIG_DEBUG_KMEMLEAK is not set # 内存泄漏检测仅调试版本开启 # CONFIG_DEBUG_SPINLOCK is not set # 自旋锁调试量产关掉 # CONFIG_DEBUG_HIGHMEM is not set # CONFIG_SERIAL_MSM_DEBUG is not set # 串口调试输出量产必须关配套的内核启动参数里加loglevel4只输出 warning 及以上。这一条单独看收益不大但它影响的是整条启动链路上所有的打印调用越靠前的阶段收益越明显。init.rc 里被删掉的服务通常包括各种调试桥、测试桩和只在工厂模式用的守护进程删之前建议先在开发机上注释掉跑一轮 Monkey确认没有隐式依赖再动手。至于驱动模块原则很简单lsmod或者 defconfig 里能对上的、确实没接外设的全部去掉。注意别把编译进内核的选项误当成模块删掉这两类在 defconfig 里的写法不同y与m改错了会直接起不来这类改动务必留后悔药——改一个提交一次每次都能单独回滚。3.2 preload-class 与预装 APK 的减负zygote 预加载的类列表是开机时间里的固定开销列表越长fork 之前花的时间越多。这份列表在框架代码里维护默认包含大量常用类。裁剪的思路是把列表导出对照项目实际会跑的应用把确认用不到的类剔除然后观察首次启动和二次启动的差异。这里有个容易忽略的点预加载类被砍之后首个使用到这些类的应用会变慢所以指标不能只看开机响应速度那一列也要同步回归。预装 APK 的影响更直接。开机时包管理器要扫描预装目录解析每个 APK 的清单、签名和资源APK 越多、单个越大扫描耗时越长。方案里的做法是先统计再决定删哪些。# 统计预装 APK 的分布作为裁剪依据 adb shell ls /system/app | wc -l # 系统分区预装数量 adb shell ls /data/app | wc -l # 数据分区预装数量 adb shell pm list packages -f | grep ^package:/system | wc -l adb shell du -sh /system/app/* | sort -rh | head -20 # 找出体积最大的前 20 个统计出来之后按两条线裁剪一是功能重叠的同一类功能装了多套二是项目里明确不会用的。车机上尤其要注意很多为了手机形态准备的应用在车机场景里根本不会启动留着纯属给包扫描加负担。删完之后一定要复测一次首次开机刷机后第一次启动此时所有缓存都是冷的和二次开机这两个数差得很多方案里必须分开写。首次开机慢通常是正常的但慢到用户能感知就是问题刷机后首次启动的加载时间优化主要靠减少预装和推迟非必要的初始化任务。3.3 14 个集成应用的冷热启动测量方案响应速度的核心是能不能稳定测出来。项目里锁定的是 14 个集成应用桌面、电话、联系人、短信、相机、相册、云邮箱、音乐、视频、应用中心、安全精灵、主题中心、设置、文件浏览器。选这 14 个的理由是它们都涉及大量数据的导入导出和频繁的数据交互属于最容易被用户感知到卡顿的场景。其余像闹钟、计算器、录音机、便签、日历、浏览器、输入法、天气以及第三方应用和游戏性能问题大多能被这 14 个覆盖不单独列为测量对象。测量手段有两套互为交叉验证。一套是高速摄像机逐帧看屏幕从点击到首帧出现的过程优点是完全符合用户真实感知缺点是成本高、逐帧分析慢一般只在版本对比的关键节点用。另一套是代码内置测量用am start -W直接拿到启动耗时日常回归用它。# 冷启动测量先 force-stop 清掉进程再启动并计时 for PKG in com.demo.launcher com.demo.dialer com.demo.contacts; do adb shell am force-stop $PKG # 冷启动前提进程不在 adb shell am start -W -n $PKG/.MainActivity | \ grep -E TotalTime|WaitTime | sed s/^/$PKG / sleep 2 # 给系统一点时间回收避免连续测量互相干扰 done-W会等待启动完成并输出三个值TotalTime是应用自身从创建到首帧绘制完成的时间WaitTime是从发出命令到拿到结果的总时长Status表示是否超时。做横向对比时用TotalTime因为它剔除了 am 命令本身的开销。热启动就是把force-stop去掉此时进程还在测的是回到前台的时间。两个数必须分开记录冷热差得巨大是正常现象把它们混在一起算平均是没有意义的。测量里有几个必须固定下来的变量同一台机器、同一版本、同一亮度、同一后台负载、连续 5 次取中位数。后台负载尤其容易被忽略如果测量时后台正在跑同步或扫描数据会明显偏高。我习惯在测量前统一执行一次清理把非系统进程全部杀掉再开始跑。3.4 锁屏解锁链路与功耗锁释放锁屏和解锁这两个动作经常被漏掉但它们其实是一次完整的性能事件期间要播放解锁音效、加载或解除功耗锁、刷新壁纸和状态栏任何一环卡住都会让用户觉得这机器反应慢。测量方式是在代码里打点记录Keyguard解除开始到桌面真正可交互的时间戳再配合高速摄像机抽查几次确认打点位置没有偏差。# 解锁过程相关状态与 wakelock 的观测 adb shell dumpsys window | grep -i keyguard # 当前锁屏状态 adb shell dumpsys power | grep -i wake # 当前持有的 wakelock 列表优化方向主要有两个。一是音效资源锁屏解锁音如果是一段较长的音频文件解码和播放就会挤占这段时间替换成更短的音频或者改为异步播放都能缓解。二是功耗锁的申请与释放时机很多实现是在解锁瞬间同步申请一批 wakelock再在某个超时后统一释放这个同步申请的过程会阻塞主线程。改成按需申请、释放走异步解锁时间的改善通常能到百毫秒级。这部分改动风险不低涉及电源管理改完必须跑一轮完整的待机功耗对比确认没有引入额外的耗电。4. 内存管理与系统稳定性从 meminfo 水位到 tombstone 符号化内存和稳定性这两块要一起看因为大量稳定性问题的根因就是内存。Android 给每个应用进程分配了堆上限常见是 16MB 到 24MB一旦某个进程因为泄漏涨过这条线系统就会把它杀掉如果出问题的是 system_server 这类系统进程结果就是整机重启。所以内存管理的目标不是用得越少越好而是该释放的能释放、不该涨的不涨。4.1 meminfo 字段怎么读剩余内存到底看哪几个数/proc/meminfo一屏几十个字段第一次看很容易懵。实际判断系统还剩多少可用内存看的是MemFree Cached而不是只看MemFree。原因是 Cached 里的页在被回收之前充当文件缓存一旦有内存压力就能立刻释放出来本质上属于随时可用的内存。设置里的运行中页签显示的数字还要再减掉一个固定预留值那个值是为二级服务预留的属于系统内部约定方案里统一按MemFree Cached口径对账避免前后对不上。adb shell cat /proc/meminfo | grep -E MemTotal|MemFree|Buffers|Cached|SwapTotal|Slab|Shmem几个关键字段的读法MemTotal是可用 RAM 总量已经减掉了内核代码和预留位Buffers是块设备缓冲只记文件系统的元数据Cached是文件缓存也是回收时最先被释放的部分Slab是内核数据结构缓存如果这个值持续上涨而业务没有变化多半是驱动里有对象没释放SwapTotal在多数移动设备上是 0看到非零要确认是否开了交换分区开了之后内存表现会完全不同测试结论不能直接套用。低内存设备上还要关注LowTotal/LowFree和HighTotal/HighFree低位内存是内核自己也要用的区域LowFree 见底比总可用内存见底更危险。4.2 内存泄漏与 OOM 的定位路径先纠正一个常见误解Java 有垃圾回收不代表没有泄漏。只要还有引用指向一个已经不再使用的对象回收器就动不了它这块内存就一直被占着。Android 的每个应用跑在自己的 Dalvik 虚拟机实例里由 zygote 孵化堆上限固定泄漏累积到超过上限就会抛 OOM 或者被系统直接杀掉。好在影响范围限于自身进程不至于拖垮整机——除非出问题的是系统进程。相比 C/C 那种进程退出都不释放的泄漏Java 层的可控性要好得多。定位泄漏的标准路径是看趋势不看快照。单次dumpsys meminfo只能告诉你现在占多少看不出是不是在漏。# 每 30 秒采一次目标进程的 PSS连续采 20 次观察是否单调上涨 for i in $(seq 1 20); do adb shell dumpsys meminfo com.demo.browser | grep -E TOTAL|Java Heap|Native Heap \ | sed s/^/$(date %H:%M:%S) / sleep 30 done判读方法Java Heap持续上涨而Native Heap平稳通常是 Java 层持有引用没放重点查静态集合、单例里的 Context、未反注册的监听器和回调Native Heap上涨则要往 Bitmap、音视频解码器、JNI 层的NewGlobalRef上找。TOTAL里的 PSS 值配合procrank -u的排序能快速锁定是哪个进程在涨。确认了嫌疑进程之后再抓堆转储做对象引用链分析这一步比较重不建议一上来就做。除了泄漏空指针这类低级问题也要纳入检查范围。它们的共同特征是偶发、难复现、日志里只有一行堆栈所以方案里要求所有空指针和 Crash 都必须能从 dropbox 或 tombstone 里找到完整现场找不到现场的先补日志再谈修复。4.3 ANR 与 Crash日志抓取与符号化还原ANR 的现场在/data/anr/下Crash 的现场在/data/tombstones/下跑分和性能异常的现场在/data/system/dropbox/下。三个目录都要在采集脚本里带上出问题时第一时间抓晚了可能被新的事件覆盖。ANR 日志里最关键的是主线程的堆栈看它卡在锁等待、IPC 调用还是磁盘 IO 上基本就能定位方向。Native 崩溃的堆栈默认是一串地址像libxxx.so 0x00012345直接看等于看天书必须符号化。工具链里的三个命令要会用addr2line把地址翻译成文件名和行号nm列出符号表objdump反汇编看具体指令。# 用带符号的 so 还原崩溃地址对应的源码位置 arm-eabi-addr2line -f -C -e out/target/product/demo/symbols/system/lib/libdemo.so 0x00012345 # 导出符号表便于人工检索可疑函数 arm-eabi-nm -l -C -n -S libdvm.so dvm.data # 反汇编指定库排查指令级问题 arm-eabi-objdump -C -d libc.so libc.s-f输出函数名-C做 C 符号还原不加这个参数看到的是被修饰过的名字-e指定要解析的库文件。这里有个前提必须用和出问题版本完全对应的带符号库版本对不上解析出来的位置是错的这一点吃过亏的话印象会很深。所以方案里要求每次发版都把符号表一起归档命名带上构建号别等出事了再回头找。4.4 Monkey 与 CTS把稳定性做成可重复的门禁稳定性不能靠用着感觉还行来判断得有可重复的门禁。Monkey 负责随机压力CTS 负责兼容性基线两个都要跑而且都要有明确的通过标准。# 随机压力测试固定 seed 保证可复现忽略超时和崩溃继续跑跑满 8 小时 adb shell monkey -s 20240101 -v -p com.demo.launcher \ --throttle 300 --ignore-timeouts --ignore-crashes --pct-touch 40 \ --pct-motion 20 --pct-nav 20 --pct-appswitch 20 28800-s固定随机种子这一点非常关键不固定种子出了问题没法复现等于白跑。--throttle控制事件间隔太小会被人为放大成性能问题太大会跑不完300 毫秒是个常用起点。--pct-*调整各类事件的比例车机场景应该提高appswitch的权重因为用户会频繁在应用间切换。跑的过程中要同步监控 logcat一旦出现 ANR 或 Crash 立刻记录时间点和包名跑完汇总成每千次操作的异常数这个数比跑了多久没崩有意义得多。CTS 这块的重点不在跑通而在跑不通的时候怎么收。常见失败分三类一是系统接口签名或行为与基线不一致多由框架层改动引起二是权限与安全策略变更导致的用例失败三是第三方应用兼容性问题表现为同一个应用在不同版本上表现不一致。前两类改代码第三类要建立兼容性清单把验证过的第三方应用版本固定下来后续每次发版回归。数据区的及时清理和进程归并也放在这一块做常驻进程太多会同时拉高内存和稳定性风险归并策略要结合业务实际不能一刀切。5. 传输、流量、存储与模块功耗外围四块的落地与常见问题排查前面四块是主攻方向剩下的四块属于不做会挨骂、做过头没收益的类型。这一章的重点不是把每个指标都压到极致而是给出可执行的测量方法和治理手段让这四块有据可依、不拖后腿。5.1 WIFI、蓝牙与 SD 卡怎么把速度测准传输速度的测量最大的坑是测的不是想测的东西。WIFI 这块要分两个指标连接速度和 AP 切换重连速度。前者是首次连上 AP 的耗时后者是从一个 AP 漫游到另一个 AP 的耗时后者对车机尤其重要因为车辆移动时 AP 会不断变化。测量方式是脚本记录状态机切换的时间戳而不是肉眼看图标。蓝牙要测配对连接速度和传输速度配对慢通常是协议栈的扫描间隔设置问题传输慢则要区分是蓝牙版本限制还是应用层分片策略问题。SD 卡这一块分内置和外置测的是顺序读写速度用 dd 就够了。# 外置 SD 卡顺序写入测速256MBbs1Mconvfsync 确保真正落盘 adb shell dd if/dev/zero of/mnt/sdcard/test.bin bs1M count256 convfsync # 顺序读取测速先清缓存再读避免命中页缓存导致数字虚高 adb shell sync; echo 3 /proc/sys/vm/drop_caches adb shell dd if/mnt/sdcard/test.bin of/dev/null bs1Mconvfsync这个参数不能省不加的话 dd 只是把数据写进页缓存就返回了测出来的是内存速度不是存储速度。读取前清缓存同理不清的话第二次读全命中缓存数字会好看得离谱。测完记得把测试文件删掉不然它会一直占着存储空间后面测存储容量时又会来干扰。云同步速度的测量更接近端到端体验建议按发起同步到数据可用的完整链路计时而不是只测网络层吞吐。5.2 数据流量治理谁在后台偷跑流量治理的目标很明确防止应用在后台偷跑流量。手段分两层一层是统计一层是限制。统计可以直接读网卡计数器的差值简单可靠限制则要靠系统层的联网策略把后台联网权限收起来非前台应用不允许随意发起网络请求。# 读取网卡累计收发字节前后两次相减即为该时段流量 adb shell cat /proc/net/dev | grep -E wlan0|rmnetwlan0是 WIFI 接口rmnet系列是移动数据接口不支持电话短信的型号上通常不存在或不激活。做待机流量测试时要求设备静置 24 小时前后两次取差值正常情况下应该接近零。如果发现明显增长用netstat -ano看当前连接再结合进程列表定位到具体应用。方案里还要求应用退出时必须释放网络连接长连接要有心跳上限避免为了保活而持续发包。这部分不需要复杂技术需要的是把规则写进验收清单每次发版都过一遍。5.3 存储容量清理策略要写在系统里不能靠人存储容量的问题往往不是空间不够而是该删的没删。日志、缓存、下载临时文件、崩溃转储这四类是最常见的空间黑洞。方案里的做法是先按目录统计占用排出 Top 项再针对每一项定清理策略——日志按大小滚动、缓存按时间过期、临时文件在任务结束后立即删除、崩溃转储在采集上报后删除。# 按占用从大到小排出 /data 下的目录定位空间黑洞 adb shell du -sh /data/* 2/dev/null | sort -rh | head -15清理动作要由系统定时触发不能靠用户手动点。可以挂在设备空闲时执行避免在用户使用过程中做大量删除引起卡顿。删除策略要有上限保护比如缓存目录最多占多少、日志最多留几天超了才删不超就不动这样既控制了空间又不会频繁 IO。外置 SD 卡的冗余数据清理同理但要额外注意别误删用户自己的文件只清理应用自己创建的目录。5.4 模块功耗能做多少做多少做不了就守住对比基线车机上功耗不是主攻方向只要不劣化即可平板上受限于 BSP 的打包方式能做的主要在应用层和系统层。可下手的地方有四个音视频编解码尽量走硬件通路避免软解带来的持续高负载背光亮度曲线做平滑过渡避免频繁大幅跳变LCD 和触摸屏的刷新频率按场景调整静态界面降频modem 模块在无信号或弱信号场景下的搜网策略要收敛避免长时间高功率搜网。# 观察功耗相关状态电池信息、唤醒锁、各服务耗电统计 adb shell dumpsys battery adb shell dumpsys batteryinfo | head -50 adb shell dumpsys power | grep -i wake lockdumpsys batteryinfo输出的是各功能模块的耗电统计适合做粗略排序找出耗电大户dumpsys power里的 wakelock 列表能看出谁在阻止系统休眠长时间持有的 wakelock 是待机耗电的头号嫌疑。对平板这类无法深入 BSP 的设备最终交付的是一份版本对比结论相同场景、相同脚本、不同 BSP 版本各测三次取均值判断新版本是否达标。这个结论虽然不产生优化动作但它是验收的一部分必须有。5.5 四块里的常见问题排查现象、原因与解决外围四块看起来简单实际做的时候翻车点不少下面几条是最常遇到的。现象一SD 卡测速结果忽高忽低同一台机器两次能差一倍。原因多半是测量前没有统一缓存状态第一次读命中了页缓存第二次没有也可能是有后台任务在同时读写。解决办法是固定测量流程先drop_caches再立即测量连续 5 次去掉最高最低取中间值测量期间杀掉非系统进程。现象二待机流量统计出来是负数或者明显偏小。原因是网卡计数器在接口重启时会归零两次采样之间如果发生过 WIFI 重连差值就不可信了。解决办法是采样前先记录接口状态发现重连就重新开始计时或者改用系统自带的分应用流量统计做交叉验证。现象三清理脚本跑完之后可用空间没变。原因是删除的文件还被进程持有句柄空间要等进程退出才真正释放du统计的是目录项而不是实际可回收空间。解决办法是清理后主动触发一次垃圾回收或重启相关服务并在清理前后各记录一次df而不是du。现象四功耗对比结论和上一轮完全相反。原因是测试环境变了——屏幕亮度、后台应用、信号强度任意一项不同电流曲线就会漂。解决办法是把测试场景写成脚本每次执行前自动设置亮度、清理后台、记录信号强度环境不一致的数据直接作废。现象五WIFI 重连时间测得很好用户体验依然很差。原因是测量只覆盖了链路层重连没算应用层重新建连和首包往返的时间。解决办法是把测量终点从关联成功改为首个业务请求返回成功虽然数字会变难看但那个数才是用户感知到的。6. 验收与答辩把优化结果讲成一条能复现的曲线验收这件事我见过最常见的失败方式是把一堆优化前 2800ms、优化后 1900ms的数字堆在 PPT 上然后被问一句这个数怎么测的就哑了。真正能站得住的验收靠的是三样东西统一的测量口径、可复现的采集脚本、版本间的对比曲线。口径在前面已经定好脚本在基线采集那一步已经写完验收环节要做的只是把它们跑一遍把结果画成图。答辩或评审时的讲述顺序我一般按这个来先讲设备形态决定了哪些模块做、哪些不做这一段能体现判断力再讲每个模块的量化口径和采集方式这一段能体现严谨然后挑两到三个真正有改善的模块展示改动前后的对比曲线注意是曲线不是单点一条随版本变化的趋势线比一个漂亮的单点有说服力得多最后讲没做好的部分和原因比如平板上的 BSP 功耗只能做版本对比、某项指标受硬件限制无法继续压。主动交代边界比被人问出来强。验收项交付物判定方式开机速度四段链路时间戳表 首/二次开机对比相比基线降低 15% 以上响应速度14 个应用的冷/热启动中位数表冷启动中位数不超过 1200ms内存管理8 小时 PSS 趋势曲线无单调上涨无 OOM系统稳定性Monkey 报告 ANR/Crash 统计ANR 率低于 0.1%传输与流量重连耗时表 24 小时待机流量重连不超过 5s待机零业务流量存储与功耗空间占用 Top 表 版本对比结论7 天不塌陷不劣于对比版本表格里每一项都对应一个具体文件评审时随时能调出来看这比口头描述有力得多。如果时间允许现场跑一次采集脚本是最有杀伤力的——设备接上、脚本一跑、数据出来所有的质疑都会自动消失。还有个细节值得提所有对比数据必须来自同一台设备。拿 A 机器的优化前和 B 机器的优化后做对比是这类方案里最隐蔽也最致命的错误硬件批次不同、闪存颗粒不同差异可能比优化本身还大。我现在的习惯是每次验收前先把测试设备的构建号、闪存型号、电池状态全部记一遍跑完再核对一次确认中途没被刷过机。从那以后我每次提交性能报告都强制走一遍同机、同版本、同场景、连续五次取中位数的流程宁可数据难看一点也不要一个经不起追问的漂亮数字。希望帮到你。本文还有配套的精品资源点击获取