招才猫直聘安卓DU动态框架实现

导语

市场环境瞬息变换,产品需要根据市场进行快速调整。如何在保证优秀的用户体验,常规发版节奏不变的情况下,可针对部分重要产品功能完成热部署,成为当下我们要解决的技术问题。市场上的解决方案多种多样,如插件化、RN、WEEX等。前几期文章介绍过iOS的轻量级动态部署开发框架,现将Android端的动态部署开发框架-DU也一并发出来,一起探讨学习。下面将向大家详细介绍此框架。

DU的框架产生

什么是DU?DU,全称为DynamicUpdate。顾名思义,就是动态更新框架。DU的诞生,是为了解决Android平台的动态部署能力,因此我们可以通过DU解决产品同学想快速验证的部分重要需求,同时用户体验接近Native。

要实现快速迭代,就要避开APP发版环节,采用动态部署的方式,将功能动态下发到用户手机内。市场上解决此类问题的方案也多种多样,常见方案大致存在以下几种:

A、Web / Hybird 方案;

B、JS + Native 方案;

C、Android插件化方案。

三种方案各有优缺点,具体优缺点对比以及技术选型不是本文重点,这里不再展开讨论,感兴趣的同学可自行查阅相关文章。我们选取的是第二种方案:JS+Native解决方案。

JS+Native动态部署方案,其原理是:通过JS与native交互协议,用JS代码操作原生系统的UI组件,代替DOM元素来渲染。由于最终渲染是由Native完成,所以即实现了跨平台动态更新的特性,也保证了流畅的用户体验。市面上较流行的解决方案有:

React Native:是由Facebook开源的跨平台移动应用开发框架。支持用开源的JavaScript库React.js来开发iOS和Android原生App。其主张“Learn once, write everywhere”。

WEEX:WEEX是一款基于通用跨平台的 Web 开发语言和开发经验,来构建 Android、iOS 和 Web 应用的开发框架。目前 前端框架Vue 和 Rax被广泛应用于 Weex 页面开发。其宣称“Write Once, Run Everywhere”。

DU也是基于JS+Native原理。它更加轻量,是面向Android研发人员设计的一款动态开发框架。它的设计思路遵循招才猫动态部署框架整体的思路,使Android/iOS  Native开发的小伙伴上手机开发成本低,甚至可忽略。下图是用DU框架开发的页面,大家可以看出,与我们Android开发规范相似度非常高。

看了demo代码,相信大家对DU的开发有了个初步的认识。下面我将向大家详细介绍DU开发框架原理与实现。

DU框架原理

上一章已提到,DU的技术方案为JS+Native。具体方案如下图:

协议通信层:DU框架,选取了Google的JavaScript引擎V8,实现JavaScript和Android的相互调用。在JS端以及Native分别对协议进行了封装与解析。DU所有的数据交互,都是以此为基础来扩展的。
JS端SDK:DU围绕协议层,封装了对上层业务支持的各个功能模块,并提供DynamicUpdateAPI,供JS业务层调用。如页面组件、类转换器、资源管理、配置管理、扩展模块、崩溃收集等,这些模块的封装,会通过协议层传输到Native端,具体的实现,都由Native端完成。
Native端:则是对JS端定义的模块具体实现。如网络扩展模块,会接受JS端传递的网络协议数据,然后调用Native网络框架(如OKHttp)去执行真正的网络请求,并将结果通过协议层,会传JS端。
JS业务层:业务逻辑具体实现层,通过调用JS端提供的DynamicUpdateAPI,完成需求功能的开发工作。
DU框架完整的运转流程如下图:

DU渲染原理

上文提到过,DU是一款面向Android开发人员的动态开发框架,主要体现在页面创建、生命周期管理以及组件间通讯等封装,都与Android Native开发规范高度保持一致。除了JS语言与Java语言在编写方式上稍有差别外,Android同学在使用DU过程中,会有一种熟悉感扑面而来。下面向大家介绍DU页面渲染以及生命周期管理的原理。

1、渲染原理

DU的布局文件、文案定义、颜色、尺寸定义,都与Android完全一致,都是用xml定义,就连文件命名也延续了Android的命名;

DU的图片,放到了固定的图片文件夹中,在布局文件中使用的时候,只要定义$drawable/xxx即可加载对应的图片文件;

DU框架里面封装了加载DU页面的DUActivity以及DUFragment。在启动DU页面时,将布局文件与页面进行关联。在Activity/Fragment创建的时候,会自动创建资源解析器与构建器。

资源解析器会将布局文件以及布局中用到的颜色、尺寸、图片等信息,进行解析转换,并交给Views生成器,生成一个个带有完整描述的组件,这些组件会形成ViewsTree。

转换器将生成的ViewsTree交给构建器。构建模块会根据各个控件的描述,创建对应的组件,并添加到DecorView上,并为有交互的控件添加事件监听。

至此,DU完成了页面的渲染。

2、生命周期 DU将Android的生命周期,映射到了JS页面的生命周期,使JS可以在生命周期内,处理自己的业务逻辑。具体方式如下图:

DU同样支持带有启动模式的Activity。由于Android的Activity需要在AndroidManifest.xml注册,DU采用了占坑的方式,提前在AndroidManifest.xml注册了带有启动模式的Activity。

3、UI扩展 DU已经对构建页面的基本组件做了封装。如常用的Layout、Button、ListView、CheckBox等。但是业务错综复杂,交互设计千变万化。显然一个框架是不能覆盖全部页面元素的。这里DU提供了UI扩展功能,只要按照DU的扩展规范,即可轻松创建属于你的UI界面。具体方法如下:

JSUIComponents,是对应控件的调用封装。如封装JSButton对应的各种方法调用。

UIComponents组件,就是现有APP自己创建的控件,也可以是系统封装的控件,如Button。

Proxy,是控件代理层。代理层有两个作用:

A、封装属性代理。通过代理里面封装的Property,为不同控件设置不同的属性,并且Property是可以继承的,相应的也就是控件的属性可以继承。如DUViewGroupProxy.Property 继承自DUViewProxy.Property,所以DUViewGroup不但拥有了自己的属性,同时也拥有了DUView的属性。这样可以避免重复定义控件属性,增加属性复用性。

B、将调用与实现隔离。如Button控件,在招才猫端对应的就是招才猫封装的ZCMButton,但是对应其他APP,就会是xxxButton。有了代理层, 在集成DU框架时,可以实现控件的快速切换,而不是再封装一套。

view_config配置文件,此文件主要作用是将View与Viewproxy做映射关系。

layout.xml,为布局文件。

以上为Native端的实现。

SDK里面已经封装了交互协议以及View生成器。在SDK初始化的时候,已经根据view_config.xml的配置信息,将View与ViewProxy做了一一对应。在渲染页面时,view生成器会根据layout创建相应控件,并通过ViewProxy,调用view的setxxx方法,为view属性赋值。

DU模块扩展

使用DU框架进行混合开发,不可避免的要使用一些Native已经封装好功能,比如分享、网络请求、支付等等。为解决此类使用场景,DU提供了扩展功能。具体原理如图:

与UI扩展类似:

jSExtendsible封装了扩展模块的方法调用;

NativeExtengdsible,是具体扩展功能的实际执行方;

extendsible_config.xml将jSExtendsible与NativeExtengdsible做了映射关系;

SDK内部,通过ModuleManager,将jSExtendsible调用协议解析、并交给NativeExtengdsible执行,最后将执行结果返回。

DU类转换器

JS是弱类型语言,在DU的协议传输中,DU框架将所有数据转为字符串传输。而Android端的具体执行,其实都是根据协议,通过反射调用相应功能模块来完成的。Java是强类型语言,这就需要将协议中方法调用的参数做强类型转换。

例如:JS端调用textView.setHeight(12)设置高度,协议传输过程中,12是字符串,调用的是Android端TextView的setHeight(int pixels) 方法。这时需要将字符串”12“转换为int类型。类转换器处理的就是类似场景。

目前DU已经封装了30几种类转换器,基本可以覆盖常规开发需要。接入方也可以根据实际情况,扩展自己的类转换器。具体原理以及实现过程和UI扩展、模块扩展一致,如下图。这里不再赘述。

DU踩的坑

以上, DU动态框架核心技术点已给大家分享完毕。当然,我们在开发过程中,遇到了各种各样的问题,在这里和大家做个分享,采坑经验最宝贵:


1、JS引擎的选择

在DU行程框架诞生之前,招才猫使用了以WebView为引擎,作为协议中转核心,通过JS,创建Native页面并执行相关逻辑,实现了发布页面的动态化。

在后续优化中,在发布页面基础上,提取了通用协议,并做了更加丰富的封装,产出了第一版DU,同样,第一版DU框架的核心引擎也是WebView。WebView在这里的作用仅仅是执行JS,并负责与Native通讯。可以想见,WebView页面绘制等功能并没有使用,但是我们仍会进行初始化,造成了资源浪费。经过对比与调研,我们最终引入了V8,作为DU框架的JS解析执行以及通讯引擎,减少了DU在运行时的资源消耗。


2、复杂动画的处理

在需求开发中,必然会用到各种动画效果,DU框架已支持常用的动效实现,如Animation、Transition等,DU已做了封装,可以直接使用。但在我们使用过程中发现,简单动效还好,DU完全能够胜任。一旦在DU中实现比较复杂的动效时,页面就会出现卡顿现象。经过试验与分析,我们发现在动效执行过程中,JS与Native会进行频繁交互,在协议传输、解析过程中,由于数据传输过于频繁,造成通讯模块处理延迟,导致页面卡顿。

在解决动画问题上,我们针对动效交互的协议做了大量优化,但收益不佳。在后续优化中,我们放弃了在动效交互协议上做文章,而是考虑其他解决方案。得益于我们UI扩展能力,在开发实践中,我们将复杂动效封装成UI组件,将动效完全交由Native实现,JS端仅仅作为调用方。这样,就不存在动效执行中,协议交互频繁的问题了。此方案的坏处就是如果动效组件在之前版本中没有实现过,还是需要跟随版本上线。不过在实践中,需要特别复杂动效的情况,少之又少。DU完全能够胜任目前的需求。

结语

除以上框架核心技术内容,DU框架还有其他优秀的技术实现,如高效的DUListView、框架安全、崩溃采集等,限于篇幅原因,我们不在此一一赘述,欢迎大家线下相互交流、学习。

参考文献

1、https://reactnative.cn/
2、https://weex.apache.org/zh/guide/introduction.html
3、https://nodejs.org/en/
4、https://github.com/eclipsesource/J2V8
5、https://developer.android.com/docs

作者简介

黄金鑫,HRG技术部资深开发工程师,目前主要负责招才猫直聘安卓端相关业务开发与维护工作,对移动Android端相关技术,尤其是动态框架开发方面有一定研究。

阅读推荐