前言

最近开始试水Weex开发,使用这么长一段时间,感觉写Weex还是非常方便的。作为一个Android开发,免不了要追查一下weex的sdk源码。今天,就以Weex SDK for Android为例,分析SDK的

认识Weex SDK

整体分析下拉,按照js文件的渲染过程,绘制出了下面架构图:

4deb772671370daf030ff83a342523e1.png

sdk_framework

WEEX文件渲染过程

为了更加详细的说明整个渲染过程,我对源码进行了分析。并结合示例,进行了日志分析;比如,我们要开发如下一个简单的组件(红色方框内):

68f0bff074add6a67b9265ab6dca96ea.png

飞猪app购物车

那么,我们的wxc-title.we源码为:

{{item.name}}

.container {

position: relative;

flex-direction: row;

width: 750px;

height: 60px;

align-items: center;

}

.image {

margin-left: 100px;

width: 45px;

height: 45px;

}

.text {

margin-left: 10px;

font-size: 28px;

color: #444444;

}

module.exports = {

data: {

item: {

pic: '//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png',

name: '当地玩乐'

}

},

methods: {

}

}

上述.we文件经过weex编译之后,生成的js文件,经过格式化如下:

...

([function (module, exports) {

module.exports = {

"type": "div",

"classList": [

"container"

],

"children": [

{

"type": "image",

"classList": [

"image"

],

"attr": {

"src": function () {

return this.item.pic

}

}

},

{

"type": "text",

"classList": [

"text"

],

"attr": {

"value": function () {

return this.item.name

}

}

}

]

}

}, function (module, exports) {

module.exports = {

"container": {

"position": "relative",

"flexDirection": "row",

"width": 750,

"height": 60,

"alignItems": "center"

},

"image": {

"marginLeft": 100,

"width": 45,

"height": 45

},

"text": {

"marginLeft": 10,

"fontSize": 28,

"color": "#444444"

}

}

}, function (module, exports) {

module.exports = function (module, exports, __weex_require__) {

'use strict';

module.exports = {

data: function () {

return {

item: {

pic: '//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png',

name: '当地玩乐'

}

}

},

methods: {}

};

}

}

]);

上述分别使用了三个function,对template、style和script进行了封装;那么,这个文件是怎么被weex sdk执行并解析,最终生成结构化的View的呢?

渲染过程

时序图1:

从扫码开始,首先经历如下过程;依次经过readerPage,createInstance,一直到WXBridge的exeJs方法;也就是说,最终,Java通过调用native的exeJs方法,来执行js文件的。

d9220de876e9c1d4d4e5fbb2b16a50f5.png

https://img.alicdn.com/tps/TB1CKC.OFXXXXXkXpXXXXXXXXXX-2530-840.png

时序图2:

紧接着时序图1:执行到JNI层的Java_com_taobao_weex_bridge_WXBridge_execJS方法;

58dcd88b1cffa26f092fce47205f3c48.png

https://img.alicdn.com/tps/TB1K2uBOFXXXXbnaXXXXXXXXXXX-2374-1352.png

然后js通过native调用WXBridge的callNative方法,达到js调用Java代码的目的;

JNI层的部分代码如下:

