Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > Android统计应用启动时间

Android统计应用启动时间的多种方法全解析

作者:时小雨

掌握启动时间优化是提升Android应用用户体验的关键一步,本文将全面解析启动时间统计的多种方法,从基础原理到高级优化技巧,助你打造秒开应用

一、启动时间统计的重要性

应用启动时间是用户对产品的第一印象。数据表明:

本文将深入探讨以下启动时间统计方法:

二、ADB命令测量:系统级启动时间分析

2.1 基础测量命令

# 冷启动测量(先停止应用)
adb shell am force-stop com.example.app
adb shell am start-activity -W -n com.example.app/.MainActivity

# 输出示例
Starting: Intent { cmp=com.example.app/.MainActivity }
Status: ok
LaunchState: COLD
Activity: com.example.app/.MainActivity
TotalTime: 856
WaitTime: 872
Complete

2.2 关键指标解析

指标说明优化价值
TotalTime应用自身启动总耗时核心优化指标
ThisTime当前Activity启动耗时关注特定页面优化
WaitTime系统调度总耗时受系统负载影响

2.3 自动化测量脚本

#!/bin/bash
package="com.example.app"
activity="com.example.app.MainActivity"
iterations=10

echo "开始冷启动测试($iterations次循环)..."
total=0

for ((i=1; i<=$iterations; i++))
do
  adb shell am force-stop $package
  sleep 1  # 确保应用完全停止
  
  # 获取TotalTime
  result=$(adb shell am start-activity -W -n $package/$activity | grep "TotalTime")
  time_ms=$(echo $result | cut -d' ' -f2)
  
  # 过滤无效结果
  if [[ $time_ms =~ ^[0-9]+$ ]]; then
    echo "第$i次: ${time_ms}ms"
    total=$((total + time_ms))
  else
    echo "第$i次: 测量失败"
    ((i--))  # 重试
  fi
done

average=$((total / iterations))
echo "--------------------------------"
echo "平均启动时间: ${average}ms"

2.4 热启动测量技巧

# 启动应用后返回桌面
adb shell input keyevent KEYCODE_HOME

# 再次启动(热启动)
adb shell am start-activity -W -n com.example.app/.MainActivity

三、代码埋点:精确到毫秒的内部监控

3.1 基础埋点方案(Kotlin实现)

Application类记录起点

class MyApp : Application() {
    companion object {
        var appStartTime: Long = 0
    }

    override fun onCreate() {
        super.onCreate()
        appStartTime = SystemClock.uptimeMillis()
    }
}

MainActivity记录终点

class MainActivity : AppCompatActivity() {
    
    override fun onResume() {
        super.onResume()
        val launchTime = SystemClock.uptimeMillis() - MyApp.appStartTime
        Log.d("LaunchTime", "冷启动耗时: ${launchTime}ms")
    }
}

3.2 进阶方案:使用reportFullyDrawn()

class MainActivity : AppCompatActivity() {

    override fun onStart() {
        super.onStart()
        
        // 当内容完全加载后调用
        window.decorView.post {
            reportFullyDrawn()
        }
    }
}

获取完全绘制时间

adb logcat -s ActivityManager | grep "Fully drawn"

3.3 分段统计启动时间

object LaunchTracker {
    const val TAG = "LaunchTracker"
    
    // 启动阶段定义
    var appCreateTime = 0L
    var activityCreateTime = 0L
    var windowFocusedTime = 0L
    var fullyDrawnTime = 0L
    
    fun logAppCreate() {
        appCreateTime = SystemClock.uptimeMillis()
    }
    
    fun logActivityCreate() {
        activityCreateTime = SystemClock.uptimeMillis()
        Log.d(TAG, "Application初始化耗时: ${activityCreateTime - appCreateTime}ms")
    }
    
    fun logWindowFocused() {
        windowFocusedTime = SystemClock.uptimeMillis()
        Log.d(TAG, "Activity创建耗时: ${windowFocusedTime - activityCreateTime}ms")
    }
    
