9.4 KiB
9.4 KiB
启动优化方法论
概述
应用启动速度直接影响用户体验,是性能优化的重要指标。本文档系统介绍Android应用启动优化的方法论、测量工具和优化策略。
启动类型
1. 冷启动(Cold Start)
定义: 应用进程不存在,系统需要创建新进程并初始化应用。
特点:
- 耗时最长
- 需要加载所有资源
- 创建Application和主Activity
时间范围: 通常2-5秒
2. 温启动(Warm Start)
定义: 应用进程存在但Activity被销毁,需要重新创建Activity。
特点:
- 进程已存在
- 只需创建Activity
- 比冷启动快
时间范围: 通常1-3秒
3. 热启动(Hot Start)
定义: 应用进程和Activity都存在,只需将Activity带到前台。
特点:
- 最快
- 只需恢复UI状态
- 几乎不需要初始化
时间范围: 通常< 500ms
启动时间测量
1. adb命令测量
# 测量启动时间
adb shell am start -W -n com.example.app/.MainActivity
# 输出示例:
# Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.app/.MainActivity }
# Status: ok
# Activity: com.example.app/.MainActivity
# ThisTime: 500
# TotalTime: 500
# WaitTime: 520
指标说明:
- ThisTime: 最后一个Activity启动耗时
- TotalTime: 所有Activity启动耗时
- WaitTime: AMS启动Activity总耗时
2. 代码埋点测量
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
long startTime = System.currentTimeMillis();
super.onCreate(savedInstanceState);
// 初始化工作
long endTime = System.currentTimeMillis();
Log.d("Startup", "onCreate耗时: " + (endTime - startTime) + "ms");
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// 首帧渲染完成
long renderTime = System.currentTimeMillis() - getIntent().getLongExtra("start_time", 0);
Log.d("Startup", "首帧渲染耗时: " + renderTime + "ms");
}
}
}
3. Systrace/Perfetto分析
# 使用Systrace
python systrace.py -t 10 -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
# 使用Perfetto
# 通过Android Studio的Profiler录制
关键指标:
- Application.onCreate: Application初始化时间
- Activity.onCreate: Activity创建时间
- inflate: 布局解析时间
- measure/layout/draw: 视图测量、布局、绘制时间
4. 首屏渲染时间(TTI)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 监听首屏渲染
View rootView = findViewById(android.R.id.content);
rootView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
// 首屏已渲染
logTTI();
return true;
}
}
);
}
}
启动优化策略
1. Application优化
延迟初始化
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 立即初始化:必须的、轻量的
initCrashHandler();
// 延迟初始化:非关键的、耗时的
initInBackground();
}
private void initInBackground() {
new Thread(() -> {
// 在后台线程初始化
initHeavyLibrary();
initThirdPartySDK();
}).start();
}
}
使用启动器框架
// 使用Startup库管理初始化任务
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
AppStartup.initializeComponent(this, MyStartupInitializer.class);
}
}
2. 主线程优化
减少主线程工作
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 避免在主线程做耗时操作
// ❌ 错误:同步网络请求
// String data = networkRequest();
// ✅ 正确:异步加载
loadDataAsync();
setContentView(R.layout.activity_main);
}
private void loadDataAsync() {
new Thread(() -> {
String data = networkRequest();
runOnUiThread(() -> {
updateUI(data);
});
}).start();
}
}
使用IdleHandler延迟非关键任务
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 主线程空闲时执行
initNonCriticalComponents();
return false; // false表示只执行一次
}
});
3. 布局优化
减少布局层级
<!-- ❌ 错误:嵌套过深 -->
<LinearLayout>
<LinearLayout>
<LinearLayout>
<TextView />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- ✅ 正确:使用ConstraintLayout扁平化 -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
使用ViewStub延迟加载
<!-- 延迟加载非首屏内容 -->
<ViewStub
android:id="@+id/view_stub"
android:layout="@layout/heavy_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
// 需要时再加载
ViewStub viewStub = findViewById(R.id.view_stub);
viewStub.inflate();
优化布局inflate
// 使用AsyncLayoutInflater异步加载布局
new AsyncLayoutInflater(this).inflate(
R.layout.activity_main,
null,
(view, resid, parent) -> {
setContentView(view);
// 布局已加载完成
}
);
4. 资源优化
减少启动时加载的资源
// 延迟加载大图片
// 使用占位图
ImageView imageView = findViewById(R.id.image);
imageView.setImageResource(R.drawable.placeholder);
// 异步加载
Glide.with(this)
.load(imageUrl)
.into(imageView);
使用资源预加载
// 在Application中预加载常用资源
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 预加载资源到内存
preloadResources();
}
private void preloadResources() {
new Thread(() -> {
// 预加载常用drawable
Resources res = getResources();
res.getDrawable(R.drawable.common_icon);
}).start();
}
}
5. 多进程优化
<!-- AndroidManifest.xml -->
<activity
android:name=".MainActivity"
android:process=":main" />
<service
android:name=".HeavyService"
android:process=":heavy" />
优点:
- 主进程启动更快
- 避免主进程OOM
- 隔离崩溃影响
缺点:
- 进程间通信开销
- 内存占用增加
6. 类加载优化
减少类数量
// 使用ProGuard/R8混淆和优化
// build.gradle
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}
使用MultiDex优化
// 对于方法数超过65535的应用
android {
defaultConfig {
multiDexEnabled true
}
}
dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
}
7. 启动画面优化
使用Splash Screen API(Android 12+)
// 使用新的Splash Screen API
// 提供更好的启动体验
传统启动画面
// 快速显示启动画面
// 避免白屏/黑屏
<style name="LaunchTheme" parent="Theme.AppCompat.Light">
<item name="android:windowBackground">@drawable/splash_background</item>
<item name="android:windowFullscreen">true</item>
</style>
启动优化检查清单
Application层
- 延迟非关键初始化
- 使用后台线程初始化耗时任务
- 避免在Application中做网络请求
- 使用启动器框架管理初始化顺序
Activity层
- 减少onCreate中的工作
- 异步加载数据
- 使用ViewStub延迟加载
- 优化布局层级
资源层
- 减少启动时加载的资源
- 使用占位图
- 延迟加载大图片
- 优化资源大小
代码层
- 减少类数量(使用ProGuard)
- 避免静态初始化块中的耗时操作
- 优化依赖库加载
性能指标
目标值
- 冷启动: < 2秒
- 温启动: < 1秒
- 热启动: < 500ms
- 首屏渲染: < 1秒
测量方法
- 使用adb命令测量
- 代码埋点测量
- Systrace/Perfetto分析
- 用户感知时间测量