jint Java_com_taobao_weex_bridge_WXBridge_execJS(JNIEnv *env, jobject this1, jstring jinstanceid,

jstring jnamespace, jstring jfunction,

jobjectArray jargs) {

v8::HandleScope handleScope;

v8::Isolate::Scope isolate_scope(globalIsolate);

v8::Context::Scope ctx_scope(V8context);

v8::TryCatch try_catch;

int length = env->GetArrayLength(jargs);

v8::Handle<:value> obj[length];

jclass jsObjectClazz = (env)->FindClass("com/taobao/weex/bridge/WXJSObject");

for (int i = 0; i < length; i++) {

jobject jArg = (env)->GetObjectArrayElement(jargs, i);

jfieldID jTypeId = (env)->GetFieldID(jsObjectClazz, "type", "I");

jint jTypeInt = env->GetIntField(jArg, jTypeId);

jfieldID jDataId = (env)->GetFieldID(jsObjectClazz, "data", "Ljava/lang/Object;");

jobject jDataObj = env->GetObjectField(jArg, jDataId);

if (jTypeInt == 1) {

jclass jDoubleClazz = (env)->FindClass("java/lang/Double");

jmethodID jDoubleValueId = (env)->GetMethodID(jDoubleClazz, "doubleValue", "()D");

jdouble jDoubleObj = (env)->CallDoubleMethod(jDataObj, jDoubleValueId);

obj[i] = v8::Number::New((double) jDoubleObj);

env->DeleteLocalRef(jDoubleClazz);

} else if (jTypeInt == 2) {

jstring jDataStr = (jstring) jDataObj;

obj[i] = jString2V8String(env, jDataStr);

} else if (jTypeInt == 3) {

v8::Handle<:value> jsonObj[1];

v8::Handle<:object> global = V8context->Global();

json = v8::Handle<:object>::Cast(global->Get(v8::String::New("JSON")));

json_parse = v8::Handle<:function>::Cast(json->Get(v8::String::New("parse")));

jsonObj[0] = jString2V8String(env, (jstring) jDataObj);

v8::Handle<:value> ret = json_parse->Call(json, 1, jsonObj);

obj[i] = ret;

}

env->DeleteLocalRef(jDataObj);

env->DeleteLocalRef(jArg);

}

env->DeleteLocalRef(jsObjectClazz);

const char *func = (env)->GetStringUTFChars(jfunction, 0);

v8::Handle<:object> global = V8context->Global();

v8::Handle<:function> function;

v8::Handle<:value> result;

if (jnamespace == NULL) {

function = v8::Handle<:function>::Cast(global->Get(v8::String::New(func)));

result = function->Call(global, length, obj);

}

else {

v8::Handle<:object> master = v8::Handle<:object>::Cast(

global->Get(jString2V8String(env,jnamespace)));

function = v8::Handle<:function>::Cast(

master->Get(jString2V8String(env,jfunction)));

result = function->Call(master, length, obj);

}

if (result.IsEmpty()) {

assert(try_catch.HasCaught());

ReportException(globalIsolate, &try_catch, jinstanceid, func);

env->ReleaseStringUTFChars(jfunction, func);

env->DeleteLocalRef(jfunction);

return false;

}

env->ReleaseStringUTFChars(jfunction, func);

env->DeleteLocalRef(jfunction);

return true;

}

}

时序图3:createBody&generateComponentTree

接着上面的时序图,开始做页面的创建;关键的代码在WXRenderStatement中的createBodyOnDomThread,该方法通过创建跟布局的mGodComponent,通过递归generateComponentTree生成Component的逻辑树结构;然后,在WXRenderStatement的createBody方法中,生成View,绑定属性和数据;具体如下图所示:

98f9cc1ff10d1a09e51088fdef55289f.png

https://img.alicdn.com/tps/TB1OTG7OFXXXXbYXpXXXXXXXXXX-2528-1184.png

时序图4:addElement

b9bdc35d9f5c440503ecf926c6ed5621.png

https://img.alicdn.com/tps/TB1xVe5OFXXXXc3XpXXXXXXXXXX-2508-1002.png

时序图5:callNative调用Module

930efb9fa02451d87c58477f684a8a7b.png

https://img.alicdn.com/tps/TB1MSi4OFXXXXb9XpXXXXXXXXXX-1438-860.png

调用过程日志记录

以上面的weex页面为例:使用PlayGround扫码之后的调用过程中的日志为:

12-04 15:51:04.705: D/weex(30188): ###render in WXSDKInstance. pageName = WXPageActivity,template = /******/ (function(modules) { // webpackBootstrap

12-04 15:51:04.705: D/weex(30188): ###createInstance in WXSDKManager code = /******/ (function(modules) { // webpackBootstrap

12-04 15:51:04.710: D/weex(30188): ###createInstance in WXBrideManager instanceId = 3,template = /******/ (function(modules) { // webpackBootstrap

12-04 15:51:04.710: D/weex(30188): ###invokeCreateInstance in WXBrideManager instanceId = 3,template = /******/ (function(modules) { // webpackBootstrap

12-04 15:51:04.725: D/weex(30188): ###execJS instanceId = 3,namespace = null,function = createInstance,args = [{"data":"3","type":2},{"data":"/******/ (function(modules) { // webpackBootstrap\n .......

12-04 15:51:04.740: D/weex(30188): ###callNative in WXBridge instanceId = 3,tasks = [{"module":"dom","method":"createBody","args":[{"ref":"_root","type":"div","attr":{},"style":{"position":"relative","flexDirection":"row","width":750,"height":60,"alignItems":"center"}}]}],callback = -1

