浅谈SDK设计

SDK 设计核心关注


一、总览架构

复制代码
┌──────────────────────────────────────────────────────────────────┐
│                     SDK 设计关注维度                               │
│                                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        │
│  │ API 设计  │  │ 稳定性    │  │ 性能     │  │ 安全性    │        │
│  │          │  │          │  │          │  │          │        │
│  │ 易用性    │  │ 兼容性    │  │ 包体积   │  │ 隐私合规  │        │
│  │ 一致性    │  │ 容错     │  │ 内存     │  │ 数据安全  │        │
│  │ 可扩展    │  │ 隔离     │  │ 电量     │  │ 权限最小化│        │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘        │
│                                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        │
│  │ 集成体验  │  │ 可观测性  │  │ 测试     │  │ 发布维护  │        │
│  │          │  │          │  │          │  │          │        │
│  │ 文档     │  │ 日志     │  │ 单元测试  │  │ 版本管理  │        │
│  │ 初始化   │  │ 监控     │  │ 集成测试  │  │ 灰度发布  │        │
│  │ 示例代码  │  │ 调试支持  │  │ Mock     │  │ 向后兼容  │        │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘        │
└──────────────────────────────────────────────────────────────────┘

二、API 设计(最核心)

1. 易用性 --- 让接入方 5 分钟上手

kotlin 复制代码
// ✅ 好的 API: 简洁、符合直觉
// 最简使用只需一行
MySdk.init(context)

// 常见功能两三行
MySdk.track("purchase", mapOf("price" to 99.9))

// 高级用法链式配置
MySdk.builder(context)
    .setAppKey("xxx")
    .setDebug(BuildConfig.DEBUG)
    .enableFeatureX()
    .build()

// ❌ 坏的 API: 暴露过多概念
val config = MySdkConfig()
config.appKey = "xxx"
config.env = MySdkEnv.PROD
val transport = MySdkHttpTransport(OkHttpClient())
val serializer = MySdkJsonSerializer(GsonFactory.create())
val dispatcher = MySdkEventDispatcher(transport, serializer)
val engine = MySdkEngine(config, dispatcher)
engine.initialize(context)
engine.start()
// 接入方: "我只想打个点,为什么要了解这么多?"

2. 一致性 --- 统一的设计范式

kotlin 复制代码
// ✅ 命名一致
sdk.startTracking()
sdk.stopTracking()
sdk.isTracking()       // is 前缀表示布尔

sdk.addListener(listener)
sdk.removeListener(listener)
sdk.clearListeners()   // add/remove/clear 一套

// ✅ 参数风格一致
// 全部用 Builder 模式, 或全部用 DSL, 不要混搭
// Builder:
Request.Builder()
    .url("...")
    .header("key", "value")
    .build()

// DSL:
request {
    url = "..."
    header("key", "value")
}

// ❌ 不一致的设计
sdk.init(key: String, secret: String, env: Int)  // 参数列表
sdk.configure(Config.Builder().build())           // Builder
sdk.setOptions { it.timeout = 30 }               // DSL
// 三种风格混在一起

3. 最少知识原则 --- 隐藏内部实现

less 复制代码
┌──────────────────────────────────────────────────────────────┐
│                  SDK 分层架构                                  │
│                                                              │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Public API Layer (公开层)                              │  │
│  │                                                        │  │
│  │  MySdk (入口)                                          │  │
│  │  MySdkConfig (配置)                                    │  │
│  │  EventListener (回调接口)                               │  │
│  │  Result<T> (返回类型)                                   │  │
│  │                                                        │  │
│  │  ← 接入方只需关心这一层                                  │  │
│  ├────────────────────────────────────────────────────────┤  │
│  │  Internal Layer (内部层, @RestrictTo / internal)       │  │
│  │                                                        │  │
│  │  EventDispatcher / EventQueue                          │  │
│  │  NetworkClient / CacheManager                          │  │
│  │  Serializer / Scheduler                                │  │
│  │  DatabaseHelper / FileStorage                          │  │
│  │                                                        │  │
│  │  ← 内部实现, 对接入方不可见                               │  │
│  └────────────────────────────────────────────────────────┘  │
│                                                              │
│  可见性控制:                                                  │
│    Kotlin:  internal 关键字 (模块内可见)                      │
│    Java:    @RestrictTo(LIBRARY) + ProGuard 混淆              │
│    包名:    com.sdk.public / com.sdk.internal                 │
└──────────────────────────────────────────────────────────────┘
kotlin 复制代码
// ✅ 严格控制公开 API

// 公开接口
public interface MySdk {
    fun track(event: String, params: Map<String, Any> = emptyMap())
    fun setUserId(id: String)
    fun flush()

    companion object {
        fun init(context: Context, config: Config = Config.default()): MySdk
    }
}

