perf(sdk): ContentProvider 异步初始化,减少冷启动阻塞时间
- XuqmInitializerProvider 改用 autoInitializeAsync(),TokenStore/DeviceId/ApiClient 移到 IO 线程 - XuqmSDK 新增 readAppKey() 公开方法,仅读取 config 不做初始化 - BugCollectInitProvider 适配异步初始化,直接读取 appKey 注册崩溃拦截器 - 同步阶段只做 config 文件读取(~5ms),主线程不再阻塞
这个提交包含在:
父节点
0ec173677d
当前提交
ba6de0a5fd
@ -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 文件(~5ms),TokenStore / 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}")
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户