Files
mkdocs/docs/android面试/性能优化/启动优化.md
2026-01-15 11:53:37 +08:00

13 KiB
Raw Permalink Blame History

启动优化

目录


启动流程分析

应用启动类型

1. 冷启动Cold Start

  • 定义:系统进程中没有该应用的任何进程信息,需要创建新进程
  • 特点:启动时间最长,需要加载所有资源
  • 流程
    1. 启动进程
    2. 创建Application对象
    3. 启动主线程
    4. 创建主Activity
    5. 加载布局
    6. 执行onCreate
    7. 测量、布局、绘制
    8. 显示到屏幕

2. 热启动Hot Start

  • 定义应用进程还在只是Activity被销毁
  • 特点启动时间短只需恢复Activity
  • 流程
    1. 恢复Activity
    2. 调用onCreate、onStart、onResume
    3. 显示到屏幕

3. 温启动Warm Start

  • 定义应用进程存在但Activity需要重建
  • 特点:介于冷启动和热启动之间

启动时间线

进程创建 → Application创建 → Activity创建 → 布局加载 → 首帧绘制

启动时间测量

1. 使用 adb 命令测量

# 测量冷启动时间
adb shell am start -W -n com.example.app/.MainActivity

# 输出示例:
# ThisTime: 500ms      # 最后一个Activity启动耗时
# TotalTime: 800ms     # 所有Activity启动耗时
# WaitTime: 1000ms     # 系统启动应用耗时

2. 代码中测量

// Application onCreate
@Override
public void onCreate() {
    super.onCreate();
    long startTime = System.currentTimeMillis();
    
    // 初始化代码
    
    long endTime = System.currentTimeMillis();
    Log.d("Startup", "Application onCreate: " + (endTime - startTime) + "ms");
}

// Activity onCreate
@Override
protected void onCreate(Bundle savedInstanceState) {
    long startTime = System.currentTimeMillis();
    super.onCreate(savedInstanceState);
    
    // 初始化代码
    
    long endTime = System.currentTimeMillis();
    Log.d("Startup", "Activity onCreate: " + (endTime - startTime) + "ms");
}

3. 使用 TraceView

// 开始追踪
Debug.startMethodTracing("startup");

// 结束追踪
Debug.stopMethodTracing();

4. 使用 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
# 在开发者选项中启用系统追踪

冷启动优化

1. Application 优化

延迟初始化

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 必要初始化(立即执行)
        initCrashHandler();
        
        // 非必要初始化(延迟执行)
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            initThirdPartySDK();
        }, 100);
    }
}

异步初始化

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 使用线程池异步初始化
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(() -> {
            initHeavySDK();
        });
    }
}

使用 ContentProvider 初始化(不推荐)

// 不推荐ContentProvider 的 onCreate 在 Application onCreate 之前执行
// 会阻塞主线程
public class InitProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        // 初始化代码
        return true;
    }
}

2. Activity 优化

减少 onCreate 耗时

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // 1. 延迟非关键初始化
    new Handler().post(() -> {
        initNonCriticalComponents();
    });
    
    // 2. 使用 ViewStub 延迟加载
    ViewStub viewStub = findViewById(R.id.view_stub);
    viewStub.inflate(); // 需要时才加载
    
    // 3. 异步加载数据
    loadDataAsync();
}

使用启动窗口

<!-- styles.xml -->
<style name="LaunchTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@drawable/launch_screen</item>
    <item name="android:windowFullscreen">true</item>
</style>
<!-- AndroidManifest.xml -->
<activity
    android:name=".MainActivity"
    android:theme="@style/LaunchTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
// MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    setTheme(R.style.AppTheme); // 恢复正常主题
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

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_width="match_parent"
    android:layout_height="wrap_content"
    android:layout="@layout/heavy_layout" />
// 需要时才加载
ViewStub viewStub = findViewById(R.id.view_stub);
viewStub.inflate();

使用 Merge 标签

<!-- include_layout.xml -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView android:layout_width="wrap_content" />
    <Button android:layout_width="wrap_content" />
</merge>

4. 资源优化

减少资源大小

  • 使用 WebP 格式图片
  • 压缩图片资源
  • 移除未使用的资源
  • 使用矢量图SVG

延迟加载资源

// 使用 Glide 等图片加载库延迟加载
Glide.with(this)
    .load(url)
    .into(imageView);

5. 多进程优化

<!-- AndroidManifest.xml -->
<application>
    <!-- 主进程:只保留必要的组件 -->
    <activity android:name=".MainActivity" />
    
    <!-- 后台进程:处理耗时操作 -->
    <service
        android:name=".BackgroundService"
        android:process=":background" />
</application>

热启动优化

1. 避免重建 Activity

// 在 onSaveInstanceState 中保存状态
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("key", value);
}

// 在 onCreate 中恢复状态
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        String value = savedInstanceState.getString("key");
    }
}

2. 使用 ViewModel

public class MainViewModel extends ViewModel {
    private MutableLiveData<String> data = new MutableLiveData<>();
    
