一.启动速度优化.
1.android启动优化
(1).非首屏必须的SDK(友盟,推送,分享),延迟到首页加载完成或空闲时初始化.
public class MyApplication extends Application {
MessageQueue mainQueue = Looper.getMainLooper().getQueue();
mainQueue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 3. 执行延迟初始化任务(非核心 SDK)
initThirdPartySDKs();
// 4. 返回 false:执行一次后自动移除
// 返回 true:每次空闲都会执行(一般不推荐)
return false;
}
});
}
(2).创建一个baselineProfile 模块,BaselineProfileGenerator.generate定义核心的用户旅程(应用里最高频、最关键的几条路径),它本质上是在模拟真实用户在应用中最常用、最重要的操作路径。ART 运行时(Android Runtime)会根据这些路径,在应用安装时预编译相关代码,从而提升启动和渲染性能。
- 定义核心的用户旅程,如下:
@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()
@Test
fun generate() {
// The application id for the running build variant is read from the instrumentation arguments.
rule.collect(
packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
?: throw Exception("targetAppId not passed as instrumentation runner arg"),
// See: https://d.android.com/topic/performance/baselineprofiles/dex-layout-optimizations
includeInStartupProfile = true
) {
// This block defines the app's critical user journey. Here we are interested in
// optimizing for app startup. But you can also navigate and scroll through your most important UI.
// Start default activity for your app
pressHome()
// 1. 等待冷启动完成,显示首帧
startActivityAndWait()
// 2. 模拟用户核心操作,比如点击登录按钮
device.findObject(By.text("登录")).click()
// 3. 等待登录完成
device.waitForIdle()
// 2. 等待主页加载完成
device.waitForIdle()
// 3. 模拟搜索操作
device.findObject(By.res("com.your.app.package:id/search_box")).click()
device.findObject(By.res("com.your.app.package:id/search_input"))
.setText("手机")
device.findObject(By.text("搜索")).click()
device.waitForIdle()
// 4. 等待搜索结果列表出现,并滚动浏览
val searchResultList = device.findObject(By.res("com.your.app.package:id/result_list"))
searchResultList.fling(Direction.DOWN) // 快速滑动
device.waitForIdle()
searchResultList.swipe(Direction.DOWN, 0.5f) // 慢速滑动[reference:20]
// 5. 点击进入第一个商品详情页
device.findObject(By.text("商品名称")).click()
device.waitForIdle()
// TODO Write more interactions to optimize advanced journeys of your app.
// For example:
// 1. Wait until the content is asynchronously loaded
// 2. Scroll the feed content
// 3. Navigate to detail screen
// Check UiAutomator documentation for more information how to interact with the app.
// https://d.android.com/training/testing/other-components/ui-automator
}
}
}
- 在 Android Studio 的 Run Configuration 中,选择并运行
generateBaselineProfile这个任务。生成成功后,你会在app/src/main/目录下看到baseline-prof.txt文件。它会随着应用一起打包,在安装时指导系统进行 AOT 预编译。
2.验证启动优化
(1).通过Adb命令查看界面启动时间
① 命令格式 : adb shell am start -W 包名/完整 Activity 类名 ;
② 本次命令 : adb shell am start -W kim.hsl.rtmp/kim.hsl.rtmp.MainActivity , 其中 kim.hsl.rtmp 是包名 , kim.hsl.rtmp.MainActivity 是完整的类名
冷启动时间:
热启动时间:
二.内存优化.
1.内存泄漏检测与修复 使用 LeakCanary 自动检测 Activity/Fragment/ViewModel 泄漏;常见泄漏场景:静态持有 Context、未取消注册监听、Handler 匿名内部类、单例持有 Activity 引用
2.资源对象未关闭引起的内存泄露.
Cursor,Socket在Activity销毁的时候,没有关闭。
EventBus或者广播在Activity销毁的时候,没有注销。
Handler发送延时消息导致的内存泄漏
Handler是通过内部类来创建的,内部类持有外部类Activity的引用,也就是说Handler持有Activity的引用,当通过Handler发送延时Message的时候,因为Message中target就是Handler对象,也就是Message持有handler引用,而handle持有Activity引用,所以当我们关闭Activity的时候,activity的引用也是没有办法被回收。
3.onDraw内部创建对象,做耗时操作导致内存抖动,泄漏
4.大图导致的内存OOM
使用 Glide/Coil 等图片库自动管理缓存;根据设备屏幕尺寸加载适当分辨率的图片;优先使用 WebP 格式,或者使用熊猫压缩.
验证方法
- Android Studio Memory Profiler:实时监控内存曲线,拍摄 Heap Dump 分析
- LeakCanary:自动检测并报告内存泄漏
- MAT (Memory Analyzer Tool) :深度分析 Heap Dump,生成 Leak Suspects 报告
验证步骤:
- 使用 Memory Profiler 记录页面进入 → 操作 → 退出后的内存曲线
- 反复进出页面多次,观察内存是否回落至初始水平
- 拍摄 Heap Dump,检查是否存在不应该存活的对象
- 对比优化前后的内存峰值和GC 频率 前后对比示例:
优化前:反复进出页面 10 次,内存增长 15MB → 优化后:增长 < 2MB,无泄漏
三.UI渲染优化.
1.减少布局层级 使用 ConstraintLayout 替代多层嵌套的 LinearLayout/RelativeLayout
2.使用merge标签
在 include 布局时使用 <merge> 减少多余根布局
3.ViewStub 延迟加载
对非首屏或条件性显示的布局使用 ViewStub 懒加载
验证方法
工具:
-
GPU 过度绘制调试:开发者选项 → “调试 GPU 过度绘制” → “显示过度绘制区域”
- 蓝色:1 次(理想)| 绿色:2 次(可接受)| 粉色:3 次(需优化)| 红色:4+ 次(严重)
-
GPU 渲染模式分析:开发者选项中开启,观察每帧耗时直方图
验证步骤:
- 开启 GPU 过度绘制,截图记录优化前的颜色分布
- 使用 Layout Inspector 统计最大布局深度
- 优化后重复上述操作,对比过度绘制区域减少和层级降低
前后对比示例:
优化前:布局深度 7 层,大量红色过度绘制区域 → 优化后:深度 3 层,仅蓝绿色区域
四.APK体积优化
1.资源裁剪
启用 shrinkResources true,自动移除未引用的资源
2.图片格式优化
如果是png或者jpg可以熊猫压缩或者替换成VecotrDrawable或者.webp格式
3.ABI 过滤(so库过滤)
根据目标设备过滤不需要的 ABI(如只保留 arm64-v8a)
4.lint移除无用的资源,和废弃代码
验证方法
工具:
- APK Analyzer:Android Studio 内置,查看 APK/AAB 各目录和文件占比
- 命令行:
unzip -l app-release.apk | sort -nr | head -40快速定位大文件
验证步骤:
- 使用 APK Analyzer 导出优化前的体积报告
- 记录 APK、AAB、各 ABI 包、dex、res、lib 的大小
- 实施优化后导出新报告,逐项对比
前后对比示例:
优化前:APK 大小 48MB → 优化后:18MB(AAB 分发后用户下载约 12MB)
五. 卡顿优化
1.避免主线程耗时
文件 I/O、网络请求、数据库查询、复杂计算移到子线程
2.过度绘制治理 见第三节“布局渲染优化”
3.列表滑动优化
RecyclerView 复用 ViewHolder;避免在 onBindViewHolder 中做耗时操作;使用 DiffUtil 精准刷新
验证方法
工具:
- 开发者选项 → GPU 呈现模式分析:以条形图显示每帧耗时,超过绿线表示掉帧
- Choreographer:代码层监控帧率,注册 FrameCallback 计算每秒帧数
- Systrace / Perfetto:系统级 Trace,定位具体哪段代码导致主线程阻塞
- BlockCanary:检测主线程卡顿,定位卡顿时的方法调用栈
验证步骤:
- 在相同测试路径下(如滑动列表 10 次),记录优化前的平均 FPS 和掉帧次数
- 使用 Systrace 抓取 Trace,定位耗时最多的主线程操作
- 优化后重复测试,对比 FPS 和掉帧率
前后对比示例:
优化前:滑动列表平均 42fps,掉帧率 18% → 优化后:平均 58fps,掉帧率 2%