fix(bugcollect): 修复崩溃文件未写入导致 fatal crash 不上报的 bug

根本原因:CrashCapture 调用无 Context 的 LogStorage.saveCrash 重载,
该重载通过反射获取 packageName 失败时返回空字符串,
导致崩溃文件路径变为 /data/data//files/xuqm_crashes(无效),
mkdirs() 静默失败,崩溃文件未写入,下次启动无数据可上报。

修复:
- CrashCapture.start() 新增 appContext: Context 参数
- 改为调用带 Context 的 saveCrash 重载,依赖 context.filesDir 获取可靠路径
- BugCollect.startCrashCapture() 传入 XuqmSDK.appContext
- LogStorage 删除不可靠的无 Context 重载及 getPackageName() 反射代码

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-06-18 09:43:47 +08:00
父节点 58d479523e
当前提交 e300cbdb83
共有 3 个文件被更改,包括 8 次插入40 次删除

查看文件

@ -134,6 +134,7 @@ object BugCollect {
val bugCollectApiUrl = XuqmSDK.bugCollectApiUrl ?: return val bugCollectApiUrl = XuqmSDK.bugCollectApiUrl ?: return
crashCaptureStarted = true crashCaptureStarted = true
CrashCapture.start( CrashCapture.start(
appContext = XuqmSDK.appContext,
logApiUrl = bugCollectApiUrl, logApiUrl = bugCollectApiUrl,
appKey = XuqmSDK.appKey, appKey = XuqmSDK.appKey,
getUserId = { XuqmSDK.getUserId() }, getUserId = { XuqmSDK.getUserId() },

查看文件

@ -1,12 +1,14 @@
package com.xuqm.sdk.bugcollect package com.xuqm.sdk.bugcollect
import android.content.Context
import com.xuqm.sdk.bugcollect.internal.LogStorage import com.xuqm.sdk.bugcollect.internal.LogStorage
internal object CrashCapture { internal object CrashCapture {
fun start(logApiUrl: String, appKey: String, getUserId: () -> String?) { fun start(appContext: Context, logApiUrl: String, appKey: String, getUserId: () -> String?) {
val prev = Thread.getDefaultUncaughtExceptionHandler() val prev = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
LogStorage.saveCrash(throwable, logApiUrl, appKey, getUserId()) // 使用带 Context 的重载,依赖 context.filesDir 获取可靠路径
LogStorage.saveCrash(appContext, throwable, logApiUrl, appKey, getUserId())
prev?.uncaughtException(thread, throwable) prev?.uncaughtException(thread, throwable)
} }
} }

查看文件

@ -12,38 +12,9 @@ internal object LogStorage {
File(context.filesDir, CRASH_DIR).also { it.mkdirs() } File(context.filesDir, CRASH_DIR).also { it.mkdirs() }
/** /**
* 同步写崩溃信息到文件系统UncaughtExceptionHandler 中调用 * 同步写崩溃文件 UncaughtExceptionHandler 中调用
* 文件将在下次启动时由 [com.xuqm.sdk.bugcollect.LogQueue.uploadPendingCrashes] 上报 * Application Context handler 回调时仍然有效直接用 context.filesDir 获取可靠路径
*/ * 文件在下次启动时由 LogQueue.uploadPendingCrashes 上报后删除
fun saveCrash(
throwable: Throwable,
logApiUrl: String,
appKey: String,
userId: String?,
) {
runCatching {
val dir = File(
// 使用系统属性获取 filesDir 替代 Context在 UncaughtExceptionHandler 中 Context 可能不可用)
android.os.Environment.getDataDirectory().absolutePath +
"/data/${getPackageName()}/files/$CRASH_DIR"
).also { it.mkdirs() }
val file = File(dir, "${System.currentTimeMillis()}.json")
val json = JSONObject().apply {
put("level", "fatal")
put("message", throwable.message ?: throwable.javaClass.name)
put("stack", throwable.stackTraceToString())
put("exceptionType", throwable.javaClass.simpleName)
put("logApiUrl", logApiUrl)
put("appKey", appKey)
put("userId", userId ?: JSONObject.NULL)
put("timestamp", System.currentTimeMillis())
}
file.writeText(json.toString())
}
}
/**
* Application 中通过 Context 保存崩溃更可靠
*/ */
fun saveCrash( fun saveCrash(
context: Context, context: Context,
@ -68,10 +39,4 @@ internal object LogStorage {
file.writeText(json.toString()) file.writeText(json.toString())
} }
} }
private fun getPackageName(): String = runCatching {
val clazz = Class.forName("android.app.ActivityThread")
val method = clazz.getMethod("currentPackageName")
method.invoke(null) as? String ?: ""
}.getOrDefault("")
} }