Native与Weex交互通用解决方案

1. 背景

从2018年开始,有赞移动团队使用Weex做为移动端跨平台动态性技术解决方案。自Weex引入之后需求推进速度得到很大提升,因此被开发同学使用到各个App和各个模块中,在使用过程中各个App为了Weex调用Native功能,都各自实现了不同功能的WeexModule,经过2年多的发展,发现各个App中有很多功能差不多的WeexModule,例如:专用于路由跳转、配置中心、账号信息等类似功能的WeexModule

我们期望能有一个解决Native与Weex交互的通用解决方案,简化业务方接入工作,也方便同个Weex页面可以在不同模块或者不同App进行正常渲染,因此ZanWeexModuleSDK就孕育而生。下面将带大家逐步解析ZanWeexModuleSDK设计方案。

2. 现状分析

我们首先分析一个有赞通用的NativeWeex交互流程图 image 从上图我们可以看到,一个完善的基础WeexAPP它会有有很多个WeexModule用于WeexNative组件进行交互,常用的就是路由、网络请求、配置中心、埋点、日志、基础UI调用、分享这些重要功能。并且还有很多模块如:IM业务模块、商品业务模块等也有Weex页面。当我们再去看多个App或模块时,就有下图的现状。

image

上图是不同App或业务模块都有各自实现的WeexModule并相互独立,当有业务需求将一个App或业务模块的Weex页面迁移到另外App或业务模块里,在WeexModule上的工作量就非常大,既要梳理两个WeexModule的逻辑差异,又要重新合并逻辑。所以非常需要一套通用WeexMoudle规则,来做到规范和统一。

3. 整体设计

我们通过分析各个App在使用不同功能的WeexMoule场景和实现,发现有大部分的功能都是重叠的,只有少部分App特有逻辑,因此我们进行了这样的改造: image

从上图中我们可以看到,通用WeexModule把各个业务的WeexModule逻辑全部收拢,简单业务接入通用WeexModule就只需要关心Weex代码实现,不用再关心业务使用的WeexModule实现内容。并且也能做到同个Weex页面可以在不同模块或者不同App进行正常渲染。

3.1 筛选通用WeexModule

有了整体设计方案接下来我们就要分析具体怎么筛选出这些WeexModule。想要打造一个完善的Weex通用MoudleSDK规范不是一口气吃成胖子,设计出来的SDK差异越大越复杂,接入成本就会越高,推动接入就越不容易,所以需要一步一步来。第一个版本只包含最通用的模块,简化接口设计和集成方式。

所以总结出以下筛选原则:

  • WeexModule复用度高
  • WeexModule的接入、使用方便
  • 新旧接口尽量保持一致

通过以上原则,筛选出第一版的通用module并简单介绍下:

  • 网络Module : 替换每个Weex自己实现网络请求封装类,Weex上使用方式保不变
  • 路由Module : 简单路由已实现,独立业务提供自定义实现,接入使用方便
  • 配置Module : 获取移动端动态配置的配置数据,Weex无需重新解析,直接返回的是Map
  • 日志Module : Debug本地日志打印,Release日志上传服务器,使用方便
  • 通知Module : 用于跨页面通知状态变化,使用方便
  • 埋点Module : Weex上使用方式不变,方便ZWeexManager进行init时候不用在实现IZWeexService
  • 账号Module : 常用来判断登陆状态和获取登陆信息

具体怎么筛选的,举两个选择出的例子:

路由Module

路由module是非常通用的WeexModule,它需要处理跳转到各个不同实现的页面,如:NativeWeexFlutterH5,所以WeexSdk直接提供navigator的是肯定无法满足各个App的, 所以各个App就有了各自不同的路由module,为了做到后续将各个AppWeex相关和Native交互都统一走ZanWeexModule,所以设计了ZanNavigatorModule,而ZanNavigatorModule用法上也和原先一样

网络Module

卡门网关是有赞移动端依赖的对外网络请求网关,之前各个AppWeex网络请求依赖的是在WXStreamModule提供的网络请求能力,让后自己封装一个工具类,用于处理卡门网络请求。而各个App的Native网络请求都已统一使用ZanRemote二方库提供的卡门网关网络请求能力,卡门网络请求一旦有改动ZanRemote二方库升级了,而Weex的封装类没进行修改升级,就会导致WeexNative不一致,产生请求问题,而这种问题也不容易排查,可能会产生故障。因此我们需要WeexNative统一使用一套网络请求能力,所以需要提供一个WeexModule处理网络请求,因此有了ZanCarmenModule,在用法上还保持了以前封装类的使用方式,方便快速替换

有了这些通用Module就能很好的支持业务方在实际开发中使用。通用Module选出来了,那接下来我们就可以对各个WeexModule去设计了。

3.2 WeexModule设计思路

针对那些可能存在自定义逻辑的WeexModule,所以在设计的时候需要实现公共部分,并且提供接口给到使用方设置自己的逻辑,例如:路由Module,业务方会存在一些跳转到自己指定的页面场景

/**
 * 自定义处理Navigator
 */
public interface WXMNavigatorService extends WXMBaseService {  
    /**
     * 
     * @param context 上下文
     * @param uri weex模块标示
     * @param url 路径
     * @param params 可选参数
     * @return
     */
    boolean navigator(Context context, String uri, String url, JSONObject params);

}
  • 怎么使用这WXMNavigatorService个接口,让相应的WeexModule找到对应的WXMNavigatorService接口? 为此我们在WeexModuleManager管理类里提供里一个serviceMap保存WXMBaseService实例,让后给到用于查询使用
