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>
这个提交包含在:
父节点
58d479523e
当前提交
e300cbdb83
@ -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("")
|
|
||||||
}
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户