12-04 15:51:04.740: D/weex(30188): ###callDomMethod to create component...task = {"args":[{"attr":{},"ref":"_root","style":{"alignItems":"center","flexDirection":"row","height":60,"position":"relative","width":750},"type":"div"}],"method":"createBody","module":"dom"}

12-04 15:51:04.745: D/weex(30188): ###callDomMethod task = {"args":[{"attr":{},"ref":"_root","style":{"alignItems":"center","flexDirection":"row","height":60,"position":"relative","width":750},"type":"div"}],"method":"createBody","module":"dom"}

12-04 15:51:04.745: D/weex(30188): ###createBody element = {"attr":{},"ref":"_root","style":{"alignItems":"center","flexDirection":"row","height":60,"position":"relative","width":750},"type":"div"}

12-04 15:51:04.745: D/weex(30188): ###handleMessage in WXDomHandler...what = 0,obj = {"args":[{"attr":{},"ref":"_root","style":{"alignItems":"center","flexDirection":"row","height":60,"position":"relative","width":750},"type":"div"}],"instanceId":"3"}

12-04 15:51:04.750: D/weex(30188): ###createBodyOnDomThread in WXRenderStatement dom = layout: {left: 0.0, top: 0.0, width: 0.0, height: 0.0, direction: LTR}direction =INHERIT

12-04 15:51:04.750: D/weex(30188): ###createView in WXComponent className = WXDiv

12-04 15:51:04.755: D/weex(30188): ###generateComponentTree in WXRenderStatement component = WXDiv

12-04 15:51:04.755: D/weex(30188): ###callAddElement in WXBridge instanceId = 3,ref = _root,dom = {"ref":"153","type":"image","attr":{"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"},"style":{"marginLeft":100,"width":45,"height":45}},index=-1,callback = -1

12-04 15:51:04.755: D/weex(30188): ###callAddElement in WXBridgeManager instanceId = 3,ref = _root,dom = {"ref":"153","type":"image","attr":{"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"},"style":{"marginLeft":100,"width":45,"height":45}},index = -1,callback = -1

12-04 15:51:04.760: D/weex(30188): ###addElement parentRef = _root,element = {"attr":{"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"},"ref":"153","style":{"height":45,"marginLeft":100,"width":45},"type":"image"},index = -1

12-04 15:51:04.760: D/weex(30188): ###handleMessage in WXDomHandler...what = 3,obj = {"args":["_root",{"attr":{"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"},"ref":"153","style":{"height":45,"marginLeft":100,"width":45},"type":"image"},-1],"instanceId":"3"}

12-04 15:51:04.760: D/weex(30188): ###callAddElement in WXBridge instanceId = 3,ref = _root,dom = {"ref":"154","type":"text","attr":{"value":"当地玩乐"},"style":{"marginLeft":10,"fontSize":28,"color":"#444444"}},index=-1,callback = -1

12-04 15:51:04.765: D/weex(30188): ###callAddElement in WXBridgeManager instanceId = 3,ref = _root,dom = {"ref":"154","type":"text","attr":{"value":"当地玩乐"},"style":{"marginLeft":10,"fontSize":28,"color":"#444444"}},index = -1,callback = -1

12-04 15:51:04.765: D/weex(30188): ###addElement parentRef = _root,element = {"attr":{"value":"当地玩乐"},"ref":"154","style":{"color":"#444444","fontSize":28,"marginLeft":10},"type":"text"},index = -1

12-04 15:51:04.765: D/weex(30188): ###addDom in WXDomManager instanceId = 3,parentRef = _root,element = {"attr":{"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"},"ref":"153","style":{"height":45,"marginLeft":100,"width":45},"type":"image"},index = -1

12-04 15:51:04.765: D/weex(30188): ###callNative in WXBridge instanceId = 3,tasks = [{"module":"dom","method":"createFinish","args":[]}],callback = -1

12-04 15:51:04.770: D/weex(30188): ###addDom in WXDomStatement dom = {"attr":{"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"},"ref":"153","style":{"height":45,"marginLeft":100,"width":45},"type":"image"},parentRef = _root,index = -1

12-04 15:51:04.770: D/weex(30188): ###createComponentOnDomThread in WXRenderManager dom = layout: {left: 0.0, top: 0.0, width: 0.0, height: 0.0, direction: LTR}direction =INHERIT

