Shiply Android 远程配置 SDK 集成
一、接入SDK
1. 在项目根目录下的 build.gradle 中配置 Maven 仓库
allprojects {
repositories {
maven { url "https://maven.cnb.cool/tencent-tds/shiply-public/-/packages/" }
}
}
2. 在主 module 的 build.gradle 中添加运行时 SDK
dependencies {
implementation 'com.tencent.shiply:rdelivery:1.3.35-RC02'
}
3. 初始化 SDK,建议在 Application.onCreate 方法中初始化
String hostAppId = "testAppId"; // shiply 产品 appid
String hostAppKey = "testAppKey"; // shiply 产品 appkey
String hostPacketName = "com.tencent.shiply.demo"; // 宿主的 app 包名
String hostAppVersion = "1.0.0"; // 宿主的 app 版本
String userId = "123321"; // 用户id
boolean is64BitCpu = false; // 当前手机是否是64位cpu
boolean isDebugPackage = false; // 宿主是否是debug包
/**
* 本地配置开关更新策略
*
* 固定间隔拉取策略会计算APP在后台的时长。
* APP从后台切换到前台时,如果APP在后台的时长超过拉取时间间隔,直接触发拉取;
* 如果还没到时间间隔,按照剩余的时间间隔发起定时.
* 为了节省请求量,建议业务方只设置 START_UP 和 PERIODIC 两种策略,更新时间间隔建议大于1小时。
*/
int strategy = RDeliverySetting.UpdateStrategy.START_UP.getValue() // sdk 启动时拉取全量配置
| RDeliverySetting.UpdateStrategy.PERIODIC.getValue() // 按照固定时间间隔,周期拉取全量配置
| RDeliverySetting.UpdateStrategy.HOT_RELOAD.getValue() // app 切换后台超过30秒,再切回前台时拉取全量配置
| RDeliverySetting.UpdateStrategy.NETWORK_RECONNECT.getValue(); // 断网重连时拉取全量配置
int updateInterval = 60 * 60 * 3;
RDeliverySetting setting = new RDeliverySetting.Builder()
.setAppId(hostAppId)
.setAppKey(hostAppKey)
.setUserId(userId)
.setIsDebugPackage(isDebugPackage)
.setUpdateStrategy(strategy)
.setUpdateInterval(updateInterval)
.setBundleId(hostPacketName)
.setHostAppVersion(hostAppVersion)
.setDevModel(Build.MODEL)
.setDevManufacturer(Build.MANUFACTURER)
.setAndroidSystemVersion(String.valueOf(Build.VERSION.SDK_INT))
.setIs64BitCpu(is64BitCpu)
.setEnableDetailLog(true) // 是否要打印详细日志,为了方便排查问题,建议开启,当对配置保密性要求高时可以关闭
.build();
// 创建可以依赖注入的对象,如:网络请求、数据存储、任务调度、日志接出
DependencyInjector injector = new DependencyInjector(
new HttpsURLConnectionNetwork(this), // 网络接口的默认实现
new MmkvStorageFactory(), // 存储接口的默认实现
new HandlerTask(), // 任务调度接口的默认实现
new SystemLog() // 日志接口的默认实现,建议对接到业务方自己的日志接口,可以继承AbsLog类来快速实现
);
// 创建配置开关实例
RDelivery rDelivery = RDelivery.create(this, setting, injector, null);
二、 使用SDK
1. 通过 RDelivery 实例获取配置开关内容
String configSwitchName = "testName"; //配置开关名称
String defaultConfigValue = "";
String configValue = rDelivery.getStringByKey(configSwitchName, defaultConfigValue, true);//获取配置值
boolean defaultSwitchValue = false;
boolean switchValue = rDelivery.isOnByKey(configSwitchName, defaultSwitchValue, true);//获取开关值
RDeliveryData defaultConfigSwitchData = null;
RDeliveryData configSwitchData = rDelivery.getRDeliveryDataByKey(configSwitchName, defaultConfigSwitchData, true);//从内存缓存获取配置开关数据
RDeliveryData configSwitchData2=rDelivery.getRDeliveryDataByKeyFromDisc(configSwitchName);//直接从磁盘读取配置开关数据
Map<String, RDeliveryData> resultList=rDelivery.getAllRDeliveryData();
通过 rDelivery.getStringByKey 获取配置值,当远端不存在这个配置开关 key 时,会返回 defaultConfigValue; readDiskWhenDataNotInited 参数为 true 时,如果本地内存数据未初始化好,会直接从磁盘读取数据;
通过 rDelivery.isOnByKey 获取开关值,当远端不存在这个配置开关 key 或者前端页面这个配置开关 key 的开关值为「未设置」时,会返回 defaultSwitchValue; readDiskWhenDataNotInited 参数为 true 时,如果本地内存数据未初始化好,会直接从磁盘读取数据;
通过 rDelivery.getRDeliveryDataByKey 从内存获取配置开关数据,RDeliveryData 中包含了同一个配置开关 key 的配置值和开关值,当远端不存在这个配置开关 key 时,会返回 defaultConfigSwitchData; readDiskWhenDataNotInited 参数为 true 时,如果本地内存数据未初始化好,会直接从磁盘读取数据;
通过 rDelivery.getRDeliveryDataByKeyFromDisc 直接从磁盘读取配置开关数据;创建RDelivery实例时会异步将磁盘数据加载到内存,配置开关数量越多,这个异步加载耗时也会越大,这个过程中业务如果查询开关配置只会获取到默认值,为了解决这个问题,提供了这个直接从磁盘读取配置开关的接口;这个接口涉及到I/O,需要慎重使用,只推荐在APP启动时就需要读取配置开关的场景才使用;
通过 rDelivery.getAllRDeliveryData 获取所有的配置开关数据;
2. 手动拉取 RDelivery 远程配置开关
FullReqResultListener fullReqResultListener = new FullReqResultListener() {
@Override
public void onSuccess() {}
@Override
public void onFail(@NotNull String reason) {}
};
rDelivery.requestFullRemoteData(fullReqResultListener);//请求全量配置
long sceneId = 123;
BatchReqResultListener batchReqResultListener = new BatchReqResultListener() {
@Override
public void onSuccess(@NotNull List<RDeliveryData> datas) {}
@Override
public void onFail(@NotNull String reason) {}
};
rDelivery.requestBatchRemoteDataByScene(sceneId, batchReqResultListener);//请求一组配置
String configSwitchKey = "testKey";
SingleReqResultListener singleReqResultListener = new SingleReqResultListener() {
@Override
public void onSuccess(@Nullable RDeliveryData data) {}
@Override
public void onFail(@NotNull String reason) {}
};
rDelivery.requestSingleRemoteDataByKey(configSwitchName, singleReqResultListener);//请求单个配置
List<String> keyList = new ArrayList<String>();
keyList.add("key1");
keyList.add("key2");
MultiKeysReqResultListener multiKeysReqResultListener = new MultiKeysReqResultListener() {
@Override
public void onSuccess(List<RDeliveryData> datas) {}
@Override
public void onFail(@NotNull String reason) {}
};
rDelivery.requestMultiRemoteData(keyList, multiKeysReqResultListener);
DataChangeListener dataChangeListener = new DataChangeListener() {
@Override
public void onDataChange(@NotNull String key,@Nullable RDeliveryData oldData, @Nullable RDeliveryData newData){}
};
rDelivery.addDataChangeListener(dataChangeListener);//监听数据变化
SingleDataChangeListener singleDataChangeListener=new SingleDataChangeListener(){
@Override
public void onDataChange(@Nullable RDeliveryData oldData,@Nullable RDeliveryData newData){}
};
rDelivery.addDataChangeListenerByKey("cfg",singleDataChangeListener);
rDelivery.setFullReqResultListener(new FullReqResultListener(){
@Override
public void onSuccess(){}
@Override
public void onFail(@NonNull String reason){}
});
通过 rDelivery.requestFullRemoteData 请求全量配置;
通过 rDelivery.requestBatchRemoteDataByScene 请求一组配置,sceneId 是前端页面中创建的场景的 id,绑定了这个场景的所有配置开关任务都会返回;
通过 rDelivery.requestSingleRemoteDataByKey 请求单个配置, configSwitchName 是配置开关的名称;
通过 rDelivery.requestMultiRemoteData 请求多个配置, keyList 是配置开关的名称列表;
通过 rDelivery.addDataChangeListener 监听配置开关数据的变化,当数据有更新时会回调 dataChangeListener#onDataChange 接口;
通过 rDelivery.addDataChangeListenerByKey 监听单个配置开关数据的变化,当"cfg" 配置数据有更新时会回调 singleDataChangeListener#onDataChange 接口;
通过 rDelivery.setFullReqResultListener 监听 SDK 全量配置拉取请求的回包结果;
三、注意事项与常见问题
systemId 说明
Android RDelivery 通过 RDeliverySetting.Builder.setSystemId(String) 支持 systemId。默认值为 "10001"(Config 子系统)。当 RDelivery 实例需要与 ResHub 共享时,必须设置 setSystemId("10010")。更多 systemId 说明见 通用概念。
注意事项
- 磁盘直读仅启动用:
getRDeliveryDataByKeyFromDisc涉及 I/O,运行期用内存接口(getRDeliveryDataByKey)。 readDiskWhenDataNotInited:true 时内存未初始化好会直读磁盘,仅启动场景需要。- 定时策略后台计时:
PERIODIC/HOT_RELOAD会计算 APP 在后台时长,后台超间隔切前台直接触发。 - 建议策略:只设
START_UP+PERIODIC,间隔大于 1 小时。 - 隐私信息注入:
devModel/devManufacturer/系统版本需业务提供。
RDeliverySetting.Builder 关键参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
appId / appKey | String | 是 | 应用凭证 |
bundleId | String | 是 | 包名 |
userId | String | 否 | 用户 ID(默认 "") |
qimei | String? | 否 | 设备 Qimei |
logicEnvironment | String? | 否 | 逻辑环境 ID |
updateStrategy | Int? | 否 | 更新策略位运算 |
updateInterval | Int | 否 | 定时间隔秒,默认 14400(4h) |
hostAppVersion | String | 否 | 宿主版本 |
devModel / devManufacturer | String | 否 | 型号/厂商(需注入) |
isDebugPackage | Boolean | 否 | debug 包 |
enableDetailLog | Boolean | 否 | 详细日志,默认 true |
systemId | String? | 否 | 子系统 ID:默认 "10001",ResHub 共享用 "10010",TAB 模式 "10013" |
UpdateStrategy 枚举
| 枚举 | 说明 |
|---|---|
START_UP | 启动时更新 |
PERIODIC | 定时更新 |
HOT_RELOAD | 后台超 30 秒切前台触发 |
NETWORK_RECONNECT | 网络恢复触发 |
依赖注入
DependencyInjector(4 个必填注入项):
| 参数 | 接口 | 默认实现 |
|---|---|---|
netInterface | IRNetwork | HttpsURLConnectionNetwork |
storageFactory | IRStorage.IRStorageFactory | MmkvStorageFactory |
taskInterface | IRTask | HandlerTask |
logInterface | IRLog | SystemLog |
监听器接口
interface DataChangeListener {
fun onDataChange(key: String, oldData: RDeliveryData?, newData: RDeliveryData?)
}
interface SingleDataChangeListener {
fun onDataChange(oldData: RDeliveryData?, newData: RDeliveryData?)
}
interface UserEventListener {
fun onQueryLocalData(key: String, data: RDeliveryData)
}
interface LocalDataInitListener {
fun onInitFinish()
}
移除监听
rDelivery.removeDataChangeListener(listener) // 按对象引用移除
rDelivery.removeSingleDataChangeListener(key) // 按 key 移除(注意:入参是 key,不是 listener ref)
rDelivery.removeUserEventListener(listener) // 移除 UserEventListener
rDelivery.setFullReqResultListener(null) // setFullReqResultListener 无 remove 方法,传 null 清除
线程说明
RDelivery 没有任何 @MainThread/@WorkerThread 注解,也未暴露回调主线程配置。不能假设 listener 回调或请求回调运行在任何特定线程。
监听器注意事项
addSingleDataChangeListenerByKey对应removeSingleDataChangeListener(key)——命名不对称:add 用ByKey(key, listener),remove 用(key)。setFullReqResultListener(listener)没有对应的 remove 方法——传null清除。addDataChangeListener/removeDataChangeListener支持多个监听器同时注册。
切换用户/环境
// 切换用户(SDK 会自动重新拉取配置)
rDelivery.switchUserId("newUserId", object : LocalDataInitListener {
override fun onInitFinish() { }
})
// 切换环境(""=正式, "1"=测试)
rDelivery.switchEnvironment("1", object : LocalDataInitListener {
override fun onInitFinish() { }
})
// 切换服务类型
rDelivery.switchServerType(BaseProto.ServerType.TEST, object : LocalDataInitListener {
override fun onInitFinish() { }
})
其他 API
rDelivery.clearAllCache()
rDelivery.deleteRdeliveryDataByKey("my_key")
val keys: Set<String> = rDelivery.getAllKeys()
rDelivery.setCustomParam("key", "value")
rDelivery.setCustomParam("key", null) // 清除
rDelivery.updateQimei("newQimei")
rDelivery.addUserToExperienceList(qrCodeContent, customIDType, customIDValue, listener)
rDelivery.reportBusinessEvent(eventName, cost, errCode, extInfo, reportImmediately)
val tags: String = rDelivery.getHitSubTaskTags()
常见问题
配置读取返回 null/默认值
- 确认
RDelivery.create()初始化成功。 - 使用
requestFullRemoteData(listener)主动拉取配置后再读取。 - 检查
RDeliverySetting.Builder.setLogicEnvironment(envId)环境 ID 是否与平台一致(""=正式,"1"=测试)。
收不到配置变更事件
- 确保
addDataChangeListener已注册,监听接口签名正确。 - 检查 key 名称与 Shiply 平台配置的 key 完全一致(含大小写)。
配置更新未生效
- 检查
updateStrategy位掩码是否包含所需策略(如START_UP、PERIODIC)。 - 若使用
PERIODIC,检查updateInterval是否设置过小被 SDK 最小值兜底。 - 手动调用
requestFullRemoteData(listener)主动拉取验证。
磁盘读取 vs 内存读取
- 运行期使用
getRDeliveryDataByKey(key)(内存读取)。 - 仅在启动阶段数据未初始化完时使用
getRDeliveryDataByKeyFromDisc(key)(磁盘读取,涉及 I/O)。
文档与实现差异
- 版本号:官方文档写
1.3.38-RC02,config.gradle 实际为 1.3.38-RC05。以 config.gradle 为准。 - 文档其余 API 与源码一致。