From 77fef3a38b04d0610d692265f9057dc5a3261adf Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Thu, 18 Jun 2026 10:18:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(bugcollect):=20=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E4=B8=8E=20URL=20=E5=AE=8C=E5=85=A8=E8=A7=A3=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 注册 UncaughtExceptionHandler 不需要 URL: - CrashCapture.start 移除 logApiUrl 参数,只需 appContext + appKey - LogStorage.saveCrash 移除 logApiUrl,崩溃文件不再存储 URL - LogQueue.uploadPendingCrashes 上传时直接取 XuqmSDK.bugCollectApiUrl, URL 为 null 则跳过(下次启动重试),不依赖崩溃文件里的历史 URL - BugCollect.startCrashCapture 移除 URL 构造逻辑 - BugCollectInitProvider Phase 1 纯粹只注册,彻底消除对 platformUrl 的依赖, 版本兼容问题从根本上消失 Co-Authored-By: Claude Sonnet 4.6 --- .../com/xuqm/sdk/bugcollect/BugCollect.kt | 3 -- .../com/xuqm/sdk/bugcollect/CrashCapture.kt | 11 ++-- .../java/com/xuqm/sdk/bugcollect/LogQueue.kt | 5 +- .../internal/BugCollectInitProvider.kt | 53 +++++-------------- .../sdk/bugcollect/internal/LogStorage.kt | 5 +- 5 files changed, 24 insertions(+), 53 deletions(-) diff --git a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/BugCollect.kt b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/BugCollect.kt index 1ee6442..3e058d1 100644 --- a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/BugCollect.kt +++ b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/BugCollect.kt @@ -137,12 +137,9 @@ object BugCollect { fun startCrashCapture() { if (crashCaptureStarted) return if (!XuqmSDK.isInitialized()) return - val logApiUrl = XuqmSDK.bugCollectApiUrl - ?: (XuqmSDK.platformUrl.trimEnd('/') + "/api/bugcollect/") crashCaptureStarted = true CrashCapture.start( appContext = XuqmSDK.appContext, - logApiUrl = logApiUrl, appKey = XuqmSDK.appKey, getUserId = { XuqmSDK.getUserId() }, ) diff --git a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/CrashCapture.kt b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/CrashCapture.kt index 5c0083a..60d081e 100644 --- a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/CrashCapture.kt +++ b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/CrashCapture.kt @@ -4,11 +4,16 @@ import android.content.Context import com.xuqm.sdk.bugcollect.internal.LogStorage internal object CrashCapture { - fun start(appContext: Context, logApiUrl: String, appKey: String, getUserId: () -> String?) { + + @Volatile private var started = false + + /** 注册全局崩溃拦截器,只需 appContext + appKey,不依赖上传 URL。 */ + fun start(appContext: Context, appKey: String, getUserId: () -> String?) { + if (started) return + started = true val prev = Thread.getDefaultUncaughtExceptionHandler() Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> - // 使用带 Context 的重载,依赖 context.filesDir 获取可靠路径 - LogStorage.saveCrash(appContext, throwable, logApiUrl, appKey, getUserId()) + LogStorage.saveCrash(appContext, throwable, appKey, getUserId()) prev?.uncaughtException(thread, throwable) } } diff --git a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/LogQueue.kt b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/LogQueue.kt index 2ab2a39..ab7642e 100644 --- a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/LogQueue.kt +++ b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/LogQueue.kt @@ -65,7 +65,8 @@ internal class LogQueue( } fun uploadPendingCrashes() { - if (logApiUrl.isBlank()) return + // 上传时才需要 URL,从远端配置取最新值 + val uploadUrl = com.xuqm.sdk.XuqmSDK.bugCollectApiUrl ?: return scope.launch { val crashDir = LogStorage.crashDir(appContext) val files = crashDir.listFiles() ?: return@launch @@ -94,8 +95,6 @@ internal class LogQueue( userId = obj.optNullableString("userId"), user = IssueEvent.UserInfo(id = obj.optNullableString("userId")), ) - // 优先使用崩溃文件里保存的 URL(崩溃时可能远端配置未到达) - val uploadUrl = obj.optString("logApiUrl").takeIf { it.isNotBlank() } ?: logApiUrl LogUploader.uploadIssue(uploadUrl, event) file.delete() }.onFailure { e -> diff --git a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/BugCollectInitProvider.kt b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/BugCollectInitProvider.kt index 0dc9fd1..6b96682 100644 --- a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/BugCollectInitProvider.kt +++ b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/BugCollectInitProvider.kt @@ -17,15 +17,14 @@ import kotlinx.coroutines.launch /** * 崩溃收集的早期初始化入口。 * - * initOrder=89,在 sdk-core XuqmInitializerProvider(90) 之后运行,此时: - * - XuqmSDK.appKey / platformUrl 已由本地 config 文件同步设置 - * - 远端平台配置尚未到达(bugCollectApiUrl 可能还是 null) + * initOrder=89,在 sdk-core XuqmInitializerProvider(90) 之后运行,此时 + * XuqmSDK.appKey 已由本地 config 文件同步设置。 * - * 职责: - * 1. 立即注册 UncaughtExceptionHandler,把崩溃落盘(只需 appKey + 派生 URL) - * 2. 后台等待 XuqmSDK.awaitInitialization() 完成后,上传积压的崩溃文件 - * - * sdk-core 不依赖本类,XuqmSDK 可独立使用。 + * 职责分离: + * Phase 1(同步):只注册 UncaughtExceptionHandler,把崩溃落盘。 + * 不需要上传 URL,注册与 URL 完全解耦。 + * Phase 2(异步):等待 XuqmSDK.awaitInitialization() 完成, + * 远端配置下发 bugCollectApiUrl 后再上传积压的崩溃文件。 */ internal class BugCollectInitProvider : ContentProvider() { @@ -33,54 +32,28 @@ internal class BugCollectInitProvider : ContentProvider() { val ctx = context?.applicationContext ?: return true if (!XuqmSDK.isInitialized()) { - // core 未初始化(config 文件缺失等),跳过 - Log.w("BugCollect", "BugCollectInitProvider: XuqmSDK not initialized, skipping crash capture setup") - return true - } - - // ── Phase 1: 立即注册崩溃拦截,落盘不需要远端配置 ────────────────────── - val logApiUrl = XuqmSDK.bugCollectApiUrl - ?: deriveFallbackUrl() - - if (logApiUrl.isBlank()) { - Log.w("BugCollect", "Crash capture skipped: no URL available (update sdk-core to latest SNAPSHOT)") + Log.w("BugCollect", "BugCollectInitProvider: XuqmSDK not initialized, skipping") return true } + // ── Phase 1:立即注册拦截器,只需 appKey,无需 URL ────────────────────── CrashCapture.start( appContext = ctx, - logApiUrl = logApiUrl, appKey = XuqmSDK.appKey, getUserId = { XuqmSDK.getUserId() }, ) - // ── Phase 2: 等远端配置就绪后上传积压的崩溃文件 ───────────────────────── - uploadAfterInit(ctx) - - return true - } - - /** - * 从 XuqmSDK.platformUrl 派生兜底地址。 - * 用 try-catch 保护:旧版 sdk-core 没有此属性时返回空串,让调用方跳过。 - */ - private fun deriveFallbackUrl(): String = runCatching { - XuqmSDK.platformUrl.trimEnd('/') + "/api/bugcollect/" - }.getOrElse { e -> - Log.w("BugCollect", "Cannot derive bugcollect URL (sdk-core may be outdated): ${e.message}") - "" - } - - private fun uploadAfterInit(ctx: Context) { + // ── Phase 2:远端配置就绪后上传 ───────────────────────────────────────── CoroutineScope(SupervisorJob() + Dispatchers.IO).launch { runCatching { XuqmSDK.awaitInitialization() - // 远端配置到达后 bugCollectApiUrl 已更新,用最新 URL 上传 BugCollect.uploadPendingCrashes() }.onFailure { e -> - Log.w("BugCollect", "Failed to upload pending crashes after init: ${e.message}") + Log.w("BugCollect", "Pending crash upload failed after init: ${e.message}") } } + + return true } override fun query(uri: Uri, p: Array?, s: String?, sa: Array?, so: String?): Cursor? = null diff --git a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/LogStorage.kt b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/LogStorage.kt index e332008..62a4444 100644 --- a/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/LogStorage.kt +++ b/sdk-bugcollect/src/main/java/com/xuqm/sdk/bugcollect/internal/LogStorage.kt @@ -13,13 +13,11 @@ internal object LogStorage { /** * 同步写崩溃文件(在 UncaughtExceptionHandler 中调用)。 - * Application Context 在 handler 回调时仍然有效,直接用 context.filesDir 获取可靠路径。 - * 文件在下次启动时由 LogQueue.uploadPendingCrashes 上报后删除。 + * 不存储上传 URL——URL 在下次启动上传时从 XuqmSDK.bugCollectApiUrl 取得。 */ fun saveCrash( context: Context, throwable: Throwable, - logApiUrl: String, appKey: String, userId: String?, ) { @@ -31,7 +29,6 @@ internal object LogStorage { 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())