fix(bugcollect): 修复 fatal crash 不上报——startCrashCapture 从未被调用
根本原因链: 1. XuqmInitializerProvider 只调用 XuqmSDK.autoInitialize,从不调用 startCrashCapture 2. 即使调用,startCrashCapture 检查 bugCollectApiUrl == null 即提前返回; 而 bugCollectApiUrl 由远端配置下发,崩溃发生时往往尚未到达 3. UncaughtExceptionHandler 从未注册,崩溃文件没有写入,下次启动无数据可上报 修复: - XuqmSDK 新增 platformUrl 属性(同步可用,初始化即有值) - BugCollect.startCrashCapture 不再依赖 bugCollectApiUrl, 用 platformUrl + "/api/bugcollect/" 作为兜底 URL 写入崩溃文件 - LogQueue.uploadPendingCrashes 优先使用崩溃文件内保存的 logApiUrl - XuqmInitializerProvider 通过反射调用 BugCollect.startCrashCapture, 在 SDK 初始化后立即注册 UncaughtExceptionHandler Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
e300cbdb83
当前提交
cc1557daf0
@ -131,11 +131,15 @@ object BugCollect {
|
|||||||
|
|
||||||
fun startCrashCapture() {
|
fun startCrashCapture() {
|
||||||
if (crashCaptureStarted) return
|
if (crashCaptureStarted) return
|
||||||
val bugCollectApiUrl = XuqmSDK.bugCollectApiUrl ?: return
|
if (!XuqmSDK.isInitialized()) return
|
||||||
|
// bugCollectApiUrl 由远端配置下发,启动时可能尚未到达。
|
||||||
|
// 用 platformUrl 推导兜底地址:崩溃文件里会保存此 URL,下次启动时用它上报。
|
||||||
|
val logApiUrl = XuqmSDK.bugCollectApiUrl
|
||||||
|
?: (XuqmSDK.platformUrl.trimEnd('/') + "/api/bugcollect/")
|
||||||
crashCaptureStarted = true
|
crashCaptureStarted = true
|
||||||
CrashCapture.start(
|
CrashCapture.start(
|
||||||
appContext = XuqmSDK.appContext,
|
appContext = XuqmSDK.appContext,
|
||||||
logApiUrl = bugCollectApiUrl,
|
logApiUrl = logApiUrl,
|
||||||
appKey = XuqmSDK.appKey,
|
appKey = XuqmSDK.appKey,
|
||||||
getUserId = { XuqmSDK.getUserId() },
|
getUserId = { XuqmSDK.getUserId() },
|
||||||
)
|
)
|
||||||
|
|||||||
@ -94,7 +94,9 @@ internal class LogQueue(
|
|||||||
userId = obj.optNullableString("userId"),
|
userId = obj.optNullableString("userId"),
|
||||||
user = IssueEvent.UserInfo(id = obj.optNullableString("userId")),
|
user = IssueEvent.UserInfo(id = obj.optNullableString("userId")),
|
||||||
)
|
)
|
||||||
LogUploader.uploadIssue(logApiUrl, event)
|
// 优先使用崩溃文件里保存的 URL(崩溃时可能远端配置未到达)
|
||||||
|
val uploadUrl = obj.optString("logApiUrl").takeIf { it.isNotBlank() } ?: logApiUrl
|
||||||
|
LogUploader.uploadIssue(uploadUrl, event)
|
||||||
file.delete()
|
file.delete()
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
Log.e(TAG, "Failed to upload crash: ${file.name}", e)
|
Log.e(TAG, "Failed to upload crash: ${file.name}", e)
|
||||||
|
|||||||
@ -40,6 +40,9 @@ object XuqmSDK {
|
|||||||
private var initialized = false
|
private var initialized = false
|
||||||
@Volatile private var initializedAppKey: String? = null
|
@Volatile private var initializedAppKey: String? = null
|
||||||
@Volatile private var resolvedPlatformUrl: String = DEFAULT_PLATFORM_URL
|
@Volatile private var resolvedPlatformUrl: String = DEFAULT_PLATFORM_URL
|
||||||
|
|
||||||
|
/** 初始化时使用的平台地址(同步可用,远端配置到达前即有值)。 */
|
||||||
|
val platformUrl: String get() = resolvedPlatformUrl
|
||||||
@Volatile private var loginSession: XuqmLoginSession? = null
|
@Volatile private var loginSession: XuqmLoginSession? = null
|
||||||
@Volatile private var userInfoValue: XuqmUserInfo? = null
|
@Volatile private var userInfoValue: XuqmUserInfo? = null
|
||||||
@Volatile var platformConfig: SdkPlatformConfig? = null
|
@Volatile var platformConfig: SdkPlatformConfig? = null
|
||||||
|
|||||||
@ -19,12 +19,26 @@ class XuqmInitializerProvider : ContentProvider() {
|
|||||||
if (!XuqmSDK.isInitialized()) {
|
if (!XuqmSDK.isInitialized()) {
|
||||||
XuqmSDK.autoInitialize(ctx)
|
XuqmSDK.autoInitialize(ctx)
|
||||||
}
|
}
|
||||||
|
// 在 SDK 初始化后立即启动崩溃捕获,确保任何后续崩溃都能被落盘。
|
||||||
|
// startCrashCapture 内部使用 platformUrl 兜底,无需等远端配置到达。
|
||||||
|
startCrashCaptureIfAvailable()
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
Log.w("XuqmSDK", "Auto-initialization skipped: ${e.message}")
|
Log.w("XuqmSDK", "Auto-initialization skipped: ${e.message}")
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startCrashCaptureIfAvailable() {
|
||||||
|
runCatching {
|
||||||
|
val bugCollect = Class.forName("com.xuqm.sdk.bugcollect.BugCollect")
|
||||||
|
val companion = bugCollect.getField("INSTANCE").get(null)
|
||||||
|
val method = bugCollect.getMethod("startCrashCapture")
|
||||||
|
method.invoke(companion)
|
||||||
|
}.onFailure {
|
||||||
|
// sdk-bugcollect 未依赖时跳过,不影响其他功能
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? = null
|
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? = null
|
||||||
override fun getType(uri: Uri): String? = null
|
override fun getType(uri: Uri): String? = null
|
||||||
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户