// 内部实现 (接入方看不到)
internal class MySdkImpl(
    private val context: Context,
    private val config: Config
) : MySdk {
    // 内部的线程池、缓存、网络请求等全部 internal
    internal val eventQueue = EventQueue()
    internal val networkClient = NetworkClient()
    // ...
}

// ✅ 返回接口而非实现类
fun init(): MySdk  // 返回接口
// ❌ 不要
fun init(): MySdkImpl  // 暴露了实现类

4. 可扩展性 --- 预留扩展点

kotlin 复制代码
// ✅ 策略接口: 允许接入方自定义行为
interface MySdkConfig {
    // 自定义网络层 (默认用内置的, 也可以用 OkHttp)
    var httpClient: HttpClient?

    // 自定义序列化 (默认 JSON, 也可以用 Protobuf)
    var serializer: Serializer?

    // 自定义日志
    var logger: Logger?

    // 自定义持久化
    var storage: Storage?

    // 拦截器 (请求/响应拦截)
    fun addInterceptor(interceptor: Interceptor)
}

// 接入方可以替换任何组件
MySdk.init(context) {
    httpClient = OkHttpClientAdapter(myOkHttpClient)
    logger = TimberLogger()
    addInterceptor(AuthInterceptor(tokenProvider))
}

5. 回调设计

kotlin 复制代码
// ✅ 提供多种回调方式, 适应不同项目

// 方式1: 接口回调 (Java 友好)
interface OnResultListener {
    fun onSuccess(data: Data)
    fun onError(error: SdkError)
}
sdk.fetch(listener)

// 方式2: Lambda (Kotlin 友好)
sdk.fetch(
    onSuccess = { data -> },
    onError = { error -> }
)

// 方式3: 挂起函数 (协程友好)
suspend fun fetch(): Result<Data>

// 方式4: Flow (响应式)
fun observeState(): Flow<State>

// 方式5: LiveData (Android 生命周期感知)
fun observeState(): LiveData<State>

// ✅ 好的实践: 内部用协程/Flow, 对外暴露多种适配
// 核心实现
internal suspend fun fetchInternal(): Data { ... }

// 适配层
fun fetch(listener: OnResultListener) {
    scope.launch {
        try {
            val data = fetchInternal()
            mainHandler.post { listener.onSuccess(data) }
        } catch (e: Exception) {
            mainHandler.post { listener.onError(e.toSdkError()) }
        }
    }
}

suspend fun fetchSuspend(): Result<Data> = runCatching { fetchInternal() }

fun fetchFlow(): Flow<Data> = flow { emit(fetchInternal()) }

6. 错误设计

kotlin 复制代码
// ✅ 定义清晰的错误体系
sealed class SdkError(
    val code: Int,
    override val message: String,
    override val cause: Throwable? = null
) : Exception(message, cause) {

    // 初始化错误
    class NotInitialized : SdkError(1001, "SDK 未初始化, 请先调用 init()")
    class InvalidConfig(detail: String) : SdkError(1002, "配置无效: $detail")

    // 网络错误
    class NetworkError(cause: Throwable) :
        SdkError(2001, "网络请求失败", cause)
    class ServerError(val httpCode: Int, body: String) :
        SdkError(2002, "服务端错误: $httpCode - $body")
    class Timeout : SdkError(2003, "请求超时")

    // 业务错误
    class AuthFailed : SdkError(3001, "鉴权失败")
    class RateLimited(val retryAfterMs: Long) :
        SdkError(3002, "请求过于频繁, ${retryAfterMs}ms 后重试")

    // 未知错误
    class Unknown(cause: Throwable) :
        SdkError(9999, "未知错误: ${cause.message}", cause)
}

// ✅ 提供错误码文档
// ✅ 错误信息要有指导性 (告诉用户怎么解决)
// ❌ 不要只说 "Error occurred" 这种废话

三、稳定性

1. 容错 --- SDK 不能让宿主 App 崩溃

kotlin 复制代码
// ✅ 核心原则: SDK 的任何异常都不能影响宿主 App!

// 内部异常兜底
internal object SafeExecutor {
    private val handler = CoroutineExceptionHandler { _, throwable ->
        SdkLogger.e("SDK 内部异常, 已兜底", throwable)
        // 上报但不崩溃
        ErrorReporter.report(throwable)
    }

    val scope = CoroutineScope(
        SupervisorJob() + Dispatchers.Default + handler
    )

    fun execute(block: () -> Unit) {
        try {
            block()
        } catch (e: Exception) {
            SdkLogger.e("执行异常", e)
            ErrorReporter.report(e)
        }
    }
}

// 所有公开 API 都做防护
class MySdkImpl : MySdk {
    override fun track(event: String, params: Map<String, Any>) {
        SafeExecutor.execute {
            // 即使内部实现出 bug, 也不会崩溃宿主 App
            trackInternal(event, params)
        }
    }
}

