当前 SDK 版本:v2.0.27
以主流 IDE(IntelliJ IDEA)为例,jar 包添加示例:

在成功添加 jar 包后,您需要通过 maven 管理添加必要依赖。
添加依赖方式:将以下代码添加至项目 pom.xml 中
注意
<dependencies> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.9</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.11.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.11.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.4</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0.1-jre</version> </dependency> <dependency> <groupId>com.github.zafarkhaja</groupId> <artifactId>java-semver</artifactId> <version>0.9.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.8</version> </dependency> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.0.1</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>2.5.0</version> </dependency> <dependency> <groupId>com.googlecode.protobuf-java-format</groupId> <artifactId>protobuf-java-format</artifactId> <version>1.4</version> </dependency> </dependencies>
进行数据接入上报时,您需要根据当前的环境类型和端类型确认您的数据上报地址,如果上报地址设置错误,后续会导致您无法查询到上报的数据。
注意
地址类型 | SaaS-云原生环境 | SaaS-云原生环境 | SaaS-非云原生环境 国内环境 | SaaS-非云原生 海外BytePlus环境 |
|---|---|---|---|---|
数据上报地址 |
| https://gator.uba.ap-southeast-1.volces.com | 无需手动配置 | https://mcs.tobsnssdk.com |
分流地址 | https://tab.volces.com | https://tab.ab.ap-southeast-1.volces.com | 无需手动配置 | https://tab.abtest.bytepluses.com |
私有化部署场景下,您需要获取部署私有化环境时,自行规划配置的数据上送地址。如您不清楚此地址,请联系您的项目经理或客户成功经理。
import com.bytedance.tester.AbClient; import com.bytedance.tester.model.User; import com.bytedance.tester.model.common.Variable; import com.bytedance.tester.abInfo.UserAbInfoHandler; import com.bytedance.tester.abInfo.MemoryHandler; import java.util.HashMap; public class Example { public static void main(String[] args) { /** 初始化ABTest分流类,appKey获取方式详见接口描述AbClient 初始化环境信息,其中: 1. appid,appkey为应用的标识,需要替换为真实的appkey和appid 2. meta_host: 获取meta信息的地址,各环境各地域的上送地址请参见上文的 获取数据上送地址 章节。 3. track_host: 事件上报地址,各环境各地域的上送地址请参见上文的 获取数据上送地址 章节; 4. setNewEnv(true): true: 代表SaaS-云原生环境, false: 代表SaaS-非云原生环境 **/ AbClient abClient = new AbClient.Builder("appkey", appid).setMetaHost("https://tab.volces.com").setTrackHost("https://gator.volces.com").setNewEnv(true).build(); // 开启debug模式,默认打印未命中实验的用户信息 abClient.setDebugMode(true); // 声明一个用户,decisionID用于分流,trackID用于事件上报 // decisionID: 本地分流用户标识,不用于事件上报,请替换为客户的真实用户标识 // trackID: 事件上报用户标识,用于事件上报,请替换为客户的真实用户标识 // add: 添加用户属性,仅用于分流,不随埋点上报 // build: 生成User对象 User user = new User.UserBuilder().create("decisionID", "trackID") .setDeviceId(6981329701821561868L) // 可选,非必要 .add("gender", "male") .add("phone", null) .add("is_vip", false) .add("version", "1.2.2") .add("age", 18) .build(); // 进组不出组内存实现接口,若用户不配置下列代码,则默认不开启“进组不出组”功能 // 持久化存储进组信息,请自行实现 UserAbInfoHandler 接口(推荐) // MemoryHandler为内存存储,仅用于测试,请勿在生产环境使用 UserAbInfoHandler memoryHandler = MemoryHandler.getInstance(); abClient.setUserAbInfoHandler(memoryHandler); // 推荐接口 String defaultValue = null; // 默认版本值,当分流未命中时返回该值,注意:返回值为包装后的对象,并非defaultValue本身,依然需要使用get方法获取 Variable variable1 = abClient.activate("variantKey", user, defaultValue); if(null == variable1) { return; } Object value1 = variable1.getValue(); if(null == value1) { return; } if(value1.equals("a")){ } else if(value1.equals("b")){ } } }
import com.bytedance.tester.AbClient; import com.bytedance.tester.model.User; import com.bytedance.tester.model.common.Variable; import com.bytedance.tester.abInfo.UserAbInfoHandler; import com.bytedance.tester.abInfo.MemoryHandler; import java.util.HashMap; public class Example { public static void main(String[] args) { // 初始化ABTest分流类,appKey获取方式详见接口描述AbClient AbClient abClient = new AbClient.Builder("2b47a1f318d78fd71854815*********").build(); // 开启debug模式,默认打印未命中实验的用户信息 abClient.setDebugMode(true); // 声明一个用户,decisionID用于分流,trackID用于事件上报 // decisionID: 本地分流用户标识,不用于事件上报,请替换为客户的真实用户标识 // trackID: 事件上报用户标识,用于事件上报,请替换为客户的真实用户标识 // add: 添加用户属性,仅用于分流,不随埋点上报 // build: 生成User对象 User user = new User.UserBuilder().create("decisionID", "trackID") .setDeviceId(6981329701821561868L) // 可选,非必要 .add("gender", "male") .add("phone", null) .add("is_vip", false) .add("version", "1.2.2") .add("age", 18) .build(); // 进组不出组内存实现接口,若用户不配置下列代码,则默认不开启“进组不出组”功能 // 持久化存储进组信息,请自行实现 UserAbInfoHandler 接口(推荐) // MemoryHandler为内存存储,仅用于测试,请勿在生产环境使用 UserAbInfoHandler memoryHandler = MemoryHandler.getInstance(); abClient.setUserAbInfoHandler(memoryHandler); // 推荐接口 String defaultValue = null; // 默认版本值,当分流未命中时返回该值,注意:返回值为包装后的对象,并非defaultValue本身,依然需要使用get方法获取 Variable variable1 = abClient.activate("variantKey", user, defaultValue); if(null == variable1) { return; } Object value1 = variable1.getValue(); if(null == value1) { return; } if(value1.equals("a")){ } else if(value1.equals("b")){ } } }
import com.bytedance.tester.AbClient; import com.bytedance.tester.model.User; import com.bytedance.tester.model.common.Variable; import com.bytedance.tester.abInfo.UserAbInfoHandler; import com.bytedance.tester.abInfo.MemoryHandler; import java.util.HashMap; public class Example { public static void main(String[] args) { /** 初始化ABTest分流类,appKey获取方式详见接口描述 AbClient初始化环境信息,其中: 1. meta_host: 获取meta信息的地址,私有化用户按需修改; 2. track_host: 事件上报地址,私有化用户按需修改; 3.setOnpremise(true): 是否指定上述两个域名,如果指定设为true **/ AbClient abClient = new AbClient.Builder("2b47a1f318d78fd71854815*********").setMetaHost("xxx").setTrackHost("xxxx").setOnpremise(true).build(); // 开启debug模式,默认打印未命中实验的用户信息 abClient.setDebugMode(true); // 声明一个用户,decisionID用于分流,trackID用于事件上报 // decisionID: 本地分流用户标识,不用于事件上报,请替换为客户的真实用户标识 // trackID: 事件上报用户标识,用于事件上报,请替换为客户的真实用户标识 // add: 添加用户属性,仅用于分流,不随埋点上报 // build: 生成User对象 User user = new User.UserBuilder().create("decisionID", "trackID") .setDeviceId(6981329701821561868L) // 可选,非必要 .add("gender", "male") .add("phone", null) .add("is_vip", false) .add("version", "1.2.2") .add("age", 18) .build(); // 进组不出组内存实现接口,若用户不配置下列代码,则默认不开启“进组不出组”功能 // 持久化存储进组信息,请自行实现 UserAbInfoHandler 接口(推荐) // MemoryHandler为内存存储,仅用于测试,请勿在生产环境使用 UserAbInfoHandler memoryHandler = MemoryHandler.getInstance(); abClient.setUserAbInfoHandler(memoryHandler); // 推荐接口 String defaultValue = null; // 默认版本值,当分流未命中时返回该值,注意:返回值为包装后的对象,并非defaultValue本身,依然需要使用get方法获取 Variable variable1 = abClient.activate("variantKey", user, defaultValue); if(null == variable1) { return; } Object value1 = variable1.getValue(); if(null == value1) { return; } if(value1.equals("a")){ } else if(value1.equals("b")){ } } }
说明
私有化环境中,如果您希望使用用户分群作为受众条件,则可以在初始化new AbClient中增加.setCohortClient(new HttpCohortClient(app_id, metaHost))的配置。
描述: 用户对象,用于表明分流用户的详细属性
使用方式: 用分流接口时作为入参,使用方式如下所示
// 首先通过Builder类创建用户对象,trackId为缺省值,允许为空 User.UserBuilder userBuilder = new User.UserBuilder().create("decisionId", "trackId"); // 填充deviceid,用于绑定尚未生成uuid的用户(可选) userBuilder.setDeviceId(6981329701821561868L); // 向Builder中填充用户属性 userBuilder.add("age", 18); userBuilder.add("name", "小明"); userBuilder.add("is_vip", true); // 生成User对象 User user = userBuilder.build();
描述: 变体对象,用于表明分流结果的详细属性
使用方式: 分流接口的返回对象基本类,使用方式如下所示
String vid = variable.vid; // 变体的ID String type = variable.type; // 变体值的数据类型,与Tester平台配置保持一致 // 通用方法,获取Object对象,具体数据类型需要用户自行判断 Object o = variable.getValue(); // 精确数据类型 String vStr = variable.getString(); // 非字符串类型默认返回null int vInt = variable.getInteger(); // 非int类型默认返回0 double vDouble = variable.getDouble(); // 非double类型默认返回0.0 boolean vBoolean = variable.getBoolean(); // 非boolean类型默认返回false
描述:进组不出组接口
接口内容:
public interface UserAbInfoHandler { /** * 获取进组信息 * @param decisionId 用户分流ID * @return key:实验ID value:进组的变体ID */ HashMap<String, String> query(String decisionId); /** * 更新进组信息 * @param decisionId 用户分流ID * @param abInfo 用户进组信息,<experimentID, versionID> * @return 是否成功更新 */ boolean createOrUpdate(String decisionId, HashMap<String, String> abInfo); }
使用方式:
AbClient.eventDispatcher.properties
AbClient abClient = new AbClient.Builder("appKey").setMetaHost("MetaHost").setTrackHost("TrackHost").setOnpremise(true).build(); abClient.setThreadCount(4); abClient.setQueueSize(102400);
接口:
new AbClient.Builder(String appKey).setMetaHost(String metaHost).setTrackHost(String trackHost).setOnpremise(boolean isOnpremise).build();
描述:初始化ABTest分流类。
参数:
appKey:表明您的Tester应用。出于安全考虑,此处请使用appKey而非appID表明您的应用。
在集团中接入一个应用后,您可以在集团相关页面查看应用的AppKey等信息,详情请参考:如何创建应用。
metaHost :填写获取实验元信息的地址,默认为SaaS-非云原生国内线上地址,其他环境参考上文2 .1~2.3。海外Saas域 名:MetaHost.SG
trackHost :填写事件上报的地址,默认为SaaS-非云原生国内线上地址,其他环境参考上文2 .1~2.3。海外Saas域 名:TrackHost.SG
isOnpremise(default=false):表明您的应用是否需要额外配置metaHost和 trackHost
说明
1、请尽早初始化 AbClient,以免影响您的分流服务和埋点上报服务。
2、每个应用有且仅有一个分流类 AbClient,请确保它在所有线程中的唯一性。
Meta 元信息服务默认使用火山引擎 A/B 平台国内线上地址,请谨慎修改。saas 合法地址请参考代码中 MetaHost 枚举类。私有化客户需修改为部署时 AB 服务的挂载域名。
埋点上报服务默认使用火山引擎 A/B 平台国内线上地址,请谨慎修改。saas 合法地址请参考代码中 TrackHost 枚举类。私有化客户需修改为部署时 AppLog 服务的挂载域名。
接口: Variable activate(String variantKey, User user, Object defaultValue)
描述: 获取特定key的分流结果,并上报曝光事件
参数:
variantKey:变体的key
user:用户对象
defaultValue:变体默认值
返回值: 该数返回命中变体对象,未命中时返回默认值对象
public class Variable { String vid; // 变体id Object val; // 变体值 String type; // 变体值的数据类型 }
说明
接口: HashMap<String, Variable> getABInfo(User user)
描述: 获取实验和feature的分流结果
参数:
user:用户对象
返回值: 该数返回命中变体的Map对象 ,具体格式如下所示
{ "key1": { "vid": "123", "value": "v1", "type": "string" }, "key2": { "vid": "456", "value": false, "type": "boolean" }, "key3": { "vid": "789", "value": 123.45, "type": "double" } }
接口: String getExperimentVariantName(String experimentId, User user)
描述: 获取用户命中的实验版本名称
参数:
experimentId:指定分流的实验ID
user:用户对象
返回值: 该数返回用户命中的实验版本名称
接口: HashMap<String, Variable> getExperimentConfigs(String experimentId, User user)
描述: 获取用户命中的特定实验的变体详情
参数:
experimentId:指定分流的实验ID
user:用户对象
返回值:
该数返回命中变体的Ma p对象 ,表明用户命中某个实验的变体详情,通常仅能命中一个变体。
接口: HashMap<String, Variable> getAllExperimentConfigs(User user)
描述: 获取用户命中的所有实验的变体详情
参数:
user:用户对象
返回值:
该数返回命中变体的Ma p对象 ,表明用户命中所有实验的变体详情,通常命中多个变体。
接口: HashMap<String, Variable> getFeatureConfigs(String featureId, User user)
描述: 获取用户命中的特定feature的变体详情
参数:
featureId:feature ID
user:用户对象
返回值:
返回命中变体的Map对象 ,表明用户命中某个f ature的变体详情,通常仅能命中一个变体。
接口: HashMap<String, Variable> getAllFeatureConfigs(User user)
描述: 获用户命中的所有feature的变体详情
参数:
user:用户对象
返回值: 返回命中变体的Map对象 ,表明用户命中所有feature的变体详情,通常命中多个变体。
功能同接口“getExperimentVariantName”(此接口上报曝光事件)
功能同接口“getExperimentConfigs”(此接口上报曝光事件)
功能同接口“getFeatureConfigs”(此接口上报曝光事件)
接口: void setInterval(int n)
描述: 设置meta元信息更新的时间间隔
参数:
n:元信息自动更新时间间隔(单位:秒)
接口: void setThreadCount(int n)
描述: 设置埋点上报服务的最大线程数
参数:
n:最大线程数(默认为4)
接口: int getSuccessEvents()
描述: 获上报成功的事件数,用于监测事件上报回调结果
返回值: 上成功的事件数
注意: 私化版本不支持回调函数
接口: int getFailEvents()
描述: 获上报失败的事件数,用于监测事件上报回调结果
返回值: 上报失败的事件数
接口: int getQueueEvents()
描述: 获线程池中剩余事件数,用于监测尚未上报的事件数量
返回值: 尚未上报的事件数
ABTest服务端SDK使用文件记录log日志 。如果您希望修改日志配置,请编辑文件log4j2.xml,默认的log地址 为./logs
请参考集成示例,redis实现
/** * 实现 UserAbInfoHandler接口,并实现接口中声明的方法 * 进组不出组,Redis实现。 */ public class NotOutGroupRedisImplement implements UserAbInfoHandler { /** * 存储分流结果 * @param userId 用户ID * @param hashMap 分流结果参数和值,key=分流参数,value=对应版本的标识 * @return */ @Override public boolean createOrUpdate(String userId, HashMap<String, String> hashMap) { long res = 0 ; for (Map.Entry<String, String> entry : hashMap.entrySet()) { res = JedisUtils.getJedis().hset(userId, entry.getKey(), entry.getValue()); } return res > 0 ; } /** * 查询用户对应的分组 * @param userId * @return 用户对应的分流结果 key=分流参数,value=对应版本的标识 */ @Override public HashMap<String, String> query(String userId) { Map<String, String> resMap = JedisUtils.getJedis().hgetAll(userId); return (HashMap<String, String>) resMap; } }
在AB分流代码中配置进组不出组的逻辑
AbClient abClient = new AbClient.Builder(appKey).build(); NotOutGroupRedisImplement notOutGroupHandler = new NotOutGroupRedisImplement(); abClient.setUserAbInfoHandler(notOutGroupHandler); Variable variable1 = abClient.activate("variantKey", user, defaultValue);
用户类中的 decisionId 需要使用用户唯一标识,例如客户端设备 ID、SSID 等;trackId 用于事件上报,如果用户已登录,设置为对应的实名用户 ID,如果是匿名用户,设置为空字符串 ""。
匿名用户需要额外设置可关联的设备标识,通常按以下规则选择:
场景 | 推荐配置 | 是否使用 anonymousId |
|---|---|---|
私有化环境 App 端匿名用户,且有 long 类型设备 ID |
| 不使用 |
Web 及小程序匿名用户,且有 long 类型 webId |
| 不使用 |
SaaS-非云原生环境匿名用户 |
| 不使用 |
匿名标识是客户自定义字符串,无法使用 long 类型的 |
| 使用 |
注意:
decisionId 用于分流,请设置为稳定的用户唯一标识。trackId 用于事件上报。匿名用户没有实名 ID 时,建议设置为空字符串 ""。deviceId、webId 或 SaaS 场景下的 bdDid,优先使用这些字段。anonymousId 是 deviceId / webId / bdDid 之外的补充方案。常规匿名用户配置示例:
User user = new User.UserBuilder().create("decisionID", "") // trackId有值设置对应的值,没有值设置空字符串 .setDeviceId(6981329701821561868L) // 私有化App端使用此方法 .setWebId(7018215618686981329L) // Web及小程序使用此方法 .setBdDid("H5PPHTEHXLMEGCGROGLY2343242M2LMFIMQE4VIRW76HKYWH4Q01") // SaaS-非云原生环境使用此方法 .build();
如果客户侧匿名 ID 是字符串类型,例如自定义匿名 ID、三方匿名 ID 等,无法放入 deviceId / webId 这类 long 类型字段,可以通过 anonymousId 进行匿名用户识别。
使用 anonymousId 时,请将 trackId 设置为空字符串 "",并通过 setAnonymousId 设置匿名 ID。用户登录后,如果希望登录实名之后的行为能和匿名之前的行为关联起来,需要继续带上同一个 anonymousId,同时把 trackId 设置为登录后的实名用户 ID。
注意:
anonymousId 适用于私有化/火山云新环境曝光上报场景。setBdDid,不使用 setAnonymousId。AbClient 时需要配置正确的 metaHost 和 trackHost。activate 或其他带有 WithImpression 的接口时,SDK 会自动上报曝光事件。decisionId 仍用于分流;trackId 用于事件上报,匿名时建议设置为空字符串。import com.bytedance.tester.AbClient; import com.bytedance.tester.model.User; import com.bytedance.tester.model.common.Variable; public class AnonymousIdExample { public static void main(String[] args) { int appId = 123456; AbClient abClient = new AbClient.Builder("appKey", appId) .setMetaHost("https://tab.volces.com") .setTrackHost("https://gator.volces.com") .setNewEnv(true) .build(); User anonymousUser = new User.UserBuilder() .create("anonymous-decision-id", "") .setAnonymousId("anonymous-id-001") .add("version", "1.2.2") .build(); String defaultValue = null; Variable variable = abClient.activate("variantKey", anonymousUser, defaultValue); if (variable == null) { return; } Object value = variable.getValue(); // 根据业务逻辑使用分流结果 value } }
用户登录后,如果希望实名之后的行为与匿名之前的行为关联,需要在设置实名 trackId 的同时,继续带上匿名阶段使用的同一个 anonymousId。
import com.bytedance.tester.AbClient; import com.bytedance.tester.model.User; import com.bytedance.tester.model.common.Variable; public class AnonymousToLoginExample { public static void main(String[] args) { int appId = 123456; AbClient abClient = new AbClient.Builder("appKey", appId) .setMetaHost("https://tab.volces.com") .setTrackHost("https://gator.volces.com") .setNewEnv(true) .build(); User loginUser = new User.UserBuilder() .create("login-decision-id", "login-user-id") .setAnonymousId("anonymous-id-001") .build(); Variable variable = abClient.activate("variantKey", loginUser, null); if (variable == null) { return; } Object value = variable.getValue(); // 登录后的曝光事件会携带 login-user-id 和 anonymous-id-001 } }