12-04 15:51:04.770: D/weex(30188): ###createComponentOnDomThread in WXRenderStatement dom = layout: {left: 0.0, top: 0.0, width: 0.0, height: 0.0, direction: LTR}direction =INHERIT

12-04 15:51:04.770: D/weex(30188): ###generateComponentTree in WXRenderStatement component = WXImage

12-04 15:51:04.775: D/weex(30188): ###callDomMethod to create component...task = {"args":[],"method":"createFinish","module":"dom"}

12-04 15:51:04.775: D/weex(30188): ###handleMessage in WXDomHandler...what = 255,obj = null

12-04 15:51:04.775: D/weex(30188): ###callDomMethod task = {"args":[],"method":"createFinish","module":"dom"}

12-04 15:51:04.775: D/weex(30188): ###createFinish

12-04 15:51:04.775: D/weex(30188): ###handleMessage in WXDomHandler...what = 3,obj = {"args":["_root",{"attr":{"value":"当地玩乐"},"ref":"154","style":{"color":"#444444","fontSize":28,"marginLeft":10},"type":"text"},-1],"instanceId":"3"}

12-04 15:51:04.775: D/weex(30188): ###addDom in WXDomManager instanceId = 3,parentRef = _root,element = {"attr":{"value":"当地玩乐"},"ref":"154","style":{"color":"#444444","fontSize":28,"marginLeft":10},"type":"text"},index = -1

12-04 15:51:04.780: D/weex(30188): ###addDom in WXDomStatement dom = {"attr":{"value":"当地玩乐"},"ref":"154","style":{"color":"#444444","fontSize":28,"marginLeft":10},"type":"text"},parentRef = _root,index = -1

12-04 15:51:04.780: D/weex(30188): ###createComponentOnDomThread in WXRenderManager dom = layout: {left: 0.0, top: 0.0, width: 0.0, height: 0.0, direction: LTR}direction =INHERIT

12-04 15:51:04.780: D/weex(30188): ###createComponentOnDomThread in WXRenderStatement dom = layout: {left: 0.0, top: 0.0, width: 0.0, height: 0.0, direction: LTR}direction =INHERIT

12-04 15:51:04.785: D/weex(30188): ###generateComponentTree in WXRenderStatement component = WXText

12-04 15:51:04.785: D/weex(30188): ###handleMessage in WXDomHandler...what = 9,obj = {"instanceId":"3"}

12-04 15:51:04.790: D/weex(30188): ###handleMessage in WXDomHandler...what = 255,obj = null

12-04 15:51:04.820: D/weex(30188): ###createBody in WXRenderStatement component = WXDiv

12-04 15:51:04.820: D/weex(30188): ###createView in WXComponent className = WXDiv

12-04 15:51:04.820: D/weex(30188): ###applyLayoutAndEvent in WXComponent className = WXDiv

12-04 15:51:04.825: D/weex(30188): ###bindData in WXContainer

12-04 15:51:04.825: D/weex(30188): ###bindData in WXComponent = WXDiv

12-04 15:51:04.825: D/weex(30188): ###updateProperties in props = {"alignItems":"center","backgroundColor":"#ffffff","flexDirection":"row","height":60,"position":"relative","width":750}

12-04 15:51:04.825: D/weex(30188): ###setProperty in WXComponent = WXDiv

12-04 15:51:04.825: D/weex(30188): ###setProperty in WXComponent = WXDiv

12-04 15:51:04.825: D/weex(30188): ###setProperty in WXComponent = WXDiv

12-04 15:51:04.825: D/weex(30188): ###setProperty in WXComponent = WXDiv

12-04 15:51:04.825: D/weex(30188): ###setProperty in WXComponent = WXDiv

12-04 15:51:04.830: D/weex(30188): ###setProperty in WXComponent = WXDiv

12-04 15:51:04.830: D/weex(30188): ###updateProperties in props = {}

12-04 15:51:04.830: D/weex(30188): ###addComponent in WXRenderManager instanceId = 3,component = WXImage,parentRef = _root,index = -1

12-04 15:51:04.830: D/weex(30188): ###addComponent in WXRenderStatement to start render the component to view...

12-04 15:51:04.835: D/weex(30188): ###createView in WXComponent className = WXImage