2. 线程安全

kotlin 复制代码
// ✅ 公开 API 必须线程安全 (接入方可能从任何线程调用)
class MySdkImpl : MySdk {
    @Volatile
    private var isInitialized = false

    // 方式1: 同步锁
    private val lock = Any()
    override fun track(event: String, params: Map<String, Any>) {
        synchronized(lock) {
            checkInitialized()
            eventQueue.enqueue(Event(event, params))
        }
    }

    // 方式2: 串行化 (推荐)
    // 所有操作 post 到内部单线程, 天然线程安全
    private val sdkHandler = HandlerThread("sdk-thread").apply { start() }
        .let { Handler(it.looper) }

    override fun track(event: String, params: Map<String, Any>) {
        sdkHandler.post {
            trackInternal(event, params)
        }
    }

    // 方式3: 协程串行化
    private val sdkScope = CoroutineScope(
        SupervisorJob() + Dispatchers.Default.limitedParallelism(1)
    )
}

3. 兼容性

kotlin 复制代码
// ✅ minSdk 尽量低 (覆盖更多设备)
// 推荐 minSdk 21 (Android 5.0, 覆盖 99%+ 设备)

// ✅ 依赖尽量少, 避免版本冲突
// ❌ 不要
dependencies {
    api("com.squareup.okhttp3:okhttp:4.12.0")     // 可能与宿主版本冲突!
    api("com.google.code.gson:gson:2.10.1")        // 同上
    api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}

// ✅ 依赖最小化
dependencies {
    // 核心功能用 Android 原生 API 实现
    // 如果必须用第三方库, 使用 implementation (不传递依赖)
    implementation("com.squareup.okhttp3:okhttp:4.12.0")

    // 或者提供适配接口, 让宿主注入
    compileOnly("com.squareup.okhttp3:okhttp:4.12.0")
}

// ✅ 编译选项
android {
    compileSdk = 34
    defaultConfig {
        minSdk = 21
        // 不要设置 targetSdk (由宿主 App 决定)
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}
csharp 复制代码
依赖冲突解决策略:

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  策略1: 不依赖第三方库 (最安全)                               │
│    网络 → HttpURLConnection / 内置轻量封装                   │
│    JSON → org.json (系统自带) / 手写解析                     │
│    线程 → Handler / ExecutorService                         │
│    存储 → SharedPreferences / SQLiteOpenHelper               │
│                                                             │
│  策略2: compileOnly + 运行时检测                             │
│    compileOnly("com.squareup.okhttp3:okhttp:4.12.0")        │
│    运行时: 检测类是否存在, 存在则使用, 否则用内置实现           │
│                                                             │
│  策略3: 重打包 (Shade/Relocate)                              │
│    用 shadow 插件将第三方库重命名包名                         │
│    com.squareup.okhttp3 → com.mysdk.internal.okhttp3        │
│    完全避免冲突, 但增加包体积                                 │
│                                                             │
│  策略4: 提供多 Artifact                                      │
│    mysdk-core (纯净, 无依赖)                                 │
│    mysdk-okhttp (OkHttp 适配器)                              │
│    mysdk-gson (Gson 适配器)                                  │
│    接入方按需引入                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4. 隔离 --- 不影响宿主

kotlin 复制代码
// ✅ 独立线程池 (不占用宿主线程资源)
internal val sdkExecutor = ThreadPoolExecutor(
    1, 2, 30, TimeUnit.SECONDS,
    ArrayBlockingQueue(128),
    NamedThreadFactory("mysdk"),
    ThreadPoolExecutor.DiscardOldestPolicy() // 满了丢弃旧的, 不抛异常
)

// ✅ 独立数据库 (不与宿主冲突)
// 数据库文件用 SDK 特有前缀
private const val DB_NAME = "mysdk_internal.db"

// ✅ 独立 SharedPreferences
private const val SP_NAME = "mysdk_prefs"
val prefs = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)

// ✅ 独立文件目录
val sdkDir = File(context.filesDir, "mysdk_data")
sdkDir.mkdirs()

// ✅ 独立进程 (极端隔离, 谨慎使用)
// <service android:name=".SdkService" android:process=":mysdk" />
// 优点: 完全隔离, SDK crash 不影响主进程
// 缺点: 进程间通信开销, 内存增加

四、性能

1. 初始化性能 --- 不能拖慢 App 启动

kotlin 复制代码
// ✅ 异步初始化 + 延迟初始化
class MySdk {
    companion object {
        @Volatile
        private var instance: MySdkImpl? = null

        // 轻量同步初始化 (< 5ms)
        fun init(context: Context, config: Config = Config.default()) {
            val appContext = context.applicationContext
            instance = MySdkImpl(appContext, config)
            // 重量级初始化放到后台
            instance!!.initAsync()
        }
    }
}

