SDK 设计核心关注
一、总览架构
┌──────────────────────────────────────────────────────────────────┐
│ SDK 设计关注维度 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ API 设计 │ │ 稳定性 │ │ 性能 │ │ 安全性 │ │
│ │ │ │ │ │ │ │ │ │
│ │ 易用性 │ │ 兼容性 │ │ 包体积 │ │ 隐私合规 │ │
│ │ 一致性 │ │ 容错 │ │ 内存 │ │ 数据安全 │ │
│ │ 可扩展 │ │ 隔离 │ │ 电量 │ │ 权限最小化│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 集成体验 │ │ 可观测性 │ │ 测试 │ │ 发布维护 │ │
│ │ │ │ │ │ │ │ │ │
│ │ 文档 │ │ 日志 │ │ 单元测试 │ │ 版本管理 │ │
│ │ 初始化 │ │ 监控 │ │ 集成测试 │ │ 灰度发布 │ │
│ │ 示例代码 │ │ 调试支持 │ │ Mock │ │ 向后兼容 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────────┘
二、API 设计(最核心)
1. 易用性 — 让接入方 5 分钟上手
MySdk.init(context)
MySdk.track("purchase", mapOf("price" to 99.9))
MySdk.builder(context)
.setAppKey("xxx")
.setDebug(BuildConfig.DEBUG)
.enableFeatureX()
.build()
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. 一致性 — 统一的设计范式
sdk.startTracking()
sdk.stopTracking()
sdk.isTracking()
sdk.addListener(listener)
sdk.removeListener(listener)
sdk.clearListeners()
Request.Builder()
.url("...")
.header("key", "value")
.build()
request {
url = "..."
header("key", "value")
}
sdk.init(key: String, secret: String, env: Int)
sdk.configure(Config.Builder().build())
sdk.setOptions { it.timeout = 30 }
3. 最少知识原则 — 隐藏内部实现
┌──────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────────────────────────────────────────┘
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 val eventQueue = EventQueue()
internal val networkClient = NetworkClient()
}
fun init(): MySdk
fun init(): MySdkImpl
4. 可扩展性 — 预留扩展点
interface MySdkConfig {
var httpClient: HttpClient?
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. 回调设计
interface OnResultListener {
fun onSuccess(data: Data)
fun onError(error: SdkError)
}
sdk.fetch(listener)
sdk.fetch(
onSuccess = { data -> },
onError = { error -> }
)
suspend fun fetch(): Result<Data>
fun observeState(): Flow<State>
fun observeState(): LiveData<State>
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. 错误设计
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)
}
三、稳定性
1. 容错 — 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)
}
}
}
class MySdkImpl : MySdk {
override fun track(event: String, params: Map<String, Any>) {
SafeExecutor.execute {
trackInternal(event, params)
}
}
}
2. 线程安全
class MySdkImpl : MySdk {
@Volatile
private var isInitialized = false
private val lock = Any()
override fun track(event: String, params: Map<String, Any>) {
synchronized(lock) {
checkInitialized()
eventQueue.enqueue(Event(event, params))
}
}
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)
}
}
private val sdkScope = CoroutineScope(
SupervisorJob() + Dispatchers.Default.limitedParallelism(1)
)
}
3. 兼容性
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 {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
compileOnly("com.squareup.okhttp3:okhttp:4.12.0")
}
android {
compileSdk = 34
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
依赖冲突解决策略:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 策略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. 隔离 — 不影响宿主
internal val sdkExecutor = ThreadPoolExecutor(
1, 2, 30, TimeUnit.SECONDS,
ArrayBlockingQueue(128),
NamedThreadFactory("mysdk"),
ThreadPoolExecutor.DiscardOldestPolicy()
)
private const val DB_NAME = "mysdk_internal.db"
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()
四、性能
1. 初始化性能 — 不能拖慢 App 启动
class MySdk {
companion object {
@Volatile
private var instance: MySdkImpl? = null
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) }
}
class MySdkInitializer : Initializer<MySdk> {
override fun create(context: Context): MySdk {
return MySdk.init(context)
}
override fun dependencies() = emptyList<Class<out Initializer<*>>>()
}
2. 包体积 — 越小越好
┌──────────────────────────────────────────────────────────────┐
│ 包体积优化策略 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 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 / 电量
private val cache = LruCache<String, ByteArray>(maxSize = 2 * 1024 * 1024)
fun onTrimMemory(level: Int) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
cache.evictAll()
bitmapPool.clear()
}
}
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)
}
}
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"sdk_sync",
ExistingPeriodicWorkPolicy.KEEP,
PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS).build()
)
五、安全性
┌──────────────────────────────────────────────────────────────┐
│ 安全性关注点 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 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 等不可重置标识符 │
│ │
└──────────────────────────────────────────────────────────────┘
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("已删除所有用户数据")
}
}
六、初始化设计
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
MySdk.init(this) {
appKey = "xxx"
debug = BuildConfig.DEBUG
}
}
}
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup">
<meta-data
android:name="com.mysdk.MySdkInitializer"
android:value="androidx.startup" />
</provider>
internal class MySdkAutoInitProvider : ContentProvider() {
override fun onCreate(): Boolean {
MySdk.init(context!!)
return true
}
}
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
}
}
七、可观测性
日志系统
enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }
object SdkLogger {
var level: LogLevel = LogLevel.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)
}
}
}
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
}
fun maskToken(token: String): String {
if (token.length <= 8) return "****"
return token.take(4) + "****" + token.takeLast(4)
}
监控与回调
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())
八、测试
┌──────────────────────────────────────────────────────────────┐
│ 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 实现 → 接入方方便写测试 │
│ └── 内存存储替代磁盘存储 → 测试不留痕 │
│ │
└──────────────────────────────────────────────────────────────┘
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())
}
九、版本管理与发布
┌──────────────────────────────────────────────────────────────┐
│ 版本管理策略 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 语义化版本: 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 │
│ - 迁移指南 │
│ │
└──────────────────────────────────────────────────────────────┘
fun trackEvent(name: String, properties: HashMap<String, String>) { ... }
@Deprecated(
message = "使用 track() 替代, 支持更多参数类型",
replaceWith = ReplaceWith("track(name, properties)"),
level = DeprecationLevel.WARNING // 先警告
)
fun trackEvent(name: String, properties: HashMap<String, String>) {
track(name, properties.toMap())
}
fun track(name: String, properties: Map<String, Any> = emptyMap()) { ... }
@Deprecated(level = DeprecationLevel.ERROR)
fun trackEvent(...)
十、文档与集成体验
┌──────────────────────────────────────────────────────────────┐
│ 文档体系 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 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 │
│ 每个版本的详细变更记录 │
│ │
└──────────────────────────────────────────────────────────────┘
fun track(
eventName: String,
properties: Map<String, Any> = emptyMap()
)
十一、完整 SDK 架构示例
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
┌──────────────────────────────────────────────────────────────────┐
│ 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 实现给接入方测试 │
│ □ 提供测试模式 (数据不真正上报) │
│ □ 兼容性测试覆盖主流机型 │
│ │
└──────────────────────────────────────────────────────────────────┘