Skip to content

崩溃统计

字数统计:约 8000 字
难度等级:⭐⭐⭐
面试重要度:⭐⭐⭐⭐


目录

  1. 崩溃统计简介
  2. Firebase Crashlytics
  3. Bugly
  4. 自定义崩溃收集
  5. 崩溃分析
  6. 面试考点

1. 崩溃统计简介

1.1 为什么需要崩溃统计

崩溃统计帮助:
- 发现生产环境问题
- 优先修复影响大的崩溃
- 追踪修复效果
- 了解崩溃趋势

1.2 常见指标

- 崩溃率 (Crash-free users)
- 崩溃次数
- 受影响用户数
- 崩溃设备分布
- 崩溃版本分布

2. Firebase Crashlytics

2.1 集成

gradle
// 项目级 build.gradle.kts
buildscript {
    dependencies {
        classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")
    }
}

// 模块级 build.gradle.kts
plugins {
    id("com.google.firebase.crashlytics")
}

dependencies {
    implementation("com.google.firebase:firebase-crashlytics-ktx:18.4.3")
    implementation("com.google.firebase:firebase-analytics-ktx:21.4.0")
}

2.2 配置

kotlin
class MyApplication : Application() {
    
    override fun onCreate() {
        super.onCreate()
        
        // 配置 Crashlytics
        FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
        
        // 设置用户 ID
        FirebaseCrashlytics.getInstance().setUserId(getUserId())
        
        // 记录自定义键
        FirebaseCrashlytics.getInstance().setCustomKey("app_version", getVersionName())
    }
}

2.3 记录日志

kotlin
class CrashReporting {
    
    fun log(message: String) {
        FirebaseCrashlytics.getInstance().log(message)
    }
    
    fun recordException(throwable: Throwable) {
        FirebaseCrashlytics.getInstance().recordException(throwable)
    }
    
    fun setUserId(userId: String) {
        FirebaseCrashlytics.getInstance().setUserId(userId)
    }
    
    fun setCustomKey(key: String, value: String) {
        FirebaseCrashlytics.getInstance().setCustomKey(key, value)
    }
}

// 使用
try {
    // 代码
} catch (e: Exception) {
    CrashReporting.recordException(e)
    throw e
}

2.4 测试崩溃

kotlin
// 测试 Crashlytics
FirebaseCrashlytics.getInstance().crash()

// 或
throw RuntimeException("Test Crash")

3. Bugly

3.1 集成

gradle
dependencies {
    implementation("com.tencent.bugly:crashreport:4.1.9")
    implementation("com.tencent.bugly:nativecrashreport:3.9.2")
}

3.2 初始化

kotlin
class MyApplication : Application() {
    
    override fun onCreate() {
        super.onCreate()
        
        Bugly.init(
            applicationContext,
            "your-app-id",
            BuildConfig.DEBUG
        )
        
        // 设置用户 ID
        Bugly.setUserId(applicationContext, getUserId())
        
        // 设置版本
        Bugly.setAppVersion(applicationContext, getVersionName())
    }
}

3.3 上报

kotlin
// 上报 Java 异常
Bugly.postCatchedException(exception)

// 上报自定义错误
Bugly.reportException(exception)

// 设置用户属性
Bugly.putUserData(context, "user_id", userId)
Bugly.putUserData(context, "channel", channel)

4. 自定义崩溃收集

4.1 全局异常处理

kotlin
class CrashHandler private constructor() : Thread.UncaughtExceptionHandler {
    
    companion object {
        val instance = CrashHandler()
    }
    
    private var defaultHandler: Thread.UncaughtExceptionHandler? = null
    
    fun init(context: Context) {
        defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        Thread.setDefaultUncaughtExceptionHandler(this)
    }
    
    override fun uncaughtException(thread: Thread, throwable: Throwable) {
        // 保存崩溃信息
        saveCrashInfo(thread, throwable)
        
        // 上传到服务器
        uploadCrashInfo()
        
        // 调用默认处理
        defaultHandler?.uncaughtException(thread, throwable)
    }
    
    private fun saveCrashInfo(thread: Thread, throwable: Throwable) {
        val sb = StringBuilder()
        sb.append("Time: ${System.currentTimeMillis()}\n")
        sb.append("Thread: ${thread.name}\n")
        sb.append("Device: ${Build.MODEL}\n")
        sb.append("OS: ${Build.VERSION.RELEASE}\n")
        sb.append("App Version: ${getVersionName()}\n")
        sb.append("\nStack Trace:\n")
        sb.append(Log.getStackTraceString(throwable))
        
        // 保存到文件
        val file = File(context.cacheDir, "crash_${System.currentTimeMillis()}.log")
        file.writeText(sb.toString())
    }
    
    private fun uploadCrashInfo() {
        // 上传到服务器
    }
}

// 初始化
CrashHandler.instance.init(context)

4.2 ANR 监控

kotlin
class AnrMonitor {
    
    fun start() {
        Thread {
            while (true) {
                Thread.sleep(5000)
                
                // 检查主线程是否阻塞
                val mainLooper = Looper.getMainLooper()
                val stackTraces = Looper.getMainLooper().thread.stackTrace
                
                if (stackTraces.size > 10) {
                    // 可能 ANR
                    reportAnr(stackTraces)
                }
            }
        }.start()
    }
    
    private fun reportAnr(stackTraces: Array<StackTraceElement>) {
        // 上报 ANR 信息
    }
}

5. 崩溃分析

5.1 崩溃分类

崩溃类型:
- NullPointerException
- IndexOutOfBoundsException
- ClassCastException
- OutOfMemoryError
- NetworkOnMainThreadException
- ANR (Application Not Responding)

5.2 优先级排序

优先级:
P0 - 影响 > 10% 用户的崩溃
P1 - 影响 1-10% 用户的崩溃
P2 - 影响 < 1% 用户的崩溃
P3 - 偶发崩溃

5.3 修复流程

1. 查看崩溃报告
2. 分析堆栈信息
3. 复现问题
4. 定位原因
5. 修复代码
6. 测试验证
7. 发布更新
8. 追踪效果

6. 面试考点

6.1 基础概念

Q1: 常用的崩溃统计工具?

答案要点:
- Firebase Crashlytics (Google)
- Bugly (腾讯)
- Sentry
- 自建崩溃收集

Q2: 如何收集崩溃信息?

kotlin
// 实现 UncaughtExceptionHandler
class CrashHandler : Thread.UncaughtExceptionHandler {
    override fun uncaughtException(t: Thread, e: Throwable) {
        // 保存崩溃信息
        // 上传到服务器
    }
}

6.2 实战问题

Q3: 如何集成 Crashlytics?

gradle
// 1. 添加插件
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")

// 2. 应用插件
id("com.google.firebase.crashlytics")

// 3. 添加依赖
implementation("com.google.firebase:firebase-crashlytics-ktx:18.4.3")

Q4: 如何分析崩溃?

答案要点:
1. 查看崩溃率
2. 分析堆栈信息
3. 确定影响范围
4. 复现问题
5. 定位原因
6. 修复验证

参考资料


本文完,感谢阅读!