MCube动态化与原生工程结合最佳实践
跨端动态化开发方案重要性日益凸显,本文对我们团队MCube动态化实践做了总结,为大家提供经验和借鉴。
接入背景
随着我们工程的需求迭代,暴露出了业务需求量大,分端开发和发版更新成本高等痛点,使用H5页面来代替,在用户体验和性能相较原生有差异,所以我们团队开始了对动态化改造的研究。
在做过一些列动态化开发的尝试,并对多种方案进行调研后,我们选择了MCube的动态化方案。之所以选用MCube,是因为它有以下的优点:
1.方案成熟,对同为京东体系下的APP来说接入成本非常低,对照文档文档,整个接入的过程并不复杂。2.MCube有丰富原子组件,组件的灵活性非常高。
3.完善的组件市场可以很好的满足我们的需求。
4.一套XML模板适用多个平台,并且可以复用。
我们在接入的过程当中遇到兼容性问题,在沟通过程中,MCube的负责同学和开发人员积极性非常高,定制化开发的效率也很高,认真负责,有问必答,整个沟通过程非常愉快。
我们团队目前已经完成了从初步尝试,到一整个业务页面动态化改造的完整历程。期间遇到了许多问题,总结出了最佳化实践的经验,本篇文章做出总结,为其他已经接入和准备接入的同学提供经验。
单楼层动态化改造
虽然MCube的SDK提供了丰富的接口方法,但是为了方便使用和后续的全页面级别动态化控制,我们还是做了大量的封装和本地化开发。
首先,在数据层面:

根据MCube SDK的必传参数要求,我们封装了必要的module(页面名)和templateID(模版ID);在数据部分,模型封装了用于模版的数据源data,这样就可以让动态化视图(DYNContainerView)从数据源取数据进行展示,同时也方便外部下发;同时我们有大量原生楼层做动态化改造的场景,需要保留原数据进行交互和降级,所以模型预留了原生楼层使用的nativeData,方便在有以上需求的时候取用;最后,考虑到页面有将某些楼层按照section成组进行展示的场景,模型设计了用于组划分的groupID,相同groupID为一个section,按照页面具体的业务需求决定组的展示形式(卡片或者分割)。
在视图层面,以封装好的DynamicViewModel为基础,将动态化视图加载等操作进行了封装,形成了通用楼层CommonTableViewCell:

通用楼层的能力包括:
1.解析module和templateID
2.调用MCube SDK下载对应动态化模版,并在结束时给出回调
3.展示动态化视图DynView,使用数据进行刷新
4.支持设置降级视图,失败时进行降级
5.对于元素是原生视图的,通用楼层会用过layoutId将其找到,并通过Block回调出来,方便进行后续的操作,包括宽高调整,代理设置等等
self.successBlock = ^(DYNContainerView * _Nonnull dynamicView) {
UIView *nativeView = [dynamicView getRrenderViewWithLayoutId:@"100"];
if ([nativeView isKindOfClass:[原生视图类名 class]]) {
//原生事件处理
}
};
全页面动态化改造
有了通用楼层,我们就开始了下一步的改造,最终的目的是实现全页面动态化下发,这也是我们团队对于动态化改造的新理解:全页面的所有楼层均支持动态化下发,原生楼层与动态化楼层共存,由数据来驱动,灵活控制楼层的展示形式,另外还可以由配置平台设置开关,控制动态化楼层的降级。
理想情况下,业务页面的顺序和楼层数据都可以由后端下发的数据源来决定。但实际进行改造的时候,我们时常会面对的情况是:后端下发的result里混杂着所有楼层的数据字段,每个楼层在加载数据model的时候要到result里单独找到自己要用的字段进行解析,这样就存在着两个弊端:
1.楼层顺序无法由下发控制
2.无法做到分楼层动态化控制
所以我们改造的第一步是对下发的数据结构进行重新定义。
"result":{
"dynamicFloors":
[
{
"type":"楼层类型",
"module":"页面名",
"templateID":"模版ID",
"groupID":"part1"
"data":{
}
}
]
}
我们设定了一个动态化楼层数组dynamicFloors,里面的每个元素代表了某一楼层的数据。type用于确定楼层类型,对于动态化楼层,会额外下发module和templateID,以此来区分动态化和原生楼层。如果发现下发了module和templateID,那么自动用DynamicViewModel来解析,将data设置进数据源,然后用CommonTableViewCell来加载和展示楼层。流程图如下:

后续可以推动服务端按照这个结构进行下发,当然,在推动其他团队同学改在之前,前端也可以先行对下发的result进行改造,但千万要保证数据安全并做好容错处理和降级开关。
改造成果:

踩坑Q&A
接入JDBDynamicModule后安装/编译报错
我们在接入的过程当中遇到了诸如类名和方法名冲突的问题,需要与MCube的相关负责人沟通,单独为工程进行SDK的定制化开发,解除依赖。
哪些楼层适合做成动态化楼层
就我们的经验来讲,适合做成动态化楼层需要满足以下一个或多个条件:
1.楼层的展示样式存在灵活变化的需求。
2.楼层的交互形式简单且单一。动态化着重于UI展示,相对来说模版交互事件开发较为繁琐。
3.安卓与iOS差异不大。这里的差异主要说的是模开发的差异,由于MCube本身的机制,在进行模版开发时经常会有安卓和iOS模版字段不通用的情况,具体见下面“安卓与iOS模版开发时的差异”部分。差异小可以保证安卓和iOS能采用同一套模版,减少开发工作量。
通过loadWithCallback加载模版失败
首先检查DYNContainerConfig对象的module和templateID是否设置正确。
其次检查是否实现了网络加载桥接类以及协议。MCube的所有网络请求都要经过桥接类来进行处理,走我们本地的网络请求并把结果返回给MCube。首先根据自己的命名需要创建一个类,并实现协议,在+load里对创建的类进行注册,主要是告诉MCube你的网络请求后续都由我这个类来处理
+ (void)load {
[DYNMapperManager registeProtocolName:DYNProtocolConfig.networkProtocolName handlerClassName:NSStringFromClass(self)];
}
桥文件里的主要工作是实现协议里定义的相关方法,网络请求的DYNNetworkProtocol中主要需要实现的有:
getServerURL
根据自己的网络环境配置,返回MCube接口请求的URL头部,有两套:
1.正式环境:api.m.jd.com/XXX?functionId=
2.测试环境:beta-api.m.jd.com/XXX?functionId=
requestWithSetupModel: finish: cancel:
在这个方法里,需要创建API请求,将setupModel里的functionId和params,以及appType放进入参,发起请求,并把请求结果通过finish/cancel的Block回调返回给MCube进行处理。我们也可以在这里断点或者进行自己的处理,最主要的接口是获取模版列表,可以查看下发的模版列表文件是否正常。
getNetworkType
根据当前的网络环境返回网络类型,比如Wi-Fi,5G和4G等等。
模版中的ImageView图片无法加载
图片加载相关的请求也需要创建对应的桥接类,实现协议的相关方法。同时还需要给UIImageView创建一个category分类UIImageView+DYNBImageViewHandler,在分类里覆写dyn_setImageWithURL: placeholderImage:和dyn_setImageWithURL: placeholderImage: completed:方法,在它们里面利用sdWebImage等其他工具进行图片下载。
如何在模版中使用原生视图组件
MCube中可以将原生视图组件做为元素写到模版里,有以下步骤
1.原生视图组件实现协议里的两个方法:
#pragma mark - DYNCustomViewProtocol
- (id)getCustomView {
return self;
}
- (void)setDataWithValue:(id)value object:(id)object sender:(DYNViewLayout *)sender {
if ([value isKindOfClass:[原生模型 class]]) {
//使用模型刷新原生视图组件
}
}
2.在+load里声明模版元素与原生视图类的对应关系:
+ (void)load {
[DYNMapperManager registeProtocolName:@"模版元素名" module:@"模块名" handlerClassName:@"原生视图组件类名"];
}
3.模版中用对应的元素名进行编写
<FlexboxLayout layoutId="999" width="match_parent" height="wrap_content" flexDirection="column" justifyContent="flex_start" bgColor="#FFFFFF">
<模版元素名 layoutId="100" width="100%" height="100%" data="${原生模型对应key}" bgColor="#FFFFFF">
</模版元素名>
</FlexboxLayout>
安卓与iOS模版开发时的差异
上文提到:由于MCube本身的机制,在进行模版开发时经常会有安卓和iOS模版字段不通用的情况,例如:


在我们的开发过程中,遇到的安卓和iOS不兼容的问题有:
1.安卓的嵌套滑动组件接入工程后,会报与本地的库有冲突。
2.FlexboxLayout 设置100%安卓可以滑动,ios滑动不了。
3.ImageView scaleType="stretch" 安卓会展示比例有问题,需要加useAspectRatio,ios没问题。

4.安卓在设置渐变色的时候语法不生效:

模版中如何做复杂的逻辑
1.数值结算
<ImageView height="$calc(44*SCREEN_WIDTH/375)" width="$calc(SCREEN_WIDTH)" />
2.条件判断
<ImageView src="@{${data.img1}?${data.img1}:${data.img2}}"/>
<View visibility="@{@{${data.show}!=null}?1:2}"/>
3.渐变色(拼接语法)
<FlexboxLayout bgColorList="$joint(${startColored},$unescape(comma),${endColored})">
<TextView text="$joint((${data.text1} ${data.text2}))"/>
上传和发布模版
模版要上传到配置平台(具体网址请咨询相关负责人,并开通权限),在配置平台创建APP,模块(页面)和具体楼层,楼层可以发布不同版本,上传缩略图和模版文件,最后还可以选择白名单发布或者灰度发布。

如何做降级方案?
降级的形式我们在不同维度设立了三套方案:
视图层面
如果MCube的SDK加载动态化视图失败,会给我们一个失败回调Block,那么我们的处理是可以利用存在DynamicViewModel里的nativeData去加载原生楼层作为降级。
下发数据层面
可以通过控制是否下发module和templateID,来控制该楼层走原生展示还是动态化展示。
业务楼层层面
我们在配置平台设置了白名单(dynamicWhiteList)进行控制:按模块名(module)-模版名(templateID)来定位具体楼层,配置true(降级)/false(动态化)决定是否降级。
数据格式:
"dynamicWhiteList":{
"页面名module":{
"模版templateID 1":true,//降级
"模版templateID 2":false//动态化
}
}
作者:京东零售 姜海
来源:京东云开发者社区 转载请注明来源
MCube动态化与原生工程结合最佳实践的更多相关文章
- 搭建Spring4+Spring MVC web工程的最佳实践
Spring是个非常非常非常优秀的java框架,主要是用它的IOC容器帮我们依赖注入和管理一些程序中的Bean组件,实现低耦合关联,最终提高系统可扩展性和可维护性,用它来辅助我们构建web工程将会感觉 ...
- 【GoLang】GoLang GOPATH 工程管理 最佳实践
参考资料: MAC下 Intellij IDEA GO语言插件安装及简单案例:http://blog.csdn.net/fenglailea/article/details/53054502 关于wi ...
- 【机器学习】Google机器学习工程的43条最佳实践
https://blog.csdn.net/ChenVast/article/details/81449509 本文档旨在帮助那些掌握机器学习基础知识的人从Google机器学习的最佳实践中获益.它提供 ...
- “行业客户云原生最佳实践日” 亮相KubeCon上海
2018年11月13日至15日,由CNCF主办的KubeCon + CloudNativeCon将首次登陆中国上海,这是全球范围内规模最大的Kubernetes和云原生技术盛会. 唯一聚焦客户实践的分 ...
- 机器学习规则:ML工程最佳实践----rules_of_ml section 1【翻译】
作者:黄永刚 机器学习规则:ML工程最佳实践 本文旨在指引具有机器学习基础知识的工程师等人,更好的从机器学习的实践中收益.介绍一些应用机器学习需要遵循的规则,类似于Google C++ 风格指南等流行 ...
- Aggregated APIServer 构建云原生应用最佳实践
作者 张鹏,腾讯云容器产品工程师,拥有多年云原生项目开发落地经验.目前主要负责腾讯云 TKE 云原生 AI 产品的开发工作. 谢远东,腾讯高级工程师,Kubeflow Member.Fluid(CNC ...
- 8.云原生之Docker容器镜像构建最佳实践浅析
转载自:https://www.bilibili.com/read/cv15220861/?from=readlist 本章目录 0x02 Docker 镜像构建最佳实践浅析 1.Dockerfile ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- iOS系统中导航栏的转场解决方案与最佳实践
背景 目前,开源社区和业界内已经存在一些 iOS 导航栏转场的解决方案,但对于历史包袱沉重的美团 App 而言,这些解决方案并不完美.有的方案不能满足复杂的页面跳转场景,有的方案迁移成本较大,为此我们 ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
随机推荐
- 开源 SD-Small 和 SD-Tiny 知识蒸馏代码与权重
最近,人工智能社区在开发更大.更高性能的语言模型方面取得了显著的进展,例如 Falcon 40B.LLaMa-2 70B.Falcon 40B.MPT 30B; 以及在图像领域的模型,如 SD2.1 ...
- git pull 强制覆盖本地代码
使用git pull更新本地代码,报以下错误: 解决办法如下. 1.备份本地代码 备份,可以考虑直接复制一份项目保存 2.远程覆盖本地 远程覆盖本地容易出现远程和本地冲突的情况 解决办法如下: //1 ...
- js监控微信浏览器的自带的返回事件(延迟解决微信返回立即执行popstate事件)
/** * 浏览器回退事件监听 */ var listenerBackHandler = { param: { isRun: false, //防止微信返回立即执行popstate事件 }, list ...
- MySQL系列之MHA高可用——主从复制架构演变介绍、高可用MHA、管理员在高可用架构维护的职责
文章目录 1. 主从复制架构演变介绍 1.1 基本结构 1.2 高级应用架构演变 1.2.1 高性能架构 1.2.2 高可用架构 2. 高可用MHA ***** 2.1 架构工作原理 2.2 架构介绍 ...
- Python并发编程——paramiko远程控制的模块、病毒攻击原理、dll注入、
文章目录 paramiko模块 作业 攻击原理解析 一.什么是dll 二.为何要有dll 什么是dll注入: 什么时候需要dll注入 dll注入的方法 使用SetWindowsHookEx函数对应用程 ...
- linux常用命令(七)
用于系统内信息交流的相关命令 echo mesg wall write echo:在显示器上显示文字 命令语法:echo[选项] [字符串] 选项 选项含义 -n 表示输出文字后不换行 例子:将文本& ...
- OTOCI 题解
OTOCI 题目大意 给定 \(n\) 个带权的点,需要进行四种操作:查询两点连通性:加边:修改点权:查询两点路径的权值和. 思路分析 首先观察题目,我们会发现,在所有的操作结束后,所有的点构成一个森 ...
- 基于LangChain的LLM应用开发2——模型、提示和输出解析
本次会讲解LangChain的三个基本组件:模型.提示和解析器. 名词解析 模型(Models):是指作为基础的大语言模型.LangChain中通过ChatOpenAI或者AzureChatOpenA ...
- JVM 学习
目录 1. 类加载器及类加载过程 1.1 基本流程 1.2 类加载器子系统作用 1.3 类加载器角色 1.4 加载过程 (1) 加载 loading (2) 链接 linking 验证 verify ...
- P4156 [WC2016]论战捆竹竿 题解
题目链接 题意描述 给定一个字符串 \(s\),你初始拥有一个空串 \(t\),每次可以选择这个字符串的一个 Border,去掉它后接在 \(t\) 的后面,操作后 \(s\) 不变,给出一个上限 \ ...