    fun logFullyDrawn() {
        fullyDrawnTime = SystemClock.uptimeMillis()
        Log.d(TAG, "窗口焦点到完全绘制耗时: ${fullyDrawnTime - windowFocusedTime}ms")
        Log.d(TAG, "总启动耗时: ${fullyDrawnTime - appCreateTime}ms")
    }
}

// 在Application中
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        LaunchTracker.logAppCreate()
    }
}

// 在Activity中
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        LaunchTracker.logActivityCreate()
    }
    
    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) LaunchTracker.logWindowFocused()
    }
    
    // 在内容完全绘制后调用
    fun onContentDrawn() {
        LaunchTracker.logFullyDrawn()
    }
}

四、AppStartup:初始化阶段耗时监控

4.1 添加依赖

dependencies {
    implementation "androidx.startup:startup-runtime:1.2.0-alpha02"
}

4.2 实现Initializer监控初始化耗时

class AnalyticsInitializer : Initializer<Unit> {
    private val startTime = SystemClock.uptimeMillis()

    override fun create(context: Context) {
        // 模拟初始化工作
        Thread.sleep(50) 
        val cost = SystemClock.uptimeMillis() - startTime
        Log.d("AppStartup", "Analytics初始化耗时: ${cost}ms")
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        // 声明依赖关系
        return listOf(NetworkInitializer::class.java)
    }
}

class NetworkInitializer : Initializer<Unit> {
    private val startTime = SystemClock.uptimeMillis()

    override fun create(context: Context) {
        // 模拟网络库初始化
        Thread.sleep(80)
        val cost = SystemClock.uptimeMillis() - startTime
        Log.d("AppStartup", "Network初始化耗时: ${cost}ms")
    }

    override fun dependencies() = emptyList<Class<out Initializer<*>>>()
}

4.3 配置自动初始化

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false">
    
    <meta-data
        android:name="com.example.initializers.AnalyticsInitializer"
        android:value="androidx.startup" />
        
    <meta-data
        android:name="com.example.initializers.NetworkInitializer"
        android:value="androidx.startup" />
</provider>

4.4 手动初始化与延迟初始化

// 手动初始化组件
AppInitializer.getInstance(this)
    .initializeComponent(AnalyticsInitializer::class.java)

// 延迟初始化(在后台线程)
val executor = Executors.newSingleThreadExecutor()
executor.execute {
    AppInitializer.getInstance(this)
        .initializeComponent(NetworkInitializer::class.java)
}

五、启动时间优化策略

5.1 启动阶段优化策略

阶段耗时原因优化方案
应用创建ContentProvider初始化
Application.onCreate()
减少ContentProvider
异步初始化三方库
Activity创建布局复杂
数据加载
简化布局层级
懒加载非必要数据
界面绘制过度绘制
复杂渲染
减少透明视图
使用ViewStub延迟加载

5.2 代码优化示例

延迟初始化三方库

class MyApp : Application() {

    private val appExecutor by lazy { 
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) 
    }

    override fun onCreate() {
        super.onCreate()
        
        // 主线程必要初始化
        initCrashReporting()
        
        // 后台线程延迟初始化
        appExecutor.execute {
            initAnalytics()
            initPushService()
        }
    }
    
    private fun initCrashReporting() {
        // 必须立即初始化的组件
    }
    
    private fun initAnalytics() {
        // 三方分析库初始化
    }
    
    private fun initPushService() {
        // 推送服务初始化
    }
}

布局优化

<androidx.constraintlayout.widget.ConstraintLayout>
    
    <!-- 使用ViewStub延迟加载 -->
    <ViewStub
        android:id="@+id/stub_ads"
        android:layout="@layout/ads_banner"
        app:layout_constraintTop_toTopOf="parent" />
    
    <!-- 优先显示的核心内容 -->
    <TextView
        android:id="@+id/welcomeText"
        android:text="欢迎使用应用"
        ... />
    
    <!-- 使用占位控件 -->
    <include layout="@layout/placeholder_footer" />

</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
    
    override fun onStart() {
        super.onStart()
        
        // 延迟加载非必要视图
        Handler(Looper.getMainLooper()).postDelayed({
            val stub = findViewById<ViewStub>(R.id.stub_ads)
            stub?.inflate()
        }, 1000)
    }
}