    public LiveData<String> getData() {
        return data;
    }
}

// Activity 中使用
public class MainActivity extends AppCompatActivity {
    private MainViewModel viewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        // ViewModel 在配置变更时不会重建
    }
}

3. 配置变更处理

<!-- AndroidManifest.xml -->
<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden" />
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // 手动处理配置变更,避免重建 Activity
}

启动优化方案

1. 启动任务管理

public class StartupTaskManager {
    private static final List<StartupTask> tasks = new ArrayList<>();
    
    public static void addTask(StartupTask task) {
        tasks.add(task);
    }
    
    public static void executeTasks() {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (StartupTask task : tasks) {
            if (task.runOnMainThread()) {
                task.run();
            } else {
                executor.execute(task::run);
            }
        }
    }
}

interface StartupTask {
    void run();
    boolean runOnMainThread();
    int priority(); // 优先级
}

2. 启动白屏优化

// 使用启动窗口主题
<style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@drawable/splash_background</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowNoTitle">true</item>
</style>

3. 预加载优化

// 在 Application 中预加载
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 预加载常用数据
        preloadData();
        
        // 预初始化 View
        preloadViews();
    }
    
    private void preloadData() {
        // 预加载数据到内存
    }
    
    private void preloadViews() {
        // 预创建 View 对象
    }
}

启动优化工具

1. Android Studio Profiler

  • CPU Profiler分析启动时的 CPU 使用
  • Memory Profiler分析内存分配
  • Network Profiler分析网络请求

2. Systrace/Perfetto

  • 系统级性能分析
  • 查看启动时间线
  • 分析各组件耗时

3. TraceView

  • 方法级性能分析
  • 查看方法调用栈
  • 分析耗时方法

4. 自定义工具

public class StartupTracker {
    private static final Map<String, Long> timings = new HashMap<>();
    
    public static void start(String tag) {
        timings.put(tag, System.currentTimeMillis());
    }
    
    public static void end(String tag) {
        Long startTime = timings.get(tag);
        if (startTime != null) {
            long duration = System.currentTimeMillis() - startTime;
            Log.d("Startup", tag + ": " + duration + "ms");
        }
    }
}

// 使用
StartupTracker.start("Application.onCreate");
// ... 代码 ...
StartupTracker.end("Application.onCreate");

面试常见问题

Q1: 如何测量应用的启动时间?

答案:

  1. 使用 adb shell am start -W 命令
  2. 在代码中使用 System.currentTimeMillis() 记录时间点
  3. 使用 TraceView 或 Systrace 工具
  4. 使用 Android Studio Profiler

Q2: 冷启动和热启动的区别?

答案:

  • 冷启动:系统进程中没有应用进程,需要创建新进程,耗时最长
  • 热启动:应用进程还在,只需恢复 Activity耗时最短
  • 温启动:应用进程存在,但 Activity 需要重建,耗时介于两者之间

Q3: Application onCreate 中应该做什么?

答案:

  • 只做必要的初始化
  • 非必要的初始化应该延迟或异步执行
  • 避免在主线程执行耗时操作
  • 避免初始化过多第三方 SDK

Q4: 如何优化启动速度?

答案:

  1. Application 优化:延迟初始化、异步初始化
  2. Activity 优化:减少 onCreate 耗时、使用启动窗口
  3. 布局优化:减少层级、使用 ViewStub、Merge 标签
  4. 资源优化:压缩资源、使用 WebP、移除未使用资源
  5. 多进程优化:将耗时操作放到后台进程

Q5: 启动白屏问题如何解决?

答案:

  1. 使用启动窗口主题Splash Theme
  2. 设置合适的 windowBackground
  3. 使用启动画面Splash Screen
  4. 尽快显示首帧内容

Q6: ViewStub 的作用是什么?

答案:

  • ViewStub 是一个轻量级的 View用于延迟加载布局
  • 只有在调用 inflate() 时才会加载实际布局
  • 可以减少初始布局的复杂度,提升启动速度

Q7: 如何避免 Activity 重建?

答案:

  1. 使用 ViewModel 保存数据
  2. 在 onSaveInstanceState 中保存状态
  3. 处理配置变更configChanges
  4. 使用 Fragment 保存状态

Q8: 启动优化的最佳实践?

答案:

  1. 测量启动时间,找出瓶颈
  2. 延迟非关键初始化
  3. 异步执行耗时操作
  4. 优化布局层级
  5. 使用启动窗口
  6. 减少资源大小
  7. 使用多进程分离耗时操作

总结

启动优化是 Android 性能优化的重要部分,主要从以下几个方面入手:

  1. Application 优化:延迟初始化、异步初始化
  2. Activity 优化:减少 onCreate 耗时、使用启动窗口
  3. 布局优化:减少层级、使用 ViewStub
  4. 资源优化:压缩资源、延迟加载
  5. 架构优化:使用多进程、任务管理

通过合理的优化,可以将冷启动时间从 2-3 秒降低到 1 秒以内。


最后更新2024年