跳到主要内容

设计动态配置系统

问题

如何设计一个 Android 动态配置系统(Remote Config),实现不发版即可动态调整 App 行为?

答案

整体架构

SDK 设计

class RemoteConfig private constructor(context: Context) {
private val memoryCache = ConcurrentHashMap<String, ConfigValue>()
private val diskCache = ConfigDiskCache(context)
private val defaults = mutableMapOf<String, Any>()

// 三级回退:内存 → 磁盘 → 默认值
fun getString(key: String, default: String = ""): String {
return memoryCache[key]?.asString()
?: diskCache.get(key)?.asString()
?: (defaults[key] as? String)
?: default
}

fun getBoolean(key: String, default: Boolean = false): Boolean {
return memoryCache[key]?.asBoolean()
?: diskCache.get(key)?.asBoolean()
?: (defaults[key] as? Boolean)
?: default
}

// 拉取最新配置
suspend fun fetch() {
val response = api.fetchConfigs(
appVersion = BuildConfig.VERSION_NAME,
deviceId = DeviceId.get(),
)
// 更新内存和磁盘缓存
response.configs.forEach { (key, value) ->
memoryCache[key] = value
}
diskCache.putAll(response.configs)
}

// 设置默认值(App 启动时调用)
fun setDefaults(map: Map<String, Any>) {
defaults.putAll(map)
}

companion object {
@Volatile private var instance: RemoteConfig? = null

fun getInstance(context: Context): RemoteConfig {
return instance ?: synchronized(this) {
instance ?: RemoteConfig(context.applicationContext).also {
instance = it
}
}
}
}
}

更新策略

策略实现时效性
定时轮询WorkManager 每 4h 拉一次小时级
冷启动拉取Application.onCreate 后台 fetch启动时生效
推送触发FCM/厂商推送 → 触发 fetch分钟级
长连接推送WebSocket 直推配置变更秒级
配置生效时机

关键问题:配置拉取到后立即生效还是下次启动生效

  • 立即生效:适合开关类配置(Feature Flag)
  • 下次生效:适合 UI 布局等需要整体刷新的配置

推荐用 fetchAndActivate() + fetch() 两种 API 分别对应两种场景。

灰度与条件下发

// 服务端配置条件下发规则
data class ConfigRule(
val key: String,
val value: Any,
val conditions: List<Condition> // 满足所有条件才下发
)

data class Condition(
val type: ConditionType, // APP_VERSION / OS_VERSION / DEVICE_ID / PERCENT
val operator: Operator, // EQ / GTE / LTE / IN / REGEX
val value: String
)

// 例:新功能只对 versionCode >= 100 且灰度 10% 用户开放
// conditions: [
// { type: APP_VERSION, op: GTE, value: "100" },
// { type: PERCENT, op: LTE, value: "10" }
// ]

常见面试问题

Q1: 配置拉取失败怎么办?

答案

采用三级回退策略确保 App 永远不会因为配置问题崩溃

  1. 内存缓存 — 上次成功拉取的值
  2. 磁盘缓存 — 上次持久化的值(跨进程重启仍有效)
  3. 代码默认值setDefaults() 设置的兜底值

网络失败时静默使用缓存,不影响用户体验,后台重试即可。

Q2: 如何实现配置的 A/B 测试?

答案

对同一个配置 key 设置多个变体(variants),用户按设备 ID hash 分桶:

配置 key: "new_checkout_flow"
变体 A (50%): true → 新流程
变体 B (50%): false → 旧流程

客户端 SDK 上报设备信息,服务端根据分桶策略返回对应变体值。结合埋点数据就能对比两组用户的转化率。

相关链接