六、技术对比与选型指南

6.1 启动时间统计方法对比

方法精度使用场景优势局限
ADB命令系统级自动化测试
竞品分析
无需修改代码
反映系统真实时间
无法区分内部阶段
代码埋点毫秒级开发期优化
关键路径监控
精确分段统计
可集成到监控系统
需要代码侵入
reportFullyDrawn用户感知用户体验优化最接近真实体验
官方推荐方案
需要API 19+
AppStartup组件级初始化优化依赖管理
延迟初始化
仅覆盖初始化阶段

6.2 性能优化关键指标

冷启动目标:< 1.5秒

热启动目标:< 1秒

初始化耗时:< 500ms

首屏渲染:< 700ms

6.3 优化效果评估流程

graph TD
    A[测量基线数据] --> B[识别瓶颈阶段]
    B --> C[实施优化策略]
    C --> D[验证优化效果]
    D -->|未达标| B
    D -->|达标| E[监控线上数据]

七、高级技巧与工具

7.1 使用Jetpack Macrobenchmark进行基准测试

添加依赖

androidTestImplementation "androidx.benchmark:benchmark-macro-junit4:1.2.0"

创建基准测试

@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
    
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()
    
    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = "com.example.app",
        metrics = listOf(StartupTimingMetric()),
        iterations = 10,
        setupBlock = {
            // 每次测试前停止应用
            pressHome()
        }
    ) {
        // 启动应用
        startActivityAndWait()
    }
}

7.2 使用Perfetto分析启动过程

1.录制启动过程:

adb shell perfetto --config :test --out /data/misc/perfetto-traces/trace

2.分析关键阶段:

7.3 线上监控方案

class LaunchMonitor private constructor() {
    
    companion object {
        @Volatile private var instance: LaunchMonitor? = null
        
        fun get() = instance ?: synchronized(this) {
            instance ?: LaunchMonitor().also { instance = it }
        }
    }
    
    private var appStartTime = 0L
    private var activityStartTime = 0L
    private var fullyDrawnTime = 0L
    
    fun recordAppStart() {
        appStartTime = SystemClock.uptimeMillis()
    }
    
    fun recordActivityStart() {
        activityStartTime = SystemClock.uptimeMillis()
    }
    
    fun recordFullyDrawn() {
        fullyDrawnTime = SystemClock.uptimeMillis()
        uploadMetrics()
    }
    
    private fun uploadMetrics() {
        val totalTime = fullyDrawnTime - appStartTime
        val initTime = activityStartTime - appStartTime
        val uiTime = fullyDrawnTime - activityStartTime
        
        // 上报到监控平台
        Firebase.analytics.logEvent("launch_time", bundleOf(
            "total" to totalTime,
            "init" to initTime,
            "ui" to uiTime
        ))
    }
}

// 在Application中
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        LaunchMonitor.get().recordAppStart()
    }
}

// 在Activity中
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        LaunchMonitor.get().recordActivityStart()
    }
    
    override fun onStart() {
        super.onStart()
        window.decorView.post {
            // 确保内容完全加载
            LaunchMonitor.get().recordFullyDrawn()
        }
    }
}

八、总结与最佳实践

8.1 启动时间优化关键点

8.2 推荐优化组合拳

1.开发阶段

2.测试阶段

3.线上阶段

8.3 持续优化路径

graph LR
    A[建立性能基线] --> B[识别瓶颈阶段]
    B --> C[实施优化方案]
    C --> D[A/B测试验证]
    D --> E[监控线上指标]
    E --> F[发现新瓶颈]
    F --> B

启动时间优化是一个持续的过程。通过本文介绍的各种统计方法和优化技巧,结合监控-分析-优化的闭环流程,你将能够显著提升应用的启动性能,为用户带来更流畅的使用体验。

终极目标:让用户感觉不到启动过程的存在!

以上就是Android统计应用启动时间的多种方法全解析的详细内容,更多关于Android统计应用启动时间的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文