1、前言
不管是之前的初始化还是各种组件的注册,最终的目的都是将前端的js文件转换为客户端的Native视图,也就是渲染的过程。这一部分涉及的内容比较多,主要可以细分为js framework的加载和运行、js bundle的执行、事件的传递等
2、js framework的运行
系列一已经说到了SDK初始化js framework的过程,加载的就是weex-main-jsfm.js,是WeexSDK在生成aar时直接打包到assets目录下的。这一大坨字符串又是从哪来的?其实就是将weex源码工程的runtime目录下一堆js文件打包生成的:
runtime
|-- api:冻结原型链,提供给原生调用的方法,如registerComponents
|-- bridge:和客户端桥接的相关接口,如createBody等,指令最后会通过这里调用客户端
|-- entries:客户端执行js framework 的入口文件
|-- frameworks:核心文件,初始化js bundle实例、管理实例、返回错误码等
|-- services:broadcast调度转换等
|-- shared:抹平console、setTimeout等差异性的方法
|-- vdom:重要文件,将VDOM转化成客户端能渲染的指令
加载是通过WXBridge的initFrameworkEnv完成的,最后当然还是js引擎负责去解析weex-main-jsfm.js,在0.28.0版本中该引擎并未包含在weex sdk中,需要注意下。加载的结果返回1说明js framework加载成功,后续才能正常的使用weex的功能。执行js framework的入口是runtime/entries/setup.js,来看一下都干了些什么事情:
/**
* Setup frameworks with runtime.
* You can package more frameworks by
* passing them as arguments.
*/
export default function (frameworks) {
const { init, config } = runtime
config.frameworks = frameworks
const { native, transformer } = subversion
for (const serviceName in services) {
runtime.service.register(serviceName, services[serviceName])
}
runtime.freezePrototype()
// register framework meta info
global.frameworkVersion = native
global.transformerVersion = transformer
// init frameworks
const globalMethods = init(config)
// set global methods
for (const methodName in globalMethods) {
global[methodName] = (...args) => {
const ret = globalMethods[methodName](...args)
if (ret instanceof Error) {
console.error(ret.toString())
}
return ret
}
}
主要完成的功能是:
- 挂载原型链方法和全局属性方法
- 创建客户端通信桥
挂载原型链方法和全局属性方法
初始化中最主要的部分是通过runtime/api/init.js完成的,这里对原型链方法和全局属性方法进行挂载,说白了也是和客户端交互的接口,在这里能看到熟悉的registerComponents等:
* init: 页面内部生命周期初始化
* registerComponents:注册 Component
* registerMoudles:注册 Module
* createInstance: 页面内部生命周期创建
* refreshInstance: 页面内部生命周期刷新
* destroyInstance: 页面内部生命周期销毁
* getRoot:获取页面节点
.....
像registerComponents,就是前端缓存到map中,解析VDom的时候映射,然后发送指令给原生进行渲染。
创建客户端通信桥
既然是客户端要和前端交互,那肯定少不了桥。类似H5和客户端的交互,通信桥要实现的也是js调用native、native调用js。从api/init.js和bridge/TaskCenter.js能看到定义了一些供双方互相调用的方法。因为最终还是遵循的JS -> C++ -> Java的通信机制,所有桥的方法都定义在weexcore/Source/android/wrap/wx_brdige.h中。js调用native一个很明显的方法就是callNative,除此之外常用的还有callCreateBody、callAddElement等,native调用js一个最直接的方法就是execJS,其他常用的还有createInstanceContext、destroyInstance等。在上层都被定义在IWXBridge中,实现类是WXBridge。
3、渲染过程
渲染过程涉及到java、c++、javascript,细节比较多,主要可以拆分成下面几个部分:
3.1、页面实例的初始化
js framework已经准备好了,可以准备开始渲染界面了。WXSDKInstance是weex渲染页面的基本单元,创建一个最基本的weex容器:
public class MainActivity extends AppCompatActivity implements IWXRenderListener {
WXSDKInstance mWXSDKInstance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWXSDKInstance = new WXSDKInstance(this);
mWXSDKInstance.registerRenderListener(this);
mWXSDKInstance.renderByUrl(pageName, bundleUrl, null, null,WXRenderStrategy.APPEND_ASYNC);
}
@Override
public void onViewCreated(WXSDKInstance instance, View view) {
setContentView(view);
}
//其他回调
官方的示例中是传入url,从网络拉取js bundle然后加载执行,默认用SDK自带的通过DefaultWXHttpAdapter请求,内部使用了一个定长为3的FixedThreadPool,没有做过优化或处理,因此一般都是自己去处理网络请求这块,拿到js bundle然后通过WXSDKInstance#render去解析渲染。
3.2、渲染的准备阶段
总的来说,这一段从
WXSDKInstance#render开始,经由WXBridge从UI线程切换到子线程,给当前页面生成一个唯一id,并且将js bundle以及设备相关信息通过createInstanceContext传递给js引擎,然后核心部分由底层代码处理。这里有两个关键部分:一是js bundle,这里被包装成Wson的数据结构,解析为RenderObject,而RenderPage则负责关键部分。二是相关指令,主要是创建根视图和添加子元素,最终都会抽象成java层的Action执行