如何通过 Firebase Remote Config 实现非代码层面的功能开关?
解读
国内面试中,这道题考察的是“灰度/热修/运营配置”落地能力,而非单纯调用 SDK。面试官想确认:
- 你是否真正理解 Remote Config 的“服务端控制→客户端无感更新”本质;
- 能否在合规(工信部 164 号文、个人信息保护)前提下,把开关与业务解耦,做到“不发版、不强制更新、可回滚”;
- 是否具备“兜底、缓存、一致性、可观测”的工程化思维,而不是 Demo 级调用。
知识点
- Firebase 中国加速节点与代理配置:国内需接入上海/香港 CDN,并在 AndroidManifest 中声明
firebase_remote_config_auto_fetch为 false,避免首次拉取被 GFW 重置。 - 服务端参数结构设计:采用“三段式” key =
module_subModule_switch,value 用受控 JSON(如{"on":true,"whiteList":[...],"percent":10}),既支持布尔,也支持白名单+百分比灰度。 - 客户端双缓存策略:应用启动时优先使用
FirebaseRemoteConfig#activate()上一次缓存,再异步fetch()新值;若 500 ms 内拉取失败,则回滚到本地defaults.xml,保证首帧可用。 - 合规与隐私:Remote Config 不携带个人敏感字段,但白名单灰度需用 MD5 后的设备标识;在《隐私政策》中显式列出“我们使用 Firebase 进行功能配置”,并通过 MIIT 备案。
- 一致性保障:对同一用户同一版本,使用
Settings.Secure.ANDROID_ID做一致性哈希,确保灰度结果不跳变;同时记录lastFetchTime与firebaseInstallationsId,方便后台审计。 - 可观测与回滚:在 APM(如腾讯 Bugly 或阿里 SLS)埋点
rc_key=xxx,rc_value=xxx,rc_fetch_latency=xxx,一旦崩溃率上涨,可在 Firebase Console 一键回滚,5 分钟内全网生效。 - 与 CI/CD 集成:在 GitLab-CI 中通过
firebase-tools命令行firebase remoteconfig:rollback --version-number $RC_VERSION实现自动化回滚;同时把参数表存到企业微信文档,运营可直接编辑,无需研发介入。
答案
-
工程配置
在build.gradle中引入国内 BOM:
implementation platform('com.google.firebase:firebase-bom:32.7.0')
implementation 'com.google.firebase:firebase-config'
并在Application#onCreate()初始化:
FirebaseApp.initializeApp(this)
val rc = Firebase.remoteConfig
rc.setConfigSettingsAsync(remoteConfigSettings {
minimumFetchIntervalInSeconds = if (BuildConfig.DEBUG) 0 else 3600
fetchTimeoutInSeconds = 5
})
rc.setDefaultsAsync(R.xml.remote_config_defaults) -
参数设计
以“短视频水印开关”为例,Console 新建参数shortVideo_watermark_switch,默认值 false,灰度规则:- 白名单:内部员工 200 台设备 IMEI(MD5)
- 百分比:10% 用户随机桶
- 版本号:≥ 7.30.0
服务端 JSON 值:{"on":true,"whiteList":["a35f2c...","e4bc1d..."],"percent":10}
-
客户端解析
在短视频拍摄模块注入 Repository:
class FeatureSwitchRepository @Inject constructor(
private val rc: FirebaseRemoteConfig
) {
fun isWatermarkEnabled(): Boolean {
val raw = rc.getString("shortVideo_watermark_switch")
val bean = Gson().fromJson(raw, WatermarkBean::class.java)
if (bean.whiteList.contains(md5(DeviceUtil.getIMEI()))) return true
val bucket = (DeviceUtil.getAndroidId().hashCode() and Int.MAX_VALUE) % 100
return bucket < bean.percent && bean.on
}
}
ViewModel 中通过StateFlow<Boolean>暴露,UI 层 Compose 直接collectAsState(),实现真正的非代码切换。 -
兜底与回滚
- 首次安装无网络:使用
defaults.xml中 false,保证水印关闭,符合工信部“最小可用”原则。 - 线上事故:Console 把
shortVideo_watermark_switch值改为{"on":false},5 分钟内所有客户端下次拉取生效;若需秒级回滚,调用 rollback API 并触发 Push 透传消息,客户端收到后立刻rc.activate().await()。
- 首次安装无网络:使用
-
合规埋点
在isWatermarkEnabled()返回值处埋点:
APM.onEvent("rc_switch", mapOf(
"key" to "shortVideo_watermark_switch",
"value" to result,
"fetch_latency" to (SystemClock.elapsedRealtime() - fetchStart)
))
数据上报走国内 SLS 通道,不含 IMEI 明文,满足《个人信息保护法》。
拓展思考
- 多进程场景:Remote Config 默认只在主进程拉取,若短视频模块运行在
:video独立进程,需在ContentProvider初始化时把主进程缓存序列化到 MMKV,子进程读取,避免重复 fetch。 - 与 Tinker 热修协同:若开关触发代码补丁下载,需保证补丁版本号也写入 Remote Config,防止“旧客户端 + 新补丁”导致 ABI 不一致。
- 离线优先策略:国内地铁、电梯场景网络不稳定,可结合 WorkManager 每 6 小时触发一次
fetch(),并设置isDeveloperMode=false,避免频繁请求被 Firebase 限流。 - A/B 测试升级:Remote Config 可与 Firebase A/B 或国内腾讯兔小巢对接,把开关升级为实验层,自动计算置信区间;但需注意实验参数不能超过 500 个,否则 Console 加载卡顿。
- 合规沙盒:Android 14 引入
READ_MEDIA_IMAGES新权限,若开关涉及权限弹窗,需在 Console 增加targetSdkVersion判断,避免低版本用户被误开权限请求,导致应用市场审核被拒。