public class WeexModuleManager {  
  /**
     * 根据默认uri区分weex模块,或者使用自定义字符串
     */
    private final HashMap<String,HashMap<Class,WXMBaseService>> serviceMap = new HashMap<>();

 /**
     * 获取sevice
     * @param uri 唯一标示
     * @param clazz service类型
     * @return 
     */
    public WXMBaseService getService(String uri,Class clazz){
        ...
    }
    /**
     * 根据uri区分weex模块
     * @param uri  唯一标示
     * @param clazz service类型
     * @param service service实现类
     */
    public void setService(String uri,Class clazz,WXMBaseService service){
     ...
    }
}

通过WeexModuleManager提供的getService方法我们可以在WeexMoudle里找到业务方法实现WXMBaseService调用对应的方法,传递参数给到业务方。

3.3 针对weex回调参数设计规则

各个module处理完之后,抛给Weex页面的回调必须要有统一的规则,不然在接入的难度上会有所提高,为了统一规范和降低接入成本,所以我们需要一个通用的回调规范

    /**
     * 带回调的跳转
     * @param url  格式 app_name://pageName 和 https://
     * @param params  跳转到相应页面需要的数据,相应页面不需要数据可不传 {"key1":"value"}
     * @param jsCallback   触发后回调 返回值格式:{"code":200,"data":{},"message":"","success":true}
     */
    @JSMethod
    public void open(String url, JSONObject params, JSCallback jsCallback) {
       ...
    }

其中入参格式的定义,url是按照已有的标准用于区分Native跳转、H5Weex跳转,params方便使用方不用再做解析操作以及和iOS统一返回定义成JSONObject,其中jsCallback内部返回值是重点设计,为了保障各个WeexModule以及以后的WeexModule统一规范返回格式,模仿采用了后端返回值格式,让Weex将其当作类似网络异步请求,在使用的时候就可以统一使用回调返回方法处理。

我们通过以上步骤分析把ZanWeexModuleSdk设计出来了,接下来我们下在Weex中具体怎么使用。

4. 实践

4.1 业务方接入

Native中初始化

在native中需要初始化WeexModuleManager并且把自定义实现类注册到WeexModuleManager

 //application中初始化
   public void initWeex() {
       WeexModuleManager.init(app());
       //自定义路由跳转
       WeexModuleManager.get().setService(AppConfig.WEEX_URI,ZanWXMNavigatorService.class,new ZanWXMNavigatorService());
   }

Weex中如何使用

我们看下新的ZanCarmenModule具体使用并且对比下老的方式请求网络,ZanCarmenModuleWeex中对应的唯一标示zwm-carmen,调用ZanRemote二方库提供的卡门网络请求能力,方便进行卡门请求。

const carmen = weex.requireModule('zwm-carmen')  
// post方法
carmen.post({  
    url:"wsc.appconfigs/1.0.2/get",
    params:{"key":"value"}
}, response =>{
    if(response.success) {
        let data = response.data
    }else {
    }
})

//对比以前通过工具类请求网络
import Network from '@common/tool/Network'  
    //封装方法
    request(name,page,success,mError) {
        this.network.request('XXXX/1.0.0/list','POST',{body:{page:page,size:20}},success,mError)
    }
    //具体请求
         this.request(page,
        response => {
        let data = response.data
        },
        error => {
        }
      );

可以看到新的ZanCarmenModule相对老的方式写法上更简单,而且调用返回处理也是保持一样。

4.2 问题反馈

随着ZanWeexModuleSDK完成,陆续在有赞各个App进行的Weex项目中接入使用,如:微商城、零售、美业、精选、有赞客等App中,也有如:IM模块、商品模块、营销模块等模块中,在实际开发使用中小伙伴也给到我们很多意见和吐槽。

  • 小A:"网络Module很方便的,在新的业务模块中网络的直接用就可以了”
  • 小B:"路由Module那个可以再扩展下,现在业务方需要手动处理 NativeWeexH5的路由,可以增加多一点通用实现”
  • 小C:"日志Module要是提供一些高级点的特性就好了,比如识别出对象,就用json字符串打印"

大家遇到最多的问题是:虽然提供了ZanNavigatorModule,但并没有实现很多通用的路由跳转,还是要重新实现WXMNavigatorService,将原有的逻辑复制到navigator()方法中。

4.3 后续规划

上述这些问题也有后续的规划:

  • 针对类似路由Module这种各个端差异大且常用的WeexModule,是不可能做到一步完成所有替换,因为这种替换会伴随着风险、时间不够、考虑不全等问题。我们先逐步将公共功能剔除出来,放在ZanWeexModuleSDK中实现,例如:跳转Weex页面、跳转H5页面。最后只留下各个App的差异部分。
  • 针对类似日志Moudle这种Module会逐步完善使用体验,提供更多更好用的方法。

最后将其做成一个WeexNative交互的通用解决方案,简化业务方接入工作,只需要关心业务代码。

5. 总结

本文主要介绍了ZanWeexModuleSDK的设计方案。ZanWeexModuleSDK的实现是结合有赞移动团队使用Weex过程中,各个AppWeex交互功能重复和代码冗余中产生的,读者在规划使用Weex开发时就可以考虑这些问题,当然这也不仅仅可以应用在WeexNative交互,也可以应用在FlutterNative交互以及JSNative交互方案上。希望ZanWeexModuleSDK的设计方案能对你有所帮助。

如果你有比较好的建议,可以评论回复,如有任何问题,欢迎指正。

欢迎关注我们的公众号