12-04 15:51:04.835: D/weex(30188): ###applyLayoutAndEvent in WXComponent className = WXImage

12-04 15:51:04.835: D/weex(30188): ###bindData in WXComponent = WXImage

12-04 15:51:04.835: D/weex(30188): ###updateProperties in props = {"height":45,"marginLeft":100,"width":45}

12-04 15:51:04.835: D/weex(30188): ###setProperty in WXComponent = WXImage

12-04 15:51:04.835: D/weex(30188): ###setProperty in WXComponent = WXImage

12-04 15:51:04.835: D/weex(30188): ###setProperty in WXComponent = WXImage

12-04 15:51:04.840: D/weex(30188): ###updateProperties in props = {"src":"//img.alicdn.com/tfs/TB1AEcQNXXXXXX8XXXXXXXXXXXX-50-50.png"}

12-04 15:51:04.840: D/weex(30188): ###addComponent in WXRenderManager instanceId = 3,component = WXText,parentRef = _root,index = -1

12-04 15:51:04.840: D/weex(30188): ###addComponent in WXRenderStatement to start render the component to view...

12-04 15:51:04.840: D/weex(30188): ###createView in WXComponent className = WXText

12-04 15:51:04.840: D/weex(30188): ###applyLayoutAndEvent in WXComponent className = WXText

12-04 15:51:04.840: D/weex(30188): ###bindData in WXComponent = WXText

12-04 15:51:04.840: D/weex(30188): ###updateProperties in props = {"color":"#444444","fontSize":28,"marginLeft":10}

12-04 15:51:04.845: D/weex(30188): ###setProperty in WXComponent = WXText

12-04 15:51:04.845: D/weex(30188): ###updateProperties in props = {"value":"当地玩乐"}

12-04 15:51:04.850: D/weex(30188): ###execJS instanceId = 3,namespace = null,function = callJS,args = [{"data":"3","type":2},{"data":"[{\"args\":[\"_root\",\"viewappear\",null,null],\"method\":\"fireEvent\"}]","type":3}]

12-04 15:51:04.850: D/weex(30188): ###callNative in WXBridge instanceId = 3,tasks = [{"module":"dom","method":"updateFinish","args":[]}],callback = -1

12-04 15:51:04.855: D/weex(30188): ###callDomMethod to create component...task = {"args":[],"method":"updateFinish","module":"dom"}

12-04 15:51:04.855: D/weex(30188): ###callDomMethod task = {"args":[],"method":"updateFinish","module":"dom"}

12-04 15:51:04.855: D/weex(30188): ###updateFinish

12-04 15:51:04.860: D/weex(30188): ###handleMessage in WXDomHandler...what = 11,obj = {"instanceId":"3"}

12-04 15:51:04.875: D/weex(30188): ###handleMessage in WXDomHandler...what = 255,obj = null

View绘制过程对比

首先,我们看一下Android的View绘制过程:

667933e63fa439338d328edc049f9320.png

android_view

主要是measure测量大小,layout确定位置。

其次,我们对比一下Weex的WXComponent的测量和布局过程;

29ce30f79e1d79741b8692bab3cbcd19.png

主要是通过CSSLayout进行测量,使用view的setLayoutParams来确定View在父ViewGroup中的位置。

核心代码如下:

Spacing parentPadding = mParent.getDomObject().getPadding();

Spacing parentBorder = mParent.getDomObject().getBorder();

Spacing margin = mDomObj.getMargin();

int realWidth = (int) mDomObj.getLayoutWidth();

int realHeight = (int) mDomObj.getLayoutHeight();

int realLeft = (int) (mDomObj.getLayoutX() - parentPadding.get(Spacing.LEFT) -

parentBorder.get(Spacing.LEFT));

int realTop = (int) (mDomObj.getLayoutY() - parentPadding.get(Spacing.TOP) -

parentBorder.get(Spacing.TOP));

int realRight = (int) margin.get(Spacing.RIGHT);

int realBottom = (int) margin.get(Spacing.BOTTOM);

if (mPreRealWidth == realWidth && mPreRealHeight == realHeight && mPreRealLeft == realLeft && mPreRealTop == realTop) {

return;

}

if (mParent != null) {

mAbsoluteY = (int) (mParent.mAbsoluteY + mDomObj.getLayoutY());

mAbsoluteX = (int) (mParent.mAbsoluteX + mDomObj.getLayoutX());

}

