perf(sdk): ContentProvider 异步初始化,减少冷启动阻塞时间

- XuqmInitializerProvider 改用 autoInitializeAsync(),TokenStore/DeviceId/ApiClient 移到 IO 线程
- XuqmSDK 新增 readAppKey() 公开方法,仅读取 config 不做初始化
- BugCollectInitProvider 适配异步初始化,直接读取 appKey 注册崩溃拦截器
- 同步阶段只做 config 文件读取(~5ms),主线程不再阻塞
这个提交包含在:
XuqmGroup 2026-06-18 14:53:34 +08:00
父节点 0ec173677d
当前提交 ba6de0a5fd
共有 3 个文件被更改,包括 49 次插入5 次删除

查看文件

@ -31,15 +31,22 @@ internal class BugCollectInitProvider : ContentProvider() {
override fun onCreate(): Boolean {
val ctx = context?.applicationContext ?: return true
if (!XuqmSDK.isInitialized()) {
Log.w("BugCollect", "BugCollectInitProvider: XuqmSDK not initialized, skipping")
// ── Phase 1注册崩溃拦截器只需 appKey,无需 SDK 完全初始化)──────────
// SDK 可能正在后台初始化中,直接读取 config 文件获取 appKey
val appKey = if (XuqmSDK.isInitialized()) {
XuqmSDK.appKey
} else {
XuqmSDK.readAppKey(ctx)
}
if (appKey.isNullOrBlank()) {
Log.w("BugCollect", "BugCollectInitProvider: no appKey available, skipping")
return true
}
// ── Phase 1立即注册拦截器,只需 appKey,无需 URL ──────────────────────
CrashCapture.start(
appContext = ctx,
appKey = XuqmSDK.appKey,
appKey = appKey,
getUserId = { XuqmSDK.getUserId() ?: XuqmSDK.deviceId },
)

查看文件

@ -78,6 +78,14 @@ object XuqmSDK {
* 配置文件包含 `appKey` 和可选的 `serverUrl`私有化部署平台地址
* App 无需调用任何初始化代码
*/
/**
* 仅读取 config 文件中的 appKey不做任何初始化
* 用于 ContentProvider 等需要快速获取 appKey 的场景
*/
fun readAppKey(context: Context): String? {
return runCatching { ConfigFileReader.read(context.applicationContext)?.appKey }.getOrNull()
}
fun autoInitialize(context: Context, logLevel: LogLevel = LogLevel.WARN) {
val configFile = ConfigFileReader.read(context)
?: throw IllegalStateException(
@ -94,6 +102,33 @@ object XuqmSDK {
initialize(context, configFile.appKey, configFile.serverUrl, logLevel)
}
/**
* 异步自动初始化 ContentProvider 调用不阻塞主线程
* 同步阶段只读取 config 文件~5msTokenStore / DeviceId / ApiClient 等重活移到 IO 线程
*/
fun autoInitializeAsync(context: Context, logLevel: LogLevel = LogLevel.WARN) {
val ctx = context.applicationContext
// 同步:读取 config快,<10ms
val configFile = ConfigFileReader.read(ctx)
if (configFile == null) {
Log.w("XuqmSDK", "autoInitializeAsync: no config file, skipping")
return
}
val configPackageName = configFile.packageName
if (!configPackageName.isNullOrBlank() && configPackageName != ctx.packageName) {
Log.w("XuqmSDK", "autoInitializeAsync: package name mismatch, skipping")
return
}
// 异步TokenStore / DeviceId / ApiClient / 远端配置
kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.IO + kotlinx.coroutines.SupervisorJob()).launch {
runCatching {
initialize(ctx, configFile.appKey, configFile.serverUrl, logLevel)
}.onFailure { e ->
Log.w("XuqmSDK", "autoInitializeAsync background init failed: ${e.message}")
}
}
}
/**
* 方式 B手动初始化
*

查看文件

@ -15,9 +15,11 @@ import com.xuqm.sdk.XuqmSDK
class XuqmInitializerProvider : ContentProvider() {
override fun onCreate(): Boolean {
val ctx = context?.applicationContext ?: return true
// 同步阶段只做最小工作:读取 config 文件设置 appKey约 5ms
// TokenStore / DeviceId / ApiClient 等重初始化移到后台线程
runCatching {
if (!XuqmSDK.isInitialized()) {
XuqmSDK.autoInitialize(ctx)
XuqmSDK.autoInitializeAsync(ctx)
}
}.onFailure { e ->
Log.w("XuqmSDK", "Auto-initialization skipped: ${e.message}")