weex源码浅析(Android部分)一
来源:互联网 发布:mac双系统对电脑好吗 编辑:程序博客网 时间:2024/06/05 20:13
最近预研阿里的weex方案,同最近比较火的ReactNative比较类似,提供了一个一次编写三端共用的解决方案.
下面引用官方的描述
Weex 是一套简单易用的跨平台开发方案,能以 web 的开发体验构建高性能、可扩展的 native 应用,为了做到这些,Weex 与 Vue 合作,使用 Vue 作为上层框架,并遵循 W3C 标准实现了统一的 JSEngine 和 DOM API,这样一来,你甚至可以使用其他框架驱动 Weex,打造三端一致的 native 应用。
weex的语法借鉴了vue.js的语法,保留了MVVM的结构.具体语法官方链接:语法介绍
这里直接跳过关于weex的官方控件介绍以及绑定原理,具体的细节和实践步骤准可参考上面的链接.
我们直接来看这张图,这张图很清晰地描述了weex的工作原理.
Weex we 文件 ————–前端(we源码)
↓ (转换) ——————前端(构建过程)
JS Bundle —————–前端(JS Bundle代码)
↓ (部署) ——————服务器
在服务器上的JS bundle —-服务器
↓ (编译) —————— 客户端(JS引擎)
虚拟 DOM 树 ————— 客户端(Weex JS Framework)
↓ (渲染) —————— 客户端(渲染引擎)
Native视图 ————— 客户端(渲染引擎)
- Transformer(转换器): 一个Node JS工具, 转换Weex源码为JS Bundle
- Weex JS Framework(JS框架):运行于客户端的的JS框架,管理着Weex实例的运行。Weex实例由JS Bundle创建并构建起虚拟DOM树. 另外,它发送/接受 Native 渲染层产生的系统调用,从而间接的响应用户交互。
- Native引擎: 在不同的端上,有着不同的实现: iOS/Android/HTML5. 他们有着共同的组件设计, 模块API 和渲染效果. 所以他们能配合同样的 JS Framework 和 JS Bundle工作。
JS Bundle是由.we文件编译而成的js文件,weex js framenwork是基于chrome v8的js引擎,负责把js bundle解析成Vm Dom(json格式描述),作为js和native之间沟通的桥梁,主要的两个方法callJs()和callNative().
下面着重讲下android端的渲染过程,以官方的playground 作为demo工程.
weex的使用需要在Application中配置,序列图如下
WXApplication中调用WXSDKEngine初始化
WXSDKEngine.initialize(this, new InitConfig.Builder() //.setImgAdapter(new FrescoImageAdapter())// use fresco adapter .setImgAdapter(new ImageAdapter()) .setDebugAdapter(new PlayDebugAdapter()) .build() );
WXSDKEngine.initialize()的第二个参数以链式的方式设置一系列自定义的adapter,包括网络请求,图片加载,数据埋点之类,某些adapter,weex提供了基础版本的实现,另外一些默认为空.
initialize紧接着进入 doInitInternal,这里
private static void doInitInternal(final Application application,final InitConfig config){ WXEnvironment.sApplication = application; WXEnvironment.JsFrameworkInit = false; //调用WxBridgeManager post WXBridgeManager.getInstance().post(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); WXSDKManager sm = WXSDKManager.getInstance(); //如果没有自定义Adapter,则初始化为默认实现 if(config != null ) { sm.setIWXHttpAdapter(config.getHttpAdapter()); sm.setIWXImgLoaderAdapter(config.getImgAdapter()); sm.setIWXUserTrackAdapter(config.getUtAdapter()); sm.setIWXDebugAdapter(config.getDebugAdapter()); sm.setIWXStorageAdapter(config.getStorageAdapter()); if(config.getDebugAdapter()!=null){ config.getDebugAdapter().initDebug(application); } } //加载V8.so库 WXSoInstallMgrSdk.init(application); boolean isSoInitSuccess = WXSoInstallMgrSdk.initSo(V8_SO_NAME, 1, config!=null?config.getUtAdapter():null); if (!isSoInitSuccess) { return; } //初始化js Framework, weex_sdk下assets/main.js文件 sm.initScriptsFramework(config!=null?config.getFramework():null); WXEnvironment.sSDKInitExecuteTime = System.currentTimeMillis() - start; WXLogUtils.renderPerformanceLog("SDKInitExecuteTime", WXEnvironment.sSDKInitExecuteTime); } }); //注册 componnet, moudle register(); }
下面跟一下initScriptsFramework().通过handler post message 到WXBridgeManager中,核心代码如下
framework = WXFileUtils.loadAsset("main.js", WXEnvironment.getApplication());//调用libs/libweexv8.so中的native方法if(mWXBridge.initFramework(framework, assembleDefaultOptions())==INIT_FRAMEWORK_OK){ //省略部分代码 //init 成功注册DomModule registerDomModule(); commitAlert(IWXUserTrackAdapter.JS_FRAMEWORK,WXErrorCode.WX_SUCCESS); }
讲到很有必要通过一张图来介绍下weex中几个比较核心的类之间的关系
WXSDKEngine的方法均为static的,WXSDKInstance,WXBridgeManager,WXSDKManager都通过getInstance()获取单例对象.其中的registerComponent(),registerMoudle()等方法均通过WXBridgeManager.getInstance()调用对应的方法.
Application中的初始化工作完成之后,我们直接从IndexActivity的渲染开始
onCreate()中
//index.js为index.we进过编译之后的js bundlerenderPage(WXFileUtils.loadAsset("index.js", this),WEEX_INDEX_URL);
继续跟进
protected void renderPage(String template,String source,String jsonInitData){ AssertUtil.throwIfNull(mContainer,new RuntimeException("Can't render page, container is null")); Map<String, Object> options = new HashMap<>(); options.put(WXSDKInstance.BUNDLE_URL, source); //mInstace为WXSDKInstance的单例 mInstance.render( getPageName(), template,//index.js options, jsonInitData, ScreenUtil.getDisplayWidth(this), ScreenUtil.getDisplayHeight(this), WXRenderStrategy.APPEND_ASYNC); }
显然真正的实现在WXSDKInstance中
//生成唯一的一个递增的id,一个id代表一个页面mInstanceId = WXSDKManager.getInstance().generateInstanceId();WXSDKManager.getInstance().createInstance(this, template, options, jsonInitData);
跟到WXBridgeManager
private void invokeCreateInstance(String instanceId, String template, Map<String, Object> options, String data) { initFramework(""); //...省略 try { WXJSObject instanceIdObj = new WXJSObject(WXJSObject.String, instanceId); WXJSObject instanceObj = new WXJSObject(WXJSObject.String, template); WXJSObject optionsObj = new WXJSObject(WXJSObject.JSON, options == null ? "{}" : WXJsonUtils.fromObjectToJSONString(options)); WXJSObject dataObj = new WXJSObject(WXJSObject.JSON, data == null ? "{}" : data); WXJSObject[] args = {instanceIdObj, instanceObj, optionsObj, dataObj}; //通过JNI调用V8中execJs执行index.js invokeExecJS(instanceId, null, METHOD_CREATE_INSTANCE, args); } catch (Throwable e) { //... } } }
至此,我们已成功加载index.js到v8引擎中.
weex的js framwork(即assets/main.js)在生成js bundle的虚拟 dom和事件绑定的过程中会在js端调用callNative方法.
/** *Dispatch the native task to be executed. * @param instanceId {@link WXSDKInstance#mInstanceId} * @param tasks tasks to be executed * @param callback next tick id */ public int callNative(String instanceId, String tasks, String callback) { //... if(mDestroyedInstanceId!=null &&mDestroyedInstanceId.contains(instanceId)){ return IWXBridge.DESTROY_INSTANCE; } //... int size = array.size(); if (size > 0) { try { JSONObject task; for (int i = 0; i < size; ++i) { task = (JSONObject) array.get(i); if (task != null && WXSDKManager.getInstance().getSDKInstance(instanceId) != null) { if (TextUtils.equals(WXDomModule.WXDOM, (String) task.get(MODULE))) { //一个instanceId对应一个WXSDKInstace,register时保存在hashMap sDomModule = getDomModule(instanceId); sDomModule.callDomMethod(task); sDomModule.mWXSDKInstance = null; } else { WXModuleManager.callModuleMethod(instanceId, (String) task.get(MODULE), (String) task.get(METHOD), (JSONArray) task.get(ARGS)); } } } } catch (Exception e) { //... } //.. }
这里贴上一条log,分别对应上面的三个参数
[WXBridgeManager] callNative >>>> instanceId:1, tasks:[{"module":"dom","method":"createBody","args":[{"ref":"_root","type":"list","attr":{},"style":{}}]}], callback:-1
接着weex开始根据task开始构建原生的组件,跟进WXDomModule的callDomModule()
public void callDomMethod(JSONObject task) { if (task == null) { return; } String method = (String) task.get(WXBridgeManager.METHOD); JSONArray args = (JSONArray) task.get(WXBridgeManager.ARGS); if (method == null) { return; } try { switch (method) { case CREATE_BODY: if (args == null) { return; } createBody((JSONObject) args.get(0)); break; case UPDATE_ATTRS: if (args == null) { return; } updateAttrs((String) args.get(0), (JSONObject) args.get(1)); break; case UPDATE_STYLE: if (args == null) { return; } updateStyle((String) args.get(0), (JSONObject) args.get(1)); break; case REMOVE_ELEMENT: if (args == null) { return; } removeElement((String) args.get(0)); break; case ADD_ELEMENT: if (args == null) { return; } addElement((String) args.get(0), (JSONObject) args.get(1), (Integer) args.get(2)); break; case MOVE_ELEMENT: if (args == null) { return; } moveElement((String) args.get(0), (String) args.get(1), (Integer) args.get(2)); break; case ADD_EVENT: if (args == null) { return; } addEvent((String) args.get(0), (String) args.get(1)); break; case REMOVE_EVENT: if (args == null) { return; } removeEvent((String) args.get(0), (String) args.get(1)); break; case CREATE_FINISH: createFinish(); break; case REFRESH_FINISH: refreshFinish(); break; case UPDATE_FINISH: updateFinish(); break; case SCROLL_TO_ELEMENT: if (args == null) { return; } scrollToElement((String) args.get(0), (JSONObject) args.get(1)); break; case ADD_RULE: if (args == null) { return; } addRule((String) args.get(0), (JSONObject) args.get(1)); } } catch (IndexOutOfBoundsException e) { // no enougn args e.printStackTrace(); WXLogUtils.e("Dom module call miss arguments."); } catch (ClassCastException cce) { WXLogUtils.e("Dom module call arguments format error!!"); } }
跟到这里,weex的从.we文件渲染成android原生组件的逻辑就很清楚了.
最终的调用在WXDomStatement的createBody方法
void createBody(JSONObject element) { //... //创建WXDomObject并且遍历保存json中定义的style,attribute,event到WXDomObject WXDomObject domObject = parseInner(element); if(domObject==null){ return; } //如果没有定义.初始化这五种样式 Map<String, Object> style = new HashMap<>(5); if (!domObject.getStyles().containsKey(Constants.Name.FLEX_DIRECTION)) { style.put(Constants.Name.FLEX_DIRECTION, "column"); } if (!domObject.getStyles().containsKey(Constants.Name.BACKGROUND_COLOR)) { style.put(Constants.Name.BACKGROUND_COLOR, "#ffffff"); } //If there is height or width in JS, then that value will override value here. if ( !domObject.getStyles().containsKey(Constants.Name.WIDTH)) { style.put(Constants.Name.WIDTH, WXViewUtils.getWebPxByWidth(WXViewUtils.getWeexWidth(mInstanceId))); domObject.setModifyWidth(true); } if ( !domObject.getStyles().containsKey(Constants.Name.HEIGHT)) { style.put(Constants.Name.HEIGHT, WXViewUtils.getWebPxByWidth(WXViewUtils.getWeexHeight(mInstanceId))); domObject.setModifyHeight(true); } WXDomObject.prepareRoot(domObject); domObject.updateStyle(style); //存储css样式到WXDomObject transformStyle(domObject, true); try { //真正开始由dom替换成native控件,==>1 final WXComponent component = mWXRenderManager.createBodyOnDomThread(mInstanceId, domObject); AddDomInfo addDomInfo = new AddDomInfo(); addDomInfo.component = component; mAddDom.put(domObject.getRef(), addDomInfo); mNormalTasks.add(new IWXRenderTask() { @Override public void execute() { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId); if (instance == null || instance.getContext() == null) { WXLogUtils.e("instance is null or instance is destroy!"); return; } try { mWXRenderManager.createBody(mInstanceId, component); } catch (Exception e) { WXLogUtils.e("create body failed.", e); } } @Override public String toString() { return "createBody"; } }); //... }
1.
WXComponent createBodyOnDomThread(WXDomObject dom) { if (mWXSDKInstance == null) { return null; } WXDomObject domObject = new WXDomObject(); WXDomObject.prepareGod(domObject); //初始化根节点,默认为container类型 mGodComponent = (WXVContainer) WXComponentFactory.newInstance(mWXSDKInstance, domObject, null); mGodComponent.createView(null, -1); //对应android的根节点是FrameLayout FrameLayout frameLayout = (FrameLayout) mGodComponent.getHostView(); ViewGroup.LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); frameLayout.setLayoutParams(layoutParams); frameLayout.setBackgroundColor(Color.TRANSPARENT); //根据dom生成对应的native控件 WXComponent component = generateComponentTree(dom, mGodComponent); mGodComponent.addChild(component); mRegistry.put(component.getRef(), component); return component; }
private WXComponent generateComponentTree(WXDomObject dom, WXVContainer parent) { //这一句话很重要,调用工厂方法获得实例 WXComponent component = WXComponentFactory.newInstance(mWXSDKInstance, dom, parent, parent.isLazy()); mRegistry.put(dom.getRef(), component); //如果是viewgroup,递归 if (component instanceof WXVContainer) { WXVContainer parentC = (WXVContainer) component; int count = dom.childCount(); WXDomObject child = null; for (int i = 0; i < count; ++i) { child = dom.getChild(i); if (child != null) { parentC.addChild(generateComponentTree(child, parentC)); } } } return component; }
newInstance的核心部分
//从sTypeComponentMap的hashmap中获得对应的控件(registerComponent时会保存到这个hashmap)IFComponentHolder holder = WXComponentRegistry.getComponent(node.getType());//反射获取控件实例或者直接调用对应控件的createInstancereturn holder.createInstance(instance, node, parent, lazy);
这里以WXImage标签为例,最终转换为android的ImageView
public static class Ceator implements ComponentCreator { public WXComponent createInstance(WXSDKInstance instance, WXDomObject node, WXVContainer parent, boolean lazy) throws IllegalAccessException, InvocationTargetException, InstantiationException { return new WXImage(instance,node,parent,lazy); } }
至此,.we文件从jsBundle到虚拟Dom再到android端的原生实现就贯通了,代码分析部分省略了部分容错判断和跳过部分过渡的调用关系,请结合源码阅读.
感谢阅读完本篇分析,因能力有限,如有错误恳请斧正.
- weex源码浅析(Android部分)一
- Weex之Android端的浅析(一)
- Weex Android SDK源码分析
- BottomSheetBehavior源码部分浅析
- Weex环境构建(一)Weex+Android开发环境
- 代码干货 | Weex Android SDK源码分析
- Android应用ViewDragHelper详解及部分源码浅析
- DUBBO SPI部分源码浅析
- TProactor源码浅析一
- Chromium CC部分浅析(一)
- Android网络通信Volley框架源码浅析(一)
- Android广播机制实现源码浅析(一)
- Android广播机制实现源码浅析(一)
- [置顶] Android网络通信Volley框架源码浅析(一)
- Android单元测试框架源码分析(一)浅析Mockito
- Weex Android SDK源码分析之界面渲染(上)
- Weex Android SDK源码分析之Module(modal)
- Weex Android SDK源码分析之Module(navigator)
- linux 安装JDK
- 【android】Activity的启动模式
- Java中的Atomic包
- php中 “简单工厂模式”
- Android Webview 开发详解
- weex源码浅析(Android部分)一
- 基于python+ffmpeg的视频并发直播压力测试
- 【IAR】IAR的多行注释
- mysql数据库分表及实现
- DOM方法
- Ubuntu 16.04 CUDA函数库调用问题解
- python3.5下sklearn包的安装报错
- 阴影贴图(Shadow mapping)
- jQuery对象和DOM对象