//calculate first screen time

if (!mInstance.mEnd && !(mHost instanceof ViewGroup) && mAbsoluteY + realHeight > mInstance.getWeexHeight() + 1) {

mInstance.firstScreenRenderFinished();

}

if (mHost == null) {

return;

}

MeasureOutput measureOutput = measure(realWidth, realHeight);

realWidth = measureOutput.width;

realHeight = measureOutput.height;

if (mHost instanceof WXCircleIndicator) {

FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(realWidth, realHeight);

params.setMargins(realLeft, realTop, realRight, realBottom);

mHost.setLayoutParams(params);

return;

}

//fixed style

if (mDomObj.isFixed() && mInstance.getRootView() != null) {

if (mHost.getParent() instanceof ViewGroup) {

ViewGroup viewGroup = (ViewGroup) mHost.getParent();

viewGroup.removeView(mHost);

}

FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,

ViewGroup.LayoutParams.WRAP_CONTENT);

params.width = realWidth;

params.height = realHeight;

params.setMargins(realLeft, realTop, realRight, realBottom);

mHost.setLayoutParams(params);

mInstance.getRootView().addView(mHost);

if (WXEnvironment.isApkDebugable()) {

WXLogUtils.d("Weex_Fixed_Style", "WXComponent:setLayout :" + realLeft + " " + realTop + " " + realWidth + " " + realHeight);

WXLogUtils.d("Weex_Fixed_Style", "WXComponent:setLayout Left:" + mDomObj.getStyles().getLeft() + " " + (int) mDomObj.getStyles().getTop());

}

return;

}

...

else if (mParent.getRealView() instanceof BounceRecyclerView && this instanceof WXCell) {

RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) mHost.getLayoutParams();

if (params == null)

params = new RecyclerView.LayoutParams(realWidth, realHeight);

params.width = realWidth;

params.height = realHeight;

params.setMargins(realLeft, 0, realRight, 0);

mHost.setLayoutParams(params);

} else if (mParent.getRealView() instanceof BaseBounceView && this instanceof WXBaseRefresh) {

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(realWidth, realHeight);

realTop = (int) (parentPadding.get(Spacing.TOP) - parentBorder.get(Spacing.TOP));

params.setMargins(realLeft, realTop, realRight, realBottom);

mHost.setLayoutParams(params);

} else if (mParent.getRealView() instanceof FrameLayout) {

FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(realWidth, realHeight);

params.setMargins(realLeft, realTop, realRight, realBottom);

mHost.setLayoutParams(params);

} else if (mParent.getRealView() instanceof LinearLayout) {

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(realWidth, realHeight);

params.setMargins(realLeft, realTop, realRight, realBottom);

mHost.setLayoutParams(params);

} else if (mParent.getRealView() instanceof ScrollView) {

ScrollView.LayoutParams params = new ScrollView.LayoutParams(realWidth, realHeight);

params.setMargins(realLeft, realTop, realRight, realBottom);

mHost.setLayoutParams(params);

}

View渲染过程对比

渲染过程对比:

bc40fdbe99bcacfd0cd61a0d0326c8fb.png

weex_android

weex的渲染过程,上面已经写的比较清晰了;对于Android,其渲染过程大致可以总结为:

1.编译期使用aapt对xml进行编译,生成二进制的xml

2.运行时,使用XmlBlock构建XmlPullParser,通过LayoutInflater的rInflater进行解析,最终生成View;

具体详细过程,可以参看我的另外一遍博客:Android-LayoutInflater效率分析及源码跟踪

那么,两种方式的解析效率差异有多大呢?官方的数据如下:

3160a0297345?from=singlemessage

weex_ss

帧率对比

目前以飞猪app的购物车为例:Weex,Native,以及投放到手淘的H5,进行了帧率对比,数据如下:

fe4064629ad09aa73fcbc441d42f4d19.png

zhenlv

总结

weex无论在createBody、addElement,还是在callNative中对Module的调用,都还有很多优化空间。比如,可以把部分运行时的工作,搬到编译期做,这样可以加快页面的渲染时间;在渲染之后,滑动过程中的帧率对比发现,weex和native基本相近,比H5的表现要好。

附录

Logo

智屏生态联盟致力于大屏生态发展,利用大屏快应用技术降低开发者开发、发布大屏应用门槛

更多推荐