目录:
- 1. 需求背景
- 2. 客户端设计
- 3. Server 端设计
- 4. demo 效果
1. 背景
基于卡片的线上 AB 测能力作为 58APP Android 端 Wafers 系列工作的一部分。在此项目之前,Wafers 已经在 58App 端完成以下工作:
1. 提升本地编译速度 70%
2. 减小包大小和动态化,实现业务提效,包括:招聘页面动态化,减少由于 DU 框架带来的 6M 包大小
3. 推广包大小减少 30% 以上,实现渠道转换率提升 15%
4. 业务模块动态化,实现厂商基础包未删减功能的前提下,包大小减少 50M,减少 49.8%
5. 任意门:实现了基于页面级别的线上 A/B 测,bug 修复,紧急需求上线的能力;招聘、部落已接入并使用其上线多个需求,
最高日更新生效设备 178万
重点介绍一下任意门:
- 基于 Wafers 实现, 动态更新不能修改底层库、通用 SDK 配置等
- 基于 App 版本级别,更新只会对某个特定 App 版本生效,如果需要覆盖多个 App 版本,则需要上线多次
- 静默下载更新,对用户无感知
- 基于页面级别的路由替换
但是有一个问题,局部元素(UI+逻辑)如何实现线上A/B测?
由于 58App 首页由无线组维护,首页是用户冷启动后就会进入的页面,且首页的落地页代码非常复杂,难以实现动态路由替换落地。而我们收集了产品方面的线上 A/B 测需求,发现有比较大一部分是基于卡片的 A/B 测:
方案讨论:
# | 方案 | 结论 |
---|---|---|
1 | 任意门 | 只能实现页面级别的路由替换,无法实现局部元素的替换 |
2 | 动态化布局 DSL | 如 Tangram 是基于组件的布局动态化,不仅无法实现逻辑动态化,且对现有逻辑有比较大的改造成本 |
3 | RN/Flutter | 首页是核心页面,为了稳定性和体验,所以没有采用 RN/H5/Flutter;同时 58App 需要做 A/B 测的其他页面大部分是 native,改造成 RN/Flutter 难度大 |
4 | 基于 Wafers 动态更新 + 任意门的包下发更新逻辑,实现基于卡片的线上 A/B 测能力 | 可行,基于任意门的卡片动态化,是追求最低侵入式的实现。动态包更新范围和任意门一致 |
适用范围:
可用于以下卡片的线上 A/B 测、bug 修复、上线时效性很强的线上运营需求等,实现 UI + 逻辑维度的动态更新:
- 首页卡片(如九宫格卡片、推荐卡片、个人中心卡片)
- 微聊卡片
- 列表卡片 (首页 feed 流、其他列表)
- 普通卡片
2. 客户端设计
Mocha(Mókǎ, 摩卡,谐音魔卡)
任意门是提供基于 Activity 级别的路由替换,Mocha 作为任意门的一个新分支,目标是实现普通卡片、列表卡片的线上 AB 测,bug 修复及强运营活动的能力。
设计核心:
- Mocha 注册的模版不区分业务线,无需受制于任何业务接口/基类。type 是一个模版的唯一标识,只要列表接入了 mocha 能力,server 列表数据中下发的 type 为该 type 时,即可正常渲染加载该卡片。
- 对 server 数据结构侵入小,以新增数据的方式进行处理。
- 方便动态包的业务集成到主分支:只需要将动态包业务代码直接移动过去,并在基础包中注册该卡片即可 (相当于基础包内置了该卡片)。
3. Server 端设计
设计要点:
- 在老接口上扩展
- 区分 App 版本号
- 通过 AB 测平台分流
接下来我们来看下具体的协议设计。
普通卡片
以首页六宫格推荐卡片为例,我们在 AB 测时保留原有的数据,使用扩展的方式来实现,此时分成两种 case:
(1) case 1:新卡片的数据复用旧卡片的数据
这种 case 占到 90% 以上,一般产品对于 AB 测的诉求是修改 UI 样式/体验,这时协议如下。
旧协议:
{
"section_recommend_new": [{
"icon": "https://j1.58cdn.com.cn/arthurupload/zhiyou/mrs/ershoufangpic.png",
"subtitle": "特价二手房",
"title": "北京"
}, {
"icon": "https://a.58cdn.com.cn/app58/icons/homepage_lgg_zp00004.png",
"subtitle": "免费提供住宿",
"title": "教师/助教"
}, {
"icon": "https://pic8.58cdn.com.cn/nowater/tribenowatermark/n_v2c73eb7192e1d4148bb1c61c09c7f0e4f.png",
"subtitle": "都是仓储人哦",
"title": "仓储管理员"
}, {
"icon": "https://pic6.58cdn.com.cn/nowater/tribenowatermark/n_v2eeed9f9527fa47229bfee4356d505151.png",
"subtitle": "你是哪个厂的",
"title": "普工大家庭"
}, {
"icon": "https://pic1.58cdn.com.cn/nowater/tribenowatermark/n_v2c603c91b33d049ac9e0317f25a2d1359.png",
"subtitle": "烫染洗剪吹喽",
"title": "美容美发圈"
}, {
"icon": "https://pic7.58cdn.com.cn/nowater/tribenowatermark/n_v25d526b020b5a48f0b67e83cad86d9b65.png",
"subtitle": "北京人一起聊",
"title": "北京人"
}]
}
新协议:
增加一个字段,key 为动态包中新注册的卡片 type, value 为 {} / [],这时旧卡片将用新卡片的样式、解析器来解析渲染,此时使用的数据仍是旧卡片的数据。
{
"mocha_recommend": [],
"section_recommend_new": [{
"icon": "https://j1.58cdn.com.cn/arthurupload/zhiyou/mrs/ershoufangpic.png",
"subtitle": "特价二手房",
"title": "北京"
}, {
"icon": "https://a.58cdn.com.cn/app58/icons/homepage_lgg_zp00004.png",
"subtitle": "免费提供住宿",
"title": "教师/助教"
}, {
"icon": "https://pic8.58cdn.com.cn/nowater/tribenowatermark/n_v2c73eb7192e1d4148bb1c61c09c7f0e4f.png",
"subtitle": "都是仓储人哦",
"title": "仓储管理员"
}, {
"icon": "https://pic6.58cdn.com.cn/nowater/tribenowatermark/n_v2eeed9f9527fa47229bfee4356d505151.png",
"subtitle": "你是哪个厂的",
"title": "普工大家庭"
}, {
"icon": "https://pic1.58cdn.com.cn/nowater/tribenowatermark/n_v2c603c91b33d049ac9e0317f25a2d1359.png",
"subtitle": "烫染洗剪吹喽",
"title": "美容美发圈"
}, {
"icon": "https://pic7.58cdn.com.cn/nowater/tribenowatermark/n_v25d526b020b5a48f0b67e83cad86d9b65.png",
"subtitle": "北京人一起聊",
"title": "北京人"
}]
}
(2) case 2:新卡片的数据需要新数据
由于一般卡片的数据来自于各业务线,所以新卡片需要新数据时,需要和业务线配合一起开发新数据的 AB 测,数据仍是存放在新增的字段值中。
旧协议:
{
"section_recommend_new": [{
"icon": "https://j1.58cdn.com.cn/arthurupload/zhiyou/mrs/ershoufangpic.png",
"subtitle": "特价二手房",
"title": "北京"
}, {
"icon": "https://a.58cdn.com.cn/app58/icons/homepage_lgg_zp00004.png",
"subtitle": "免费提供住宿",
"title": "教师/助教"
}, {
"icon": "https://pic8.58cdn.com.cn/nowater/tribenowatermark/n_v2c73eb7192e1d4148bb1c61c09c7f0e4f.png",
"subtitle": "都是仓储人哦",
"title": "仓储管理员"
}, {
"icon": "https://pic6.58cdn.com.cn/nowater/tribenowatermark/n_v2eeed9f9527fa47229bfee4356d505151.png",
"subtitle": "你是哪个厂的",
"title": "普工大家庭"
}, {
"icon": "https://pic1.58cdn.com.cn/nowater/tribenowatermark/n_v2c603c91b33d049ac9e0317f25a2d1359.png",
"subtitle": "烫染洗剪吹喽",
"title": "美容美发圈"
}, {
"icon": "https://pic7.58cdn.com.cn/nowater/tribenowatermark/n_v25d526b020b5a48f0b67e83cad86d9b65.png",
"subtitle": "北京人一起聊",
"title": "北京人"
}]
}
新协议:
增加一个字段,key 为动态包中新注册的卡片 type, value 为需要的新数据,这时旧卡片将用新卡片的样式、解析器来解析渲染,此时使用的数据是新卡片的数据。
{
"mocha_recommend": [{
//...
},
{
//...
},
//...
],
"section_recommend_new": [{
"icon": "https://j1.58cdn.com.cn/arthurupload/zhiyou/mrs/ershoufangpic.png",
"subtitle": "特价二手房",
"title": "北京"
}, {
"icon": "https://a.58cdn.com.cn/app58/icons/homepage_lgg_zp00004.png",
"subtitle": "免费提供住宿",
"title": "教师/助教"
}, {
"icon": "https://pic8.58cdn.com.cn/nowater/tribenowatermark/n_v2c73eb7192e1d4148bb1c61c09c7f0e4f.png",
"subtitle": "都是仓储人哦",
"title": "仓储管理员"
}, {
"icon": "https://pic6.58cdn.com.cn/nowater/tribenowatermark/n_v2eeed9f9527fa47229bfee4356d505151.png",
"subtitle": "你是哪个厂的",
"title": "普工大家庭"
}, {
"icon": "https://pic1.58cdn.com.cn/nowater/tribenowatermark/n_v2c603c91b33d049ac9e0317f25a2d1359.png",
"subtitle": "烫染洗剪吹喽",
"title": "美容美发圈"
}, {
"icon": "https://pic7.58cdn.com.cn/nowater/tribenowatermark/n_v25d526b020b5a48f0b67e83cad86d9b65.png",
"subtitle": "北京人一起聊",
"title": "北京人"
}]
}
{
"msg": "success",
"code": 580200,
"list": [{
"type": "business_onepic",
"infoId": 1219500454809636864,
"commentNum": "17",
...
},
...
]
}
AB 测时,需要将 business_onepic 卡片改成 mocha_ business_onepic 卡片做测试,那么新协议为:
分组分流时,对于命中的用户,不会下发 business_onepic 的数据,而会下发新的卡片 mocha_ business_onepic 的数据。
{
"msg": "success",
"code": 580200,
"list": [ {
"type": "mocha_ business_onepic",
"mocha_pic": "...",
"mocha_url": "...",
...
},
...
]
}
4. demo 效果
https://wos.58cdn.com.cn/IjGfEdCbIlr/ishare/video_Xd59d1XdWbd3d37bWcU5XU7bd1U7d3Xd.mp4