internal class MySdkImpl(context: Context, config: Config) {
    fun initAsync() {
        sdkScope.launch(Dispatchers.IO) {
            // 耗时操作异步执行
            initDatabase()
            loadCachedConfig()
            startHeartbeat()
            preloadResources()
        }
    }

    // 在需要时才初始化的组件
    val networkClient by lazy { NetworkClient(config) }
    val analyticsEngine by lazy { AnalyticsEngine(database) }
}

// ✅ 支持 App Startup (Jetpack)
class MySdkInitializer : Initializer<MySdk> {
    override fun create(context: Context): MySdk {
        return MySdk.init(context) // 自动初始化
    }
    override fun dependencies() = emptyList<Class<out Initializer<*>>>()
}

2. 包体积 --- 越小越好

scss 复制代码
┌──────────────────────────────────────────────────────────────┐
│                  包体积优化策略                                │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 减少依赖                                                  │
│     尽量用 Android 原生 API                                   │
│     每引入一个库 = 增加 几十KB~几MB                             │
│                                                              │
│  2. ProGuard / R8 混淆                                       │
│     consumerProguardFiles("proguard-rules.pro")              │
│     打包到 AAR 中, 自动应用到宿主                              │
│                                                              │
│  3. 资源最小化                                                │
│     ├── 不包含不必要的资源 (图片/布局/字符串)                   │
│     ├── 资源名加 SDK 前缀 (mysdk_ 避免冲突)                   │
│     ├── VectorDrawable 替代 PNG                              │
│     └── resConfigs 只保留需要的语言                            │
│                                                              │
│  4. 按需加载                                                  │
│     核心功能在主包, 高级功能动态下发                            │
│     或拆分为多个 artifact (mysdk-core / mysdk-ui)             │
│                                                              │
│  5. 代码精简                                                  │
│     避免使用大型注解处理器 (如 Dagger 全家桶)                   │
│     避免 Kotlin 反射 (kotlin-reflect 2.5MB!)                  │
│                                                              │
│  目标: 核心 SDK < 100KB ~ 500KB                              │
│                                                              │
└──────────────────────────────────────────────────────────────┘

3. 内存 / CPU / 电量

kotlin 复制代码
// ✅ 内存控制
// 限制内部缓存大小
private val cache = LruCache<String, ByteArray>(maxSize = 2 * 1024 * 1024) // 2MB

// 及时释放大对象
fun onTrimMemory(level: Int) {
    if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
        cache.evictAll()
        bitmapPool.clear()
    }
}

// ✅ CPU 控制
// 后台任务降低线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)

// 批量处理, 减少唤醒次数
private val eventBatch = mutableListOf<Event>()
private val flushRunnable = Runnable {
    if (eventBatch.isNotEmpty()) {
        sendBatch(ArrayList(eventBatch))
        eventBatch.clear()
    }
}

override fun track(event: Event) {
    eventBatch.add(event)
    if (eventBatch.size >= BATCH_SIZE) {
        handler.removeCallbacks(flushRunnable)
        handler.post(flushRunnable)
    } else {
        handler.removeCallbacks(flushRunnable)
        handler.postDelayed(flushRunnable, FLUSH_INTERVAL) // 30秒
    }
}

// ✅ 电量优化
// 合并网络请求, 减少无线电唤醒
// 使用 WorkManager 做非紧急上传
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "sdk_sync",
    ExistingPeriodicWorkPolicy.KEEP,
    PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS).build()
)

五、安全性

scss 复制代码
┌──────────────────────────────────────────────────────────────┐
│                     安全性关注点                              │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 数据传输安全                                              │
│     ✅ 所有网络请求走 HTTPS                                   │
│     ✅ Certificate Pinning (证书锁定)                         │
│     ✅ 敏感数据加密传输                                       │
│                                                              │
│  2. 数据存储安全                                              │
│     ✅ 敏感信息不明文存储 (EncryptedSharedPreferences)        │
│     ✅ 数据库加密 (SQLCipher)                                 │
│     ✅ 日志中不打印敏感信息 (token/密码/手机号)                │
│                                                              │
│  3. 密钥管理                                                  │
│     ✅ AppKey/Secret 不硬编码在代码中                         │
│     ✅ 使用 Android Keystore 存储密钥                        │
│     ✅ 服务端动态下发配置                                     │
│                                                              │
│  4. 防篡改 / 防调试                                          │
│     ✅ ProGuard/R8 混淆                                      │
│     ✅ 签名校验 (验证宿主 App 签名)                           │
│     ✅ 关键逻辑放 Native 层 (NDK)                            │
│                                                              │
│  5. 权限最小化                                                │
│     ✅ 只申请必要权限                                        │
│     ✅ 运行时权限动态检查, 没有权限则降级                     │
│     ✅ 不要申请与 SDK 功能无关的权限                          │
│                                                              │
│  6. 隐私合规 (GDPR/CCPA/个保法)                              │
│     ✅ 提供用户数据删除接口                                   │
│     ✅ 提供数据采集开关 (opt-in / opt-out)                    │
│     ✅ 隐私政策中说明采集了什么数据                            │
│     ✅ 在用户同意前不采集任何数据                              │
│     ✅ 不采集 IMEI/MAC 等不可重置标识符                       │
│                                                              │
└──────────────────────────────────────────────────────────────┘
kotlin 复制代码
// ✅ 隐私合规示例
class MySdk {
    companion object {
        private var isConsentGiven = false

        // 必须先获得用户同意
        fun setConsentStatus(consent: Boolean) {
            isConsentGiven = consent
            if (!consent) {
                // 用户撤回同意 → 停止采集 + 删除已采集数据
                instance?.stopCollection()
                instance?.deleteAllUserData()
            }
        }

        fun init(context: Context, config: Config) {
            // 初始化时不采集数据, 等用户同意后才开始
            instance = MySdkImpl(context, config)
        }
    }
}

internal class MySdkImpl {
    fun track(event: String) {
        if (!isConsentGiven) {
            SdkLogger.d("用户未同意, 跳过数据采集")
            return
        }
        // 正常采集
    }

    fun deleteAllUserData() {
        database.clearAll()
        preferences.clear()
        cache.evictAll()
        SdkLogger.i("已删除所有用户数据")
    }
}

六、初始化设计

kotlin 复制代码
// ✅ 推荐: 多种初始化方式并存

// 方式1: 手动初始化 (最明确)
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        MySdk.init(this) {
            appKey = "xxx"
            debug = BuildConfig.DEBUG
        }
    }
}

// 方式2: App Startup 自动初始化 (零代码接入)
// AndroidManifest.xml
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup">
    <meta-data
        android:name="com.mysdk.MySdkInitializer"
        android:value="androidx.startup" />
</provider>

// 方式3: ContentProvider 自动初始化 (无需 App Startup)
// 适合不依赖 Jetpack 的场景
internal class MySdkAutoInitProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        MySdk.init(context!!)
        return true
    }
    // ... 其他方法返回空/false
}

// ✅ 防重复初始化
class MySdk {
    companion object {
        @Volatile
        private var initialized = false

        fun init(context: Context, block: Config.Builder.() -> Unit = {}) {
            if (initialized) {
                SdkLogger.w("SDK 已初始化, 忽略重复调用")
                return
            }
            synchronized(this) {
                if (initialized) return
                // ... 初始化逻辑
                initialized = true
            }
        }
    }
}

// ✅ 未初始化时给出友好提示
override fun track(event: String) {
    if (!initialized) {
        SdkLogger.e("SDK 未初始化! 请先调用 MySdk.init()")
        // 不崩溃, 但记录错误
        return
    }
}

七、可观测性

日志系统

kotlin 复制代码
// ✅ 完善的日志系统

// 1. 分级日志
enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }

object SdkLogger {
    var level: LogLevel = LogLevel.WARN  // 默认只输出 WARN+
    var delegate: LogDelegate? = null     // 允许接入方接管日志

    fun d(msg: String, t: Throwable? = null) {
        if (level <= LogLevel.DEBUG) {
            delegate?.log(LogLevel.DEBUG, TAG, msg, t)
                ?: Log.d(TAG, msg, t)
        }
    }
    // ...
}

// 2. 接入方可以接管日志
interface LogDelegate {
    fun log(level: LogLevel, tag: String, msg: String, throwable: Throwable?)
}

MySdk.init(context) {
    logger = object : LogDelegate {
        override fun log(level: LogLevel, tag: String, msg: String, t: Throwable?) {
            Timber.tag(tag).d(t, msg) // 接入方用自己的日志框架
        }
    }
    logLevel = if (BuildConfig.DEBUG) LogLevel.VERBOSE else LogLevel.WARN
}

// 3. Release 版本默认关闭详细日志
// 4. 日志中脱敏 (不打印 token/密码等)
fun maskToken(token: String): String {
    if (token.length <= 8) return "****"
    return token.take(4) + "****" + token.takeLast(4)
}

监控与回调

kotlin 复制代码
// ✅ 提供生命周期回调, 让接入方了解 SDK 状态
interface SdkStateListener {
    fun onInitialized()
    fun onError(error: SdkError)
    fun onNetworkStateChanged(connected: Boolean)
    fun onConfigUpdated(config: RemoteConfig)
}

// ✅ 提供统计信息
interface SdkMonitor {
    val pendingEventCount: Int
    val lastSyncTime: Long
    val errorCount: Int
    fun getDiagnosticInfo(): Map<String, Any>  // 排查问题用
}

// 排查问题时:
Log.d("Diag", MySdk.getDiagnosticInfo().toString())
// {version=2.1.0, events_pending=12, last_sync=2024-01-15T10:30:00,
//  errors_24h=3, network=WiFi, storage_used=1.2MB}

八、测试

erlang 复制代码
┌──────────────────────────────────────────────────────────────┐
│                     SDK 测试策略                              │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 单元测试 (80%+覆盖率)                                    │
│     ├── 核心逻辑: 事件处理/序列化/去重/采样                   │
│     ├── Mock 网络/存储/Context                               │
│     └── 边界条件: 空值/超大数据/并发                           │
│                                                              │
│  2. 集成测试                                                  │
│     ├── 端到端: 从 API 调用到数据上报                         │
│     ├── 生命周期: Activity/Fragment 各生命周期                │
│     └── 配置变更: 旋转屏幕/多窗口                             │
│                                                              │
│  3. 兼容性测试                                                │
│     ├── 多 Android 版本 (API 21~34)                          │
│     ├── 多厂商 (Samsung/Xiaomi/Huawei/Oppo)                 │
│     ├── 多分辨率/屏幕尺寸                                    │
│     └── 与主流 SDK 共存 (Firebase/友盟/Bugly)                │
│                                                              │
│  4. 性能测试                                                  │
│     ├── 启动时间影响 (< 5ms 同步初始化)                      │
│     ├── 内存占用 (< 5MB 常驻)                                │
│     ├── CPU 占用 (< 1% 平均)                                 │
│     ├── 电量消耗 (不显著增加)                                 │
│     └── 包体积 (< 500KB)                                     │
│                                                              │
│  5. 稳定性测试                                                │
│     ├── Monkey 测试 (长时间随机操作)                          │
│     ├── 弱网测试 (2G/断网/网络切换)                           │
│     ├── 内存不足场景                                          │
│     └── 进程杀死/恢复                                        │
│                                                              │
│  6. 提供 Mock / 测试工具                                      │
│     ├── MySdk.setTestMode(true) → 数据不上报                 │
│     ├── FakeMySdk 实现 → 接入方方便写测试                    │
│     └── 内存存储替代磁盘存储 → 测试不留痕                    │
│                                                              │
└──────────────────────────────────────────────────────────────┘
kotlin 复制代码
// ✅ 提供测试辅助
// 让接入方容易 Mock SDK
interface MySdk {
    fun track(event: String)
    // ...
}

// 提供假实现
class FakeMySdk : MySdk {
    val trackedEvents = mutableListOf<String>()

    override fun track(event: String) {
        trackedEvents.add(event) // 记录而不真正上报
    }
}

// 接入方测试
@Test
fun `purchase should track event`() {
    val fakeSdk = FakeMySdk()
    MySdk.setInstance(fakeSdk) // 测试时替换

    viewModel.onPurchase()

    assertEquals("purchase", fakeSdk.trackedEvents.last())
}

九、版本管理与发布

yaml 复制代码
┌──────────────────────────────────────────────────────────────┐
│                   版本管理策略                                 │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  语义化版本: MAJOR.MINOR.PATCH                               │
│    MAJOR: 不兼容的 API 变更 (1.x → 2.0)                     │
│    MINOR: 向后兼容的功能新增 (2.1 → 2.2)                    │
│    PATCH: 向后兼容的 Bug 修复 (2.2.0 → 2.2.1)              │
│                                                              │
│  API 废弃策略:                                                │
│    1. 标记 @Deprecated + 提供替代方案                         │
│    2. 至少保留 2 个大版本 (1.x 废弃 → 3.0 才移除)            │
│    3. 迁移指南文档                                           │
│                                                              │
│  发布流程:                                                    │
│    开发 → 内部测试 → 灰度发布(1%) → 扩大灰度(10%)            │
│    → 全量发布 → 监控 → Hotfix(如需要)                        │
│                                                              │
│  分发方式:                                                    │
│    ✅ Maven Central / Google Maven                           │
│    ✅ 私有 Maven (公司内部)                                   │
│    ✅ AAR 直接分发 (离线场景)                                 │
│    ✅ GitHub Packages                                        │
│                                                              │
│  Changelog:                                                  │
│    每个版本详细列出:                                          │
│    - 新增功能                                                │
│    - 修复 Bug                                                │
│    - 破坏性变更 (Breaking Changes)                           │
│    - 废弃的 API                                              │
│    - 迁移指南                                                │
│                                                              │
└──────────────────────────────────────────────────────────────┘
kotlin 复制代码
// ✅ 优雅的 API 废弃

// v2.1: 原 API
fun trackEvent(name: String, properties: HashMap<String, String>) { ... }

// v2.2: 标记废弃, 提供新 API
@Deprecated(
    message = "使用 track() 替代, 支持更多参数类型",
    replaceWith = ReplaceWith("track(name, properties)"),
    level = DeprecationLevel.WARNING  // 先警告
)
fun trackEvent(name: String, properties: HashMap<String, String>) {
    track(name, properties.toMap())  // 内部桥接到新 API
}

// 新 API
fun track(name: String, properties: Map<String, Any> = emptyMap()) { ... }

// v3.0: 提升废弃级别或移除
@Deprecated(level = DeprecationLevel.ERROR)  // 编译错误, 强制迁移
fun trackEvent(...)

十、文档与集成体验

scss 复制代码
┌──────────────────────────────────────────────────────────────┐
│                    文档体系                                    │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 快速开始 (5 分钟集成指南)                                 │
│     ├── 一行依赖配置                                         │
│     ├── 一行初始化代码                                       │
│     ├── 一行功能调用                                         │
│     └── 运行效果截图/Gif                                     │
│                                                              │
│  2. API 文档 (KDoc/Javadoc)                                  │
│     ├── 每个公开类/方法都有文档注释                           │
│     ├── 参数说明 + 返回值说明                                │
│     ├── 代码示例                                             │
│     └── @throws 说明可能的异常                               │
│                                                              │
│  3. 进阶指南                                                  │
│     ├── 自定义配置                                           │
│     ├── 高级功能                                             │
│     ├── 最佳实践                                             │
│     └── 性能优化建议                                         │
│                                                              │
│  4. 迁移指南                                                  │
│     ├── 版本间 API 变化对比                                   │
│     ├── 自动迁移脚本 (如可能)                                 │
│     └── 常见迁移问题 FAQ                                     │
│                                                              │
│  5. 示例项目                                                  │
│     ├── 最小集成示例 (sample-minimal)                        │
│     ├── 完整功能示例 (sample-full)                           │
│     ├── Java 示例 + Kotlin 示例                              │
│     └── 各种架构示例 (MVVM/MVI/Compose)                      │
│                                                              │
│  6. 常见问题 FAQ                                              │
│     ├── 集成问题 (依赖冲突/混淆/多模块)                       │
│     ├── 运行时问题 (崩溃/ANR/内存)                           │
│     └── 错误码速查表                                         │
│                                                              │
│  7. Changelog                                                │
│     每个版本的详细变更记录                                     │
│                                                              │
└──────────────────────────────────────────────────────────────┘
kotlin 复制代码
// ✅ 优秀的 KDoc 示例
/**
 * 追踪用户事件。
 *
 * 事件会先缓存在本地, 达到阈值后批量上报。
 * 可以调用 [flush] 强制立即上报。
 *
 * **线程安全**: 可以在任意线程调用。
 *
 * @param eventName 事件名称, 不能为空, 长度不超过 128 字符。
 *   建议使用 snake_case 命名, 如 "button_clicked"。
 * @param properties 事件属性, key 不超过 64 字符, value 支持
 *   String/Int/Long/Double/Boolean 类型。
 *   默认为空。
 *
 * @throws SdkError.NotInitialized 如果 SDK 未初始化
 *
 * @sample com.mysdk.samples.TrackingSample.trackPurchaseEvent
 *
 * @see flush
 * @see setUserId
 * @since 2.0.0
 */
fun track(
    eventName: String,
    properties: Map<String, Any> = emptyMap()
)

十一、完整 SDK 架构示例

csharp 复制代码
mysdk/
├── mysdk-core/                          ← 核心模块
│   ├── src/main/java/com/mysdk/
│   │   ├── MySdk.kt                     ← 公开: 唯一入口
│   │   ├── Config.kt                    ← 公开: 配置
│   │   ├── SdkError.kt                  ← 公开: 错误类型
│   │   ├── EventListener.kt             ← 公开: 回调接口
│   │   │
│   │   └── internal/                    ← 内部实现
│   │       ├── MySdkImpl.kt             ← 核心实现
│   │       ├── SafeExecutor.kt          ← 异常兜底
│   │       ├── SdkLogger.kt             ← 日志
│   │       │
│   │       ├── event/
│   │       │   ├── EventQueue.kt        ← 事件队列
│   │       │   ├── EventBatcher.kt      ← 批量处理
│   │       │   └── EventSerializer.kt   ← 序列化
│   │       │
│   │       ├── network/
│   │       │   ├── HttpClient.kt        ← 网络接口
│   │       │   ├── DefaultHttpClient.kt ← 默认实现
│   │       │   └── RetryPolicy.kt       ← 重试策略
│   │       │
│   │       ├── storage/
│   │       │   ├── Storage.kt           ← 存储接口
│   │       │   ├── SpStorage.kt         ← SP 实现
│   │       │   └── DbStorage.kt         ← 数据库实现
│   │       │
│   │       └── lifecycle/
│   │           ├── AppStateTracker.kt   ← 前后台监听
│   │           └── NetworkMonitor.kt    ← 网络状态
│   │
│   ├── proguard-rules.pro               ← 混淆规则 (打入AAR)
│   └── consumer-rules.pro               ← 消费者混淆规则
│
├── mysdk-okhttp/                        ← OkHttp 适配器 (可选)
│   └── OkHttpClientAdapter.kt
│
├── mysdk-compose/                       ← Compose 扩展 (可选)
│   └── SdkComposables.kt
│
├── sample/                              ← 示例 App
│   ├── sample-java/
│   └── sample-kotlin/
│
└── docs/                                ← 文档
    ├── quick-start.md
    ├── api-reference.md
    ├── migration-guide.md
    └── changelog.md

十二、核心 Checklist

scss 复制代码
┌──────────────────────────────────────────────────────────────────┐
│                   SDK 设计 Checklist                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  API 设计                                                        │
│  □ 最简使用不超过 3 行代码                                        │
│  □ 公开 API 尽量少, 内部 internal/private                        │
│  □ 返回接口不返回实现类                                           │
│  □ 命名一致, 风格统一                                             │
│  □ 错误类型清晰, 带指导性错误信息                                  │
│  □ 支持 Java 和 Kotlin 调用                                      │
│  □ 提供回调/协程/Flow 多种方式                                    │
│                                                                  │
│  稳定性                                                          │
│  □ SDK 异常不会导致宿主崩溃                                       │
│  □ 所有公开 API 线程安全                                          │
│  □ 参数校验 + 友好错误提示                                        │
│  □ 防重复初始化                                                   │
│  □ 未初始化时有明确提示而非崩溃                                    │
│                                                                  │
│  性能                                                            │
│  □ 同步初始化 < 5ms                                               │
│  □ 包体积 < 500KB (核心)                                         │
│  □ 常驻内存 < 5MB                                                │
│  □ 不影响 App 启动速度                                            │
│  □ 批量处理, 减少网络/磁盘操作                                    │
│                                                                  │
│  兼容性                                                          │
│  □ minSdk 21+                                                    │
│  □ 依赖最小化, 无版本冲突                                         │
│  □ 资源名加前缀防冲突                                             │
│  □ ProGuard 规则打入 AAR                                         │
│                                                                  │
│  安全与隐私                                                      │
│  □ HTTPS + 证书锁定                                              │
│  □ 敏感数据加密存储                                               │
│  □ 日志脱敏                                                      │
│  □ 隐私合规 (同意前不采集/可删除/可关闭)                           │
│  □ 权限最小化                                                    │
│                                                                  │
│  可观测性                                                        │
│  □ 分级日志 + 可接管                                              │
│  □ Debug/Release 日志级别不同                                    │
│  □ 提供诊断信息接口                                               │
│  □ 提供状态回调                                                   │
│                                                                  │
│  发布维护                                                        │
│  □ 语义化版本                                                    │
│  □ Changelog                                                     │
│  □ API 废弃有过渡期                                               │
│  □ 发布到 Maven 仓库                                             │
│  □ 完整文档 + 示例项目                                            │
│                                                                  │
│  测试                                                            │
│  □ 单元测试 > 80% 覆盖                                           │
│  □ 提供 Fake/Mock 实现给接入方测试                                │
│  □ 提供测试模式 (数据不真正上报)                                   │
│  □ 兼容性测试覆盖主流机型                                         │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘
相关推荐
Dawson Zhu3 分钟前
基于 OKF 知识图谱的 Text2SQL 领域知识注入实践——半导体晶圆厂数据资产知识库 MVP 剖析
人工智能·语言模型·架构·aigc·agi
晚安日记wanna17 分钟前
批量请求失败只弹一个 Toast面试官想听五层
前端·面试·架构
大模型码小白21 分钟前
告别造假数据,直接连数据库查真实时序数据喂给 TimechoAI 大模型
java·数据库·人工智能·microsoft·架构
晚安日记wanna25 分钟前
大表 DDL 面试翻车现场Online DDL 为什么还会锁死业务
数据库·面试·架构
晚安日记wanna27 分钟前
MySQL 主从延迟别只答并行复制
数据库·面试·架构
狼与自由1 小时前
MCP协议理解
人工智能·架构
天远API3 小时前
零信任架构实战:基于天远单人婚姻查询构建自动化联合贷款审计网关
java·人工智能·架构·自动化
海宇AI3 小时前
零信任架构实战:基于海宇公安二要素认证即时版构建自动化灵活用工准入网关
运维·人工智能·架构·自动化
荆棘鸟智能3 小时前
AI算法持续学习与迭代怎么做?从数据闭环到灰度发布的MLOps工程实践
人工智能·算法·架构·边缘计算
️公子5 小时前
DeepSeek V4.1 Flash 今日接管 Pro 流量:552B MoE + 非对称架构,Agent 推理成本怎么砍?
架构·开源·大模型·api·agent·deepseek