美文网首页react-native&weex
Weex是如何在Android客户端上跑起来的

Weex是如何在Android客户端上跑起来的

作者: deep_sadness | 来源:发表于2018-02-22 19:55 被阅读469次
    u=633704361,1790938583&fm=11&gp=0.jpg

    参考:Weex 是如何在 iOS 客户端上跑起来的
    参考:Weex SDK Android 源码解析

    目录

    • Weex概述
    • Weex工作原理
    • Weex在Android上如何跑起来
    • 关于Weex,ReactNative

    Weex概述

    Weex非常轻量,体积小巧,语法简单,方便接入和上手。ReactNative官方只允许将ReactNative基础js库和业务JS一起打成一个JS bundle,没有提供分包的功能,所以如果想节约流量就必须制作分包打包工具。而Weex默认打的JS bundle只包含业务JS代码,体积小很多,基础JS库包含在Weex SDK中,这一点Weex与Facebook的React Native和微软的Cordova相比,Weex更加轻量,体积小巧。把Weex生成的JS bundle轻松部署到服务器端,然后Push到客户端,或者客户端请求新的资源即可完成发布。如此快速的迭代就解决了前言里面说的第一个痛点,发布无法控制时间,
    
    Weex中Native组件和API都可以横向扩展,业务方可去中心化横向灵活化定制组件和功能模块。并且还可以直接复用Web前端的工程化管理和监控性能等工具。
    知乎上有一个关于Weex 和 ReactNative很好的对比文章weex&ReactNative对比,推荐大家阅读。
    Weex在2017年2月17日正式发布v0.10.0,这个里程碑的版本开始完美的兼容Vue.js开发Weex界面。
    Weex又于2017年2月24 迁移至 Apache 基金会,阿里巴巴会基于 Apache 的基础设施继续迭代。并启用了全新的 GitHub 仓库:https://github.com/apache/incubator-weex
    故以下源码分析都基于v0.17.0这个版本。
    

    知乎上的对比

    Weex工作原理

    Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,template、style、script,借鉴了成熟的MVVM的思想。

    Weex的页面结构

    DOM模型

    Weex页面通过类似的HTML DOM的方式管理界面。首先,页面会被分解成一个DOM树。每个DOM节点都代表了一个相对独立的native视图单元。然后不同的视图单元通过树状结构组织在一起,构成完成的页面。

    Weex 在 JS 引擎中,为每个页面都提供了一套 Native DOM APIs,这套接口和 HTML DOM APIs 非常接近,利用这套接口我们可以通过 JavaScript 控制 native 的渲染逻辑。而且 Weex 上层的 Vue 2.0 也是基于这套接口进行适配的。

    绝大多数情况下 JS 框架会把 Native DOM APIs 都封装好,开发者不需要直接对 Native DOM 进行操作。

    • Document 类,整个页面文档。
    • Node 类,结点的基础类。
    • Element 类,元素结点,继承自 Node,单个视图单元。
    • Comment 类,注释结点,继承自 Node,无实际意义,通常用作占位符。

    每个 Weex 页面都有一个 weex.document 对象,该对象就是一个 Document 类的实例,也是下面所有接口调用的起点。

    Navitve DOM API

    组件

    Weex 的界面就是由这些组件以 DOM 树的方式构建出来的。这些组件,原生view(Weex中的Component)与weex标签的映射。自带的组件都是通过这样的方式创建出来的。

    <div></div> 对应 WXDiv

    布局系统

    Weex 页面中的组件会按照一定的布局规范来进行排布,我们这里提供了 CSS 中的盒模型flexbox绝对/相对/固定/吸附布局这三大块布局模型。

    • 盒模型:通过宽、高、边框、内外边距、边框等 CSS 属性描述一个组件本身的尺寸。
    • flexbox:通过 CSS 3 Flexbox 布局规范定义和描述组件之间的空间分布情况。
    • position:支持 CSS position 属性中的 absolute, relative, fixed, sticky 位置类型,其中 relative 是默认值。

    功能

    Weex 提供了非常丰富的系统功能 API,包括弹出存储、网络、导航、弹对话框和 toast 等,开发者可以在 Weex 页面通过获取一个 **native module **的方式引入并调用这些客户端功能 API。

    上文可以知道所有的功能,都是通过module来实现的。在Js中调用。

    WXStorageModule->Storage Api

    生命周期

    每个 Weex 页面都有其自身的生命周期,页面从开始被创建到最后被销毁,会经历到整个过程。这是通过对 Weex 页面的创建和销毁,在路由中通过 SDK 自行定义并实现的。

    Weex在Android中是如何跑起来的

    从.we或.vue文件到JS bundle这部分前端的代码。本文暂不涉及。

    可以先通过看看.we编译后的js文件,先看看结构。更加具体的后面陆续学习后补充。

    简单的.we编译后的js

    //第一行,表明了编译前的文件。vue的话,则为vue
    // { "framework": "Weex" }
    //开始 webpackBootstrap,应该是初始化脚手架的部分。
    /******/ (function(modules) { // webpackBootstrap
    /******/    // The module cache
    /******/    var installedModules = {};
    
    /******/    // The require function
    /******/    function __webpack_require__(moduleId) {
    
    /******/        // Check if module is in cache
    /******/        if(installedModules[moduleId])
    /******/            return installedModules[moduleId].exports;
    
    /******/        // Create a new module (and put it into the cache)
    /******/        var module = installedModules[moduleId] = {
    /******/            exports: {},
    /******/            id: moduleId,
    /******/            loaded: false
    /******/        };
    
    /******/        // Execute the module function
    /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    
    /******/        // Flag the module as loaded
    /******/        module.loaded = true;
    
    /******/        // Return the exports of the module
    /******/        return module.exports;
    /******/    }
    
    
    /******/    // expose the modules object (__webpack_modules__)
    /******/    __webpack_require__.m = modules;
    
    /******/    // expose the module cache
    /******/    __webpack_require__.c = installedModules;
    
    /******/    // __webpack_public_path__
    /******/    __webpack_require__.p = "";
    
    /******/    // Load entry module and return exports
    /******/    return __webpack_require__(0);
    /******/ })
    /************************************************************************/
    /******/ ([
    //从0这里开始,就是我们写的代码了。在0这里,获取通用的style和template
    /* 0 */
    /***/ (function(module, exports, __webpack_require__) {
    
        var __weex_template__ = __webpack_require__(1)
        var __weex_style__ = __webpack_require__(2)
    
        __weex_define__('@weex-component/22de37c9919eb3fd01a7758b3c5f2baf', [], function(__weex_require__, __weex_exports__, __weex_module__) {
    
            __weex_module__.exports.template = __weex_template__
    
            __weex_module__.exports.style = __weex_style__
    
        })
    
        __weex_bootstrap__('@weex-component/22de37c9919eb3fd01a7758b3c5f2baf',undefined,undefined)
    
    /***/ }),
      /*从1开始,就是我们自己定义的对象了。看起来像是dom 模型的json文件
      type为div的话,它对应的classlist 为container。children:标记它的子节点。
      我们看到,一个节点,对应的属性可能有 type,classlist,attr,event
      */
    /* 1 */
    /***/ (function(module, exports) {
    
        module.exports = {
          "type": "div",
          "classList": [
            "container"
          ],
          "children": [
            {
              "type": "div",
              "classList": [
                "cell"
              ],
              "children": [
                {
                  "type": "image",
                  "classList": [
                    "thumb"
                  ],
                  "attr": {
                    "src": ""
                  }
                },
                {
                  "type": "text",
                  "classList": [
                    "title"
                  ],
                  "attr": {
                    "value": "Hello Weex"
                  }
                }
              ]
            }
          ]
        }
    
    /***/ }),
    /* 2 */
    /***/ (function(module, exports) {
    
        module.exports = {
          "cell": {
            "marginTop": 10,
            "marginLeft": 10
          },
          "thumb": {
            "width": 200,
            "height": 200
          },
          "title": {
            "textAlign": "center",
            "flex": 1,
            "color": "#808080"
          }
        }
    
    /***/ })
    /******/ ]);
    
    • 从1开始,就是我们自己定义的对象了。看起来像是dom 模型的json文件

      对于div这样的容器型组件,它可能有children属性。

      我们看到,一个节点,对应的属性可能有 type,classlist,attr,event

    主要还是围绕Weex SDK的源码来进行了解。

    Weex SDK初始化

    先来看看playground App Android中是如何初始化的吧。

    初始化是在Application中。

    文件位置:incubator-weex-master/android/playground/app/src/main/java/com/alibaba/weex/WxApplication.java

    public class WXApplication extends Application {
    
      @Override
      public void onCreate() {
        super.onCreate();
    
        /**
         * Set up for fresco usage.
         * Set<RequestListener> requestListeners = new HashSet<>();
         * requestListeners.add(new RequestLoggingListener());
         * ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this)
         *     .setRequestListeners(requestListeners)
         *     .build();
         * Fresco.initialize(this,config);
         **/
    //    initDebugEnvironment(true, false, "DEBUG_SERVER_HOST");
        WXSDKEngine.addCustomOptions("appName", "WXSample");
        WXSDKEngine.addCustomOptions("appGroup", "WXApp");
        WXSDKEngine.initialize(this,
                               new InitConfig.Builder()
                                   //.setImgAdapter(new FrescoImageAdapter())// use fresco adapter
                                   .setImgAdapter(new ImageAdapter())
                                   .setWebSocketAdapterFactory(new DefaultWebSocketAdapterFactory())
                                   .setJSExceptionAdapter(new JSExceptionAdapter())
                                   .setHttpAdapter(new InterceptWXHttpAdapter())
                                   .build()
                              );
    
        WXSDKManager.getInstance().setAccessibilityRoleAdapter(new DefaultAccessibilityRoleAdapter());
    
        try {
          Fresco.initialize(this);
          WXSDKEngine.registerComponent("synccomponent", WXComponentSyncTest.class);
          WXSDKEngine.registerComponent(WXParallax.PARALLAX, WXParallax.class);
    
          WXSDKEngine.registerComponent("richtext", RichText.class);
          WXSDKEngine.registerModule("render", RenderModule.class);
          WXSDKEngine.registerModule("event", WXEventModule.class);
          WXSDKEngine.registerModule("syncTest", SyncTestModule.class);
    
          WXSDKEngine.registerComponent("mask",WXMask.class);
          WXSDKEngine.registerDomObject("mask", WXMaskDomObject.class);
    
          WXSDKEngine.registerModule("myModule", MyModule.class);
          WXSDKEngine.registerModule("geolocation", GeolocationModule.class);
          /**
           * override default image tag
           * WXSDKEngine.registerComponent("image", FrescoImageComponent.class);
           */
    
    
        } catch (WXException e) {
          e.printStackTrace();
        }
    
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
          @Override
          public void onActivityCreated(Activity activity, Bundle bundle) {
    
          }
    
          @Override
          public void onActivityStarted(Activity activity) {
    
          }
    
          @Override
          public void onActivityResumed(Activity activity) {
    
          }
    
          @Override
          public void onActivityPaused(Activity activity) {
    
          }
    
          @Override
          public void onActivityStopped(Activity activity) {
    
          }
    
          @Override
          public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    
          }
    
          @Override
          public void onActivityDestroyed(Activity activity) {
            // The demo code of calling 'notifyTrimMemory()'
            if (false) {
              // We assume that the application is on an idle time.
              WXSDKManager.getInstance().notifyTrimMemory();
            }
            // The demo code of calling 'notifySerializeCodeCache()'
            if (false) {
              WXSDKManager.getInstance().notifySerializeCodeCache();
            }
          }
        });
    
      }
      ...
    }
    

    初始化的代码很长,主要可以分成下面几个部分来看:

    1. WXSDKEngine.addCustomOptions("appName", "WXSample")等方法。这些个方法可以注册一些APP中的常量到js内方便调用。JS可以通过weex.config.env.appName这样的方式来调用。
    2. 通过建造者模式来创造InitConfig来初始化WxJsFramework
    3. 通过WXSDKEngine.registerComponent("richtext", RichText.class);WXSDKEngine.registerModule("render", RenderModule.class);registerDomObject()等方法来注册自定义的Component和Module。Component可以看出是Android中native控件和Wx的绑定。而Module则可以看出是非UI功能的组件和Wx的绑定。具体的这两者,放到后面再细谈。
    4. 通过registerActivityLifecycleCallbacks方法来注册得到生命周期的回调

    1. 初始化配置

    其实初始化的这些配置是添加到WXEnvironment类中进行管理。会在初始化JsFramework的过程中,传递给到其中。通过weex.config.env.appName这样的方式来获取。

    public class WXEnvironment {
    
      //android os的信息
    //...
      /*********************
       * Global config
       ***************************/
        
      //js lib的版本
      public static String JS_LIB_SDK_VERSION = BuildConfig.buildJavascriptFrameworkVersion;
        
      public static String WXSDK_VERSION = BuildConfig.buildVersion;
      //保存全局的Application,会在WXSDKEngine中进行初始化
      public static Application sApplication;
      //简单的获得设备的devId 通过TELEPHONY_SERVICE
      public static final String DEV_Id = getDevId();
      //虽然标注了过时的,但是默认的宽度还是750
      @Deprecated
      public static int sDefaultWidth = 750;
      //是否标记初始化完成
      public volatile static boolean JsFrameworkInit = false;
    
      public static final String SETTING_EXCLUDE_X86SUPPORT = "env_exclude_x86";
    
      public static boolean SETTING_FORCE_VERTICAL_SCREEN = false;
      /**
       * Debug model
       */
      public static boolean sDebugMode = false;
      public static boolean sForceEnableDevTool = false;
      public static String sDebugWsUrl = "";
      public static boolean sDebugServerConnectable = false;
      public static boolean sRemoteDebugMode = false;
      public static String sRemoteDebugProxyUrl = "";
      public static boolean sDebugNetworkEventReporterEnable = false;//debugtool network switch
      public static long sJSLibInitTime = 0;
    
      public static long sSDKInitStart = 0;// init start timestamp
      public static long sSDKInitInvokeTime = 0;//time cost to invoke init method
      public static long sSDKInitExecuteTime = 0;//time cost to execute init job
      /** from init to sdk-ready **/
      public static long sSDKInitTime =0;
    
      public static LogLevel sLogLevel = LogLevel.DEBUG;
      private static boolean isApkDebug = true;
      public static boolean isPerf = false;
    
      private static boolean openDebugLog = false;
    
      private static String sGlobalFontFamily;
    
      //自定义的一些属性是添加到这个map当中
      private static Map<String, String> options = new HashMap<>();
      static {
        options.put(WXConfig.os, OS);
        options.put(WXConfig.osName, OS);
      }
    
      /**
       * dynamic
       */
      public static boolean sDynamicMode = false;
      public static String sDynamicUrl = "";
    
      /**
       * Fetch system information.
       * @return map contains system information.
       */
      public static Map<String, String> getConfig() {
        Map<String, String> configs = new HashMap<>();
        configs.put(WXConfig.os, OS);
        configs.put(WXConfig.appVersion, getAppVersionName());
        configs.put(WXConfig.cacheDir, getAppCacheFile());
        configs.put(WXConfig.devId, DEV_Id);
        configs.put(WXConfig.sysVersion, SYS_VERSION);
        configs.put(WXConfig.sysModel, SYS_MODEL);
        configs.put(WXConfig.weexVersion, String.valueOf(WXSDK_VERSION));
        configs.put(WXConfig.logLevel,sLogLevel.getName());
        try {
          options.put(WXConfig.scale, Float.toString(sApplication.getResources().getDisplayMetrics().density));
        }catch (NullPointerException e){
          //There is little chance of NullPointerException as sApplication may be null.
          WXLogUtils.e("WXEnvironment scale Exception: ", e);
        }
        configs.putAll(options);
        if(configs!=null&&configs.get(WXConfig.appName)==null && sApplication!=null){
           configs.put(WXConfig.appName, sApplication.getPackageName());
        }
        return configs;
      }
    
    
      public static Map<String, String> getCustomOptions() {
        return options;
      }
    
      public static void addCustomOptions(String key, String value) {
        options.put(key, value);
      }
        //...
    }
    

    2. 初始化过程

    WXSDKEngine.initialize(this, config)方法。

     public static void initialize(Application application,InitConfig config){
       //这里是先同步这个方法,方法未执行完,不会走其他被mLock锁住的方法。
       //可以看到mLock其实只是锁了一个查询初始化状态的方法
       synchronized (mLock) {
          if (mIsInit) {
            return;
          }
          long start = System.currentTimeMillis();
          WXEnvironment.sSDKInitStart = start;
         //是否可调试。可以的话。在控制台输出log
          if(WXEnvironment.isApkDebugable()){
            WXEnvironment.sLogLevel = LogLevel.DEBUG;
          }else{
            if(WXEnvironment.sApplication != null){
              WXEnvironment.sLogLevel = LogLevel.WARN;
            }else {
              WXLogUtils.e(TAG,"WXEnvironment.sApplication is " + WXEnvironment.sApplication);
            }
          }
         //进行初始化
          doInitInternal(application,config);
         //log 初始化耗时
          WXEnvironment.sSDKInitInvokeTime = System.currentTimeMillis()-start;
          WXLogUtils.renderPerformanceLog("SDKInitInvokeTime", WXEnvironment.sSDKInitInvokeTime);
          mIsInit = true;
        }
      }
    

    进一步看,doInitInternal做了什么

    private static void doInitInternal(final Application application,final InitConfig config){
      //校验Application,通过这个全局变量将application context保存到WXEnvironment中。如上面的分析所述
        WXEnvironment.sApplication = application;
        if(application == null){
          WXLogUtils.e(TAG, " doInitInternal application is null");
        }
        WXEnvironment.JsFrameworkInit = false;
      //我们这里先知道,WxBridgeManager就如他的名字一样,是js和native进行通信的一个管理者。扶着协调两者之间的通行的作用。
      //WxBridgeManager运行在一个HandlerThread(JsThread&JsHandler)中。这里就进行了异步的初始化。
        WXBridgeManager.getInstance().post(new Runnable() {
          @Override
          public void run() {
            long start = System.currentTimeMillis();
            //这里SDK manager是一个管理weex context的对象
            WXSDKManager sm = WXSDKManager.getInstance();
            //调用一个全局的回调
            sm.onSDKEngineInitialize();
            if(config != null ) {
              //将配置的参数,保存在SDK manager中
              sm.setInitConfig(config);
            }
            //初始化Android的JS引擎.
            WXSoInstallMgrSdk.init(application,
                                  sm.getIWXSoLoaderAdapter(),
                                  sm.getWXStatisticsListener());
            //进入这个方法,能够看到对so文件的初始化做了版本的适配。而且做了失败的重试
            boolean isSoInitSuccess = WXSoInstallMgrSdk.initSo(V8_SO_NAME, 1, config!=null?config.getUtAdapter():null);
            if (!isSoInitSuccess) {
              return;
            }
            //初始化JSFrameWork.其实就是像JSThread发送一个 INIT_FRAMEWORK的消息。然后开始初始化。初始化的过程就在JSThread中
            sm.initScriptsFramework(config!=null?config.getFramework():null);
            //最后统计时间
            WXEnvironment.sSDKInitExecuteTime = System.currentTimeMillis() - start;
            WXLogUtils.renderPerformanceLog("SDKInitExecuteTime", WXEnvironment.sSDKInitExecuteTime);
          }
        });
      //这里注册公共的组件部分
        register();
      }
    

    doInitInternal()方法中,可以看到,初始化,其实就是干了两件事情。

    • 初始化JSFramework
    • 注册对应的native组件到JSFramework当中。
    初始化JSFramework

    上文调用initScriptsFramework方法,其实就是通过JsThreadhandler发送WXJSBridgeMsgType.INIT_FRAMEWORKmessage
    之后会转到WxBridgeManager中的initFramework方法。

    private void initFramework(String framework) {
        //这第一次一定是去加载main.js文件了。main.js文件到底是何方神圣?
        if (!isJSFrameworkInit()) {
          if (TextUtils.isEmpty(framework)) {
            // if (WXEnvironment.isApkDebugable()) {
            WXLogUtils.d("weex JS framework from assets");
            // }
            framework = WXFileUtils.loadAsset("main.js", WXEnvironment.getApplication());
          }
          //如果为空,则直接设置失败
          if (TextUtils.isEmpty(framework)) {
            setJSFrameworkInit(false);
            commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK, "JS Framework is empty!");
            return;
          }
          try {
            if (WXSDKManager.getInstance().getWXStatisticsListener() != null) {
              WXSDKManager.getInstance().getWXStatisticsListener().onJsFrameworkStart();
            }
            //下面这段是去获取crash文件
            long start = System.currentTimeMillis();
            String crashFile = "";
            try {
              crashFile = WXEnvironment.getApplication().getApplicationContext().getCacheDir().getPath();
            } catch (Exception e) {
              e.printStackTrace();
            }
            boolean pieSupport = true;
            try {
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                pieSupport = false;
              }
            } catch (Exception e) {
              e.printStackTrace();
            }
            WXLogUtils.d("[WXBridgeManager] initFrameworkEnv crashFile:" + crashFile + " pieSupport:" + pieSupport);
            //看注释,知道开始扩展frameworkenv
            // extends initFramework.这里这个是进入native方法中
            //最新版本使用了多进程的方式来进行初始化。同样也是在native方法中完成。这儿就暂且不看了
            //这里的WxBridge就是js和native通信通道。
            //这里的assembleDefaultOptions会将WXEnvironment中的配置注入到js当中
            if (mWXBridge.initFrameworkEnv(framework, assembleDefaultOptions(), crashFile, pieSupport) == INIT_FRAMEWORK_OK) {
              WXEnvironment.sJSLibInitTime = System.currentTimeMillis() - start;
              WXLogUtils.renderPerformanceLog("initFramework", WXEnvironment.sJSLibInitTime);
              WXEnvironment.sSDKInitTime = System.currentTimeMillis() - WXEnvironment.sSDKInitStart;
              WXLogUtils.renderPerformanceLog("SDKInitTime", WXEnvironment.sSDKInitTime);
              //这里还没报错,那就算是初始化完成了。
              setJSFrameworkInit(true);
    
              if (WXSDKManager.getInstance().getWXStatisticsListener() != null) {
                WXSDKManager.getInstance().getWXStatisticsListener().onJsFrameworkReady();
              }
            //这里先将失败的任务重新添加回来
              execRegisterFailTask();
              WXEnvironment.JsFrameworkInit = true;
              //重新注册。最后通过jsThread中JsBridge通过execJS系列native方法重新注册
              registerDomModule();
              String reinitInfo = "";
              if (reInitCount > 1) {
                reinitInfo = "reinit Framework:";
              }
              commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_SUCCESS, reinitInfo + "success");
            } else {
              if (reInitCount > 1) {
                WXLogUtils.e("[WXBridgeManager] invokeReInitFramework  ExecuteJavaScript fail");
                String err = "[WXBridgeManager] invokeReInitFramework  ExecuteJavaScript fail reinit FrameWork";
                commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_REINIT_FRAMEWORK, err);
              } else {
                WXLogUtils.e("[WXBridgeManager] invokeInitFramework  ExecuteJavaScript fail");
                String err = "[WXBridgeManager] invokeInitFramework  ExecuteJavaScript fail";
                commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK, err);
              }
            }
          } catch (Throwable e) {
            if (reInitCount > 1) {
              WXLogUtils.e("[WXBridgeManager] invokeInitFramework ", e);
              String err = "[WXBridgeManager] invokeInitFramework reinit FrameWork exception!#" + e.toString();
              commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_REINIT_FRAMEWORK, err);
            } else {
              WXLogUtils.e("[WXBridgeManager] invokeInitFramework ", e);
              String err = "[WXBridgeManager] invokeInitFramework exception!#" + e.toString();
              commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK, err);
            }
          }
    
        }
      }
    
    加载本地的main.js

    第一次下载完源码。搜索全局。会发现发现找不到main.js这样的文件。其实他是SDK自带的。它是由gradle从中,加载 incubator-weex\pre-build\native-bundle-main.js文件生成的。直接打开这个文件会看到一堆经过webpack压缩之后的文件

    下面这个源文件的路径。已经在github上找不到了。

    这个文件的源文件在https://github.com/apache/incubator-weex/tree/master/html5目录下。对应的入口文件是 html5/render/native/index.js

    简单的看一下gradle文件的配置

    incubator-weex\android\sdk\build.gradle

    android{  
      ...    
       //将文件从prebuild目录中复制到assets目录下,并重命名    
       copy {        
         from '../../pre-build'        
         into new File(projectDir,"assets")       
         include 'native-bundle-main.js'        
         rename('native-bundle-main.js','main.js')    
       }    
        //从这个文件中,得到 buildJavascriptFrameworkVersion 的版本号    
        def line    
        new File(projectDir,"assets/main.js").withReader { line = it.readLine() }    
        def m = line =~ /[A-Z\s]+\s+([0-9\.]+),\s+Build\s+[0-9]+/;    
        def jsfmVersion = m[0][1]    
        println jsfmVersion  
        ...
    }
    
    assembleDefaultOptions()方法
    private WXParams assembleDefaultOptions() {
      //通过Environment的getConfig方法,就对应的配置输入到config的map中
        Map<String, String> config = WXEnvironment.getConfig();
      //再通过WXParams这个对象,传递给JsFrameworks当中
        WXParams wxParams = new WXParams();
        wxParams.setPlatform(config.get(WXConfig.os));
        wxParams.setCacheDir(config.get(WXConfig.cacheDir));
        wxParams.setOsVersion(config.get(WXConfig.sysVersion));
        wxParams.setAppVersion(config.get(WXConfig.appVersion));
        wxParams.setWeexVersion(config.get(WXConfig.weexVersion));
        wxParams.setDeviceModel(config.get(WXConfig.sysModel));
        wxParams.setShouldInfoCollect(config.get("infoCollect"));
        wxParams.setLogLevel(config.get(WXConfig.logLevel));
        String appName = config.get(WXConfig.appName);
        if (!TextUtils.isEmpty(appName)) {
          wxParams.setAppName(appName);
        }
        //这里指的注意的是设置了deivceWidth。这里需要注意的是,如果没有手动设置这个值,其实是会同个这个utils中去获取。而这个WXViewUtils其实得到整个屏幕宽度
        wxParams.setDeviceWidth(TextUtils.isEmpty(config.get("deviceWidth")) ? String.valueOf(WXViewUtils.getScreenWidth(WXEnvironment.sApplication)) : config.get("deviceWidth"));
        wxParams.setDeviceHeight(TextUtils.isEmpty(config.get("deviceHeight")) ? String.valueOf(WXViewUtils.getScreenHeight(WXEnvironment.sApplication)) : config.get("deviceHeight"));
       //这里讲所有自定额的Options设置
        wxParams.setOptions(WXEnvironment.getCustomOptions());
        //是否需要初始化v8
        wxParams.setNeedInitV8(WXSDKManager.getInstance().needInitV8());
        mInitParams = wxParams;
        return wxParams;
      }
    

    WXViewUtils

    public static int getScreenWidth(Context ctx) {
        if(ctx!=null){
          Resources res = ctx.getResources();
          //获取屏幕的宽度像素
          mScreenWidth = res.getDisplayMetrics().widthPixels;
          if(WXEnvironment.SETTING_FORCE_VERTICAL_SCREEN){
            mScreenHeight = res
                    .getDisplayMetrics()
                    .heightPixels;
            mScreenWidth = mScreenHeight > mScreenWidth ? mScreenWidth : mScreenHeight;
          }
        } else if(WXEnvironment.isApkDebugable()){
          throw new WXRuntimeException("Error Context is null When getScreenHeight");
        }
        return mScreenWidth;
      }
    
    注册对应的native组件到JSFramework当中

    回到WXSDKEngine中的doInitInternal()方法的最后的register()方法。由这个方法进行注册相应的组件。

    private static void register() {
        //这个batchOperationHelper是一个注册组件的intercepter
        //创建这个类。之后registerXX就会添加到注册队列中。最后再通过flush()将其一次性执行
        BatchOperationHelper batchHelper = new BatchOperationHelper(WXBridgeManager.getInstance());
        //下面开始批量注册SDK自带的组件
        try {
          registerComponent(
            new SimpleComponentHolder(
              WXText.class,
              new WXText.Creator()
            ),
            false,
            WXBasicComponentType.TEXT
          );
          registerComponent(
            new SimpleComponentHolder(
              WXDiv.class,
              new WXDiv.Ceator()
            ),
            false,
            WXBasicComponentType.CONTAINER,
            WXBasicComponentType.DIV,
            WXBasicComponentType.HEADER,
            WXBasicComponentType.FOOTER
          );
          registerComponent(
            new SimpleComponentHolder(
              WXImage.class,
              new WXImage.Ceator()
            ),
            false,
            WXBasicComponentType.IMAGE,
            WXBasicComponentType.IMG
          );
          registerComponent(
            new SimpleComponentHolder(
              WXScroller.class,
              new WXScroller.Creator()
            ),
            false,
            WXBasicComponentType.SCROLLER
          );
          registerComponent(
            new SimpleComponentHolder(
              WXSlider.class,
              new WXSlider.Creator()
            ),
            true,
            WXBasicComponentType.SLIDER,
            WXBasicComponentType.CYCLE_SLIDER
          );
          registerComponent(
            new SimpleComponentHolder(
                    WXSliderNeighbor.class,
              new WXSliderNeighbor.Creator()
            ),
            true,
            WXBasicComponentType.SLIDER_NEIGHBOR
          );
          String simpleList = "simplelist";
          registerComponent(SimpleListComponent.class,false,simpleList);
          registerComponent(WXListComponent.class, false,WXBasicComponentType.LIST,WXBasicComponentType.VLIST,WXBasicComponentType.RECYCLER,WXBasicComponentType.WATERFALL);
          registerComponent(WXRecyclerTemplateList.class, false,WXBasicComponentType.RECYCLE_LIST);
          registerComponent(HorizontalListComponent.class,false,WXBasicComponentType.HLIST);
          registerComponent(WXBasicComponentType.CELL, WXCell.class, true);
          registerComponent(WXBasicComponentType.CELL_SLOT, WXCell.class, true);
          registerComponent(WXBasicComponentType.INDICATOR, WXIndicator.class, true);
          registerComponent(WXBasicComponentType.VIDEO, WXVideo.class, false);
          registerComponent(WXBasicComponentType.INPUT, WXInput.class, false);
          registerComponent(WXBasicComponentType.TEXTAREA, Textarea.class,false);
          registerComponent(WXBasicComponentType.SWITCH, WXSwitch.class, false);
          registerComponent(WXBasicComponentType.A, WXA.class, false);
          registerComponent(WXBasicComponentType.EMBED, WXEmbed.class, true);
          registerComponent(WXBasicComponentType.WEB, WXWeb.class);
          registerComponent(WXBasicComponentType.REFRESH, WXRefresh.class);
          registerComponent(WXBasicComponentType.LOADING, WXLoading.class);
          registerComponent(WXBasicComponentType.LOADING_INDICATOR, WXLoadingIndicator.class);
          registerComponent(WXBasicComponentType.HEADER, WXHeader.class);
    
          registerModule("modal", WXModalUIModule.class, false);
          registerModule("instanceWrap", WXInstanceWrap.class, true);
          registerModule("animation", WXAnimationModule.class, true);
          registerModule("webview", WXWebViewModule.class, true);
          registerModule("navigator", WXNavigatorModule.class);
          registerModule("stream", WXStreamModule.class);
          registerModule("timer", WXTimerModule.class, false);
          registerModule("storage", WXStorageModule.class, true);
          registerModule("clipboard", WXClipboardModule.class, true);
          registerModule("globalEvent",WXGlobalEventModule.class);
          registerModule("picker", WXPickersModule.class);
          registerModule("meta", WXMetaModule.class,true);
          registerModule("webSocket", WebSocketModule.class);
          registerModule("locale", WXLocaleModule.class);
    
    
          registerDomObject(simpleList, WXListDomObject.class);
          registerDomObject(WXBasicComponentType.INDICATOR, WXIndicator.IndicatorDomNode.class);
          registerDomObject(WXBasicComponentType.TEXT, WXTextDomObject.class);
          registerDomObject(WXBasicComponentType.HEADER, WXCellDomObject.class);
          registerDomObject(WXBasicComponentType.CELL, WXCellDomObject.class);
          registerDomObject(WXBasicComponentType.CELL_SLOT, WXCellDomObject.class);
          registerDomObject(WXBasicComponentType.INPUT, BasicEditTextDomObject.class);
          registerDomObject(WXBasicComponentType.TEXTAREA, TextAreaEditTextDomObject.class);
          registerDomObject(WXBasicComponentType.SWITCH, WXSwitchDomObject.class);
          registerDomObject(WXBasicComponentType.LIST, WXListDomObject.class);
          registerDomObject(WXBasicComponentType.RECYCLE_LIST, WXRecyclerDomObject.class);
          registerDomObject(WXBasicComponentType.VLIST, WXListDomObject.class);
          registerDomObject(WXBasicComponentType.HLIST, WXListDomObject.class);
          registerDomObject(WXBasicComponentType.SCROLLER, WXScrollerDomObject.class);
          registerDomObject(WXBasicComponentType.RECYCLER, WXRecyclerDomObject.class);
          registerDomObject(WXBasicComponentType.WATERFALL, WXRecyclerDomObject.class);
        } catch (WXException e) {
          WXLogUtils.e("[WXSDKEngine] register:", e);
        }
        batchHelper.flush();
      }
    

    在WXSDKEngine初始化的时候就分别注册了这三样东西,Components,Modules,DomObjects。

    Components的注册过程
         
    registerComponent(
            new SimpleComponentHolder(
              WXText.class,
              new WXText.Creator()
            ),
            false,
            WXBasicComponentType.TEXT
          );
          registerComponent(
            new SimpleComponentHolder(
              WXDiv.class,
              new WXDiv.Ceator()
            ),
            false,
            WXBasicComponentType.CONTAINER,
            WXBasicComponentType.DIV,
            WXBasicComponentType.HEADER,
            WXBasicComponentType.FOOTER
          );
          registerComponent(
            new SimpleComponentHolder(
              WXImage.class,
              new WXImage.Ceator()
            ),
            false,
            WXBasicComponentType.IMAGE,
            WXBasicComponentType.IMG
          );
          registerComponent(
            new SimpleComponentHolder(
              WXScroller.class,
              new WXScroller.Creator()
            ),
            false,
            WXBasicComponentType.SCROLLER
          );
          registerComponent(
            new SimpleComponentHolder(
              WXSlider.class,
              new WXSlider.Creator()
            ),
            true,
            WXBasicComponentType.SLIDER,
            WXBasicComponentType.CYCLE_SLIDER
          );
          registerComponent(
            new SimpleComponentHolder(
                    WXSliderNeighbor.class,
              new WXSliderNeighbor.Creator()
            ),
            true,
            WXBasicComponentType.SLIDER_NEIGHBOR
          );
          String simpleList = "simplelist";
          registerComponent(SimpleListComponent.class,false,simpleList);
          registerComponent(WXListComponent.class, false,WXBasicComponentType.LIST,WXBasicComponentType.VLIST,WXBasicComponentType.RECYCLER,WXBasicComponentType.WATERFALL);
          registerComponent(WXRecyclerTemplateList.class, false,WXBasicComponentType.RECYCLE_LIST);
          registerComponent(HorizontalListComponent.class,false,WXBasicComponentType.HLIST);
          registerComponent(WXBasicComponentType.CELL, WXCell.class, true);
          registerComponent(WXBasicComponentType.CELL_SLOT, WXCell.class, true);
          registerComponent(WXBasicComponentType.INDICATOR, WXIndicator.class, true);
          registerComponent(WXBasicComponentType.VIDEO, WXVideo.class, false);
          registerComponent(WXBasicComponentType.INPUT, WXInput.class, false);
          registerComponent(WXBasicComponentType.TEXTAREA, Textarea.class,false);
          registerComponent(WXBasicComponentType.SWITCH, WXSwitch.class, false);
          registerComponent(WXBasicComponentType.A, WXA.class, false);
          registerComponent(WXBasicComponentType.EMBED, WXEmbed.class, true);
          registerComponent(WXBasicComponentType.WEB, WXWeb.class);
          registerComponent(WXBasicComponentType.REFRESH, WXRefresh.class);
          registerComponent(WXBasicComponentType.LOADING, WXLoading.class);
          registerComponent(WXBasicComponentType.LOADING_INDICATOR, WXLoadingIndicator.class);
          registerComponent(WXBasicComponentType.HEADER, WXHeader.class);
    

    这里提供了27种默认的基础组件。

    这里有两种签名registerComponent()进行注册,最后会调用的还是WXComponentRegistry这个类的方法。(这里我们先知道WXComponentRegistry是管理所有注册组件的类。)

      /**
       * 从这方法的签名可以看到。通过这个类来注册Component组件
       * @param type String类型的字符串。定义这个组件调用的名称
       * @param holder IFComponentHolder的缓存类,作用如它的名字,就是Holder。判断是否赖加载。和存储对应的键值对。一般通过默认SimpleComponentHolder来进行设置
       * @param componentInfo 存储component的字典。hashMap
       * @return
       * @throws WXException
       */
    public static boolean registerComponent(final String type, final IFComponentHolder holder, final Map<String, Object> componentInfo) throws WXException {
        if (holder == null || TextUtils.isEmpty(type)) {
          return false;
        }
    
      //在jsThread中执行,确保注册的和调用的是在同一个线程?
        //execute task in js thread to make sure register order is same as the order invoke register method.
        WXBridgeManager.getInstance()
            .post(new Runnable() {
          @Override
          public void run() {
            try {
              Map<String, Object> registerInfo = componentInfo;
              if (registerInfo == null){
                registerInfo = new HashMap<>();
              }
            //这个ComponentInfo的map中存储了type,methods两个变量。
              //这里需要记住的是,还存放这之前的 append 属性。
              registerInfo.put("type",type);
              registerInfo.put("methods",holder.getMethods());
              //分别向native和js进行注册
              registerNativeComponent(type, holder);
              registerJSComponent(registerInfo);
              //注册成功后,进行缓存,加入ComponentInfos的map中。
              sComponentInfos.add(registerInfo);
            } catch (WXException e) {
              WXLogUtils.e("register component error:", e);
            }
    
          }
        });
        return true;
      }
    

    可以看到,SDK通过WXComponentRegistry来分别向native和js进行注册,成功后并对其进行缓存。提供效率。

    • IFComponentHolder

      还是先看一下IFComponentHolder这个缓存类。再看相应的注册方法。这个类的默认实现是SimpleComponentHolder

      public class SimpleComponentHolder implements IFComponentHolder{
        public static final String TAG = "SimpleComponentHolder";
        private final Class<? extends WXComponent> mClz;
        //缓存有方法和属性的调用map.Invoker是一个方法的接口。它包括三个方法,分别为调用方法,获取方法参数的类型和确定是否在uiThread中调用方法
        private Map<String, Invoker> mPropertyInvokers;
        private Map<String, Invoker> mMethodInvokers;
        //这个Creator来创建Component对象
        private ComponentCreator mCreator;
        
        //使用默认实现的classComponentCreator
        public SimpleComponentHolder(Class<? extends WXComponent> clz) {
          this(clz,new ClazzComponentCreator(clz));
        }
      
        public SimpleComponentHolder(Class<? extends WXComponent> clz,ComponentCreator customCreator) {
          this.mClz = clz;
          this.mCreator = customCreator;
        }
      
        //通过Component注解来判断是否是懒加载。如果不是,则注册时调用generate直接生成
        @Override
        public void loadIfNonLazy() {
          Annotation[] annotations = mClz.getDeclaredAnnotations();
          for (Annotation annotation :
            annotations) {
            //懒加载是通过Component这个注解
            if (annotation instanceof Component){
              if(!((Component) annotation).lazyload() && mMethodInvokers == null){
                generate();
              }
              return;
            }
          }
        }
      
        private synchronized void generate(){
          if(WXEnvironment.isApkDebugable()) {
            WXLogUtils.d(TAG, "Generate Component:" + mClz.getSimpleName());
          }
      
          Pair<Map<String, Invoker>, Map<String, Invoker>> methodPair = getMethods(mClz);
          mPropertyInvokers = methodPair.first;
          mMethodInvokers = methodPair.second;
        }
      
        //通过解析类中的注解和方法,返回对应的属性和方法的invoker map
        static Pair<Map<String,Invoker>,Map<String,Invoker>> getMethods(Class clz){
          //methods是存放属性调用的map.invokers是存放方法调用的map
          Map<String, Invoker> methods = new HashMap<>();
          Map<String, Invoker> mInvokers = new HashMap<>();
      
          Annotation[] annotations;
          Annotation anno;
          try {
            //获取所有的方法
            for (Method method : clz.getMethods()) {
              try {
                //获取方法的注解
                annotations = method.getDeclaredAnnotations();
                for (int i = 0, annotationsCount = annotations.length;
                     i < annotationsCount; ++i) {
                  anno = annotations[i];
                  if(anno == null){
                    continue;
                  }
                  //如果是被 WXComponentProp 注解,则表示这个是属性,则创建调用的MethodInvoker加入map中。
                  if (anno instanceof WXComponentProp) {
                    String name = ((WXComponentProp) anno).name();
                    methods.put(name, new MethodInvoker(method,true));
                    break;
                  }else if(anno instanceof JSMethod){
                    //如果是JsMethod注解,则表示这个是一个方法。获取是否别名。同样构造方法放入方法的map中
                    JSMethod methodAnno = (JSMethod)anno;
                    String name = methodAnno.alias();
                    if(JSMethod.NOT_SET.equals(name)){
                      name = method.getName();
                    }
                    mInvokers.put(name, new MethodInvoker(method,methodAnno.uiThread()));
                    break;
                  }
                }
              } catch (ArrayIndexOutOfBoundsException | IncompatibleClassChangeError e) {
                //ignore: getDeclaredAnnotations may throw this
              }
            }
          }catch (IndexOutOfBoundsException e){
            e.printStackTrace();
            //ignore: getMethods may throw this
          }
          return new Pair<>(methods,mInvokers);
        }
        /*
      构造WXComponent实例,并且绑定当前的holder
       */
      @Override
      public synchronized WXComponent createInstance(WXSDKInstance instance, WXDomObject node, WXVContainer parent) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        WXComponent component = mCreator.createInstance(instance,node,parent);
      
        component.bindHolder(this);
        return component;
      }
      /* 
        获取property invoker,如果是懒加载的话,则开始生成
      */
      @Override
      public synchronized Invoker getPropertyInvoker(String name){
          if (mPropertyInvokers == null) {
            generate();
          }
      
        return mPropertyInvokers.get(name);
      } 
      /*  
         获取method invoker,如果是懒加载的话,则开始生成
      */
      @Override
      public Invoker getMethodInvoker(String name) {
        if(mMethodInvokers == null){
          generate();
        }
        return mMethodInvokers.get(name);
      }
      
      @Override
      public String[] getMethods() {
        if(mMethodInvokers == null){
          generate();
        }
        Set<String> keys = mMethodInvokers.keySet();
        return keys.toArray(new String[keys.size()]);
      }
      }  
      

      其中包括 了一个默认实现的Creator

       /*
          默认实现由一个ClaszzComponentCreator。这个Creator是用来给遵循sdk自带的构造方法的Component提供的默认实现。其中兼容了3-5的构造方法
           */
          static class ClazzComponentCreator implements ComponentCreator{
      
            private Constructor<? extends WXComponent> mConstructor;
            private final Class<? extends WXComponent> mCompClz;
      
            ClazzComponentCreator(Class<? extends WXComponent> c){
              mCompClz = c;
            }
      
            private void loadConstructor(){
              Class<? extends WXComponent> c = mCompClz;
              Constructor<? extends WXComponent> constructor;
              try {
                constructor = c.getConstructor(WXSDKInstance.class, WXDomObject.class, WXVContainer.class);
              } catch (NoSuchMethodException e) {
                WXLogUtils.d("ClazzComponentCreator","Use deprecated component constructor");
                try {
                  //compatible deprecated constructor with 4 args
                  constructor = c.getConstructor(WXSDKInstance.class, WXDomObject.class, WXVContainer.class, boolean.class);
                } catch (NoSuchMethodException e1) {
                  try {
                    //compatible deprecated constructor with 5 args
                    constructor = c.getConstructor(WXSDKInstance.class, WXDomObject.class, WXVContainer.class,String.class, boolean.class);
                  } catch (NoSuchMethodException e2) {
                    throw new WXRuntimeException("Can't find constructor of component.");
                  }
                }
              }
              mConstructor = constructor;
            }
      
            @Override
            public WXComponent createInstance(WXSDKInstance instance, WXDomObject node, WXVContainer parent) throws IllegalAccessException, InvocationTargetException, InstantiationException {
              if(mConstructor == null){
                loadConstructor();
              }
              int parameters = mConstructor.getParameterTypes().length;
              WXComponent component;
      
              if(parameters == 3){
                //这个构造方法是自动生成instanceId和默认懒加载false.懒加载的flag判断,定义在了自定义注解里面了。
                component =  mConstructor.newInstance(instance,node,parent);
              }else if(parameters == 4){
                //这个是自动生成instanceId
                component =  mConstructor.newInstance(instance,node,parent,false);
              }else{
                //compatible deprecated constructor
                //这个是兼容过时的构造器。在构造方法中定义instanceId和默认懒加载flag
                component =  mConstructor.newInstance(instance,node,parent,instance.getInstanceId(),parent.isLazy());
              }
              return component;
            }
          }
      
    • registerNativeComponent

      接下来看看,向native是如何注册这个Component的

      
        private static boolean registerNativeComponent(String type, IFComponentHolder holder) throws WXException {
          try {
            //判断是否是懒加载,如上面方法分析过,如果不是懒加载,则直接反射生成属性和方法的map
            holder.loadIfNonLazy();
            //放入TypeComponentMap中
            sTypeComponentMap.put(type, holder);
          }catch (ArrayStoreException e){
            e.printStackTrace();
            //ignore: ArrayStoreException: java.lang.String cannot be stored in an array of type java.util.HashMap$HashMapEntry[]
          }
          return true;
        }
      
    • registerJsComponent

        private static boolean registerJSComponent(Map<String, Object> componentInfo) throws WXException {
          ArrayList<Map<String, Object>> coms = new ArrayList<>();
          coms.add(componentInfo);
          //通过WxSDKManager注册这个Components
          WXSDKManager.getInstance().registerComponents(coms);
          return true;
        }
      

      通过WXSDKManager,最后转发到了WxBridgeManager中的invokeRegisterComponents方法

       private void invokeRegisterComponents(List<Map<String, Object>> components, List<Map<String, Object>> failReceiver) {
         //错误的列表不能等于源列表
          if (components == failReceiver) {
            throw new RuntimeException("Fail receiver should not use source.");
          }
         //如果注册时js还未初始化,则会加入错误的列表中,等待初始化后进行加载
          if (!isJSFrameworkInit()) {
            WXLogUtils.e("[WXBridgeManager] invokeRegisterComponents: framework.js uninitialized.");
      
            for (Map<String, Object> comp : components) {
              failReceiver.add(comp);
            }
            return;
          }
          if (components == null) {
            return;
          }
        //转成WXJSObject对象数组。这个WXJsObject就是与JsFramework通行的实体。
         //WxJsonUtils中通过fastJson将map转成json结构
          WXJSObject[] args = {new WXJSObject(WXJSObject.JSON,
              WXJsonUtils.fromObjectToJSONString(components))};
          try {
            //通过WxBridge进行通行,执行js代码。注入到js框架中。
            mWXBridge.execJS("", null, METHOD_REGISTER_COMPONENTS, args);
          } catch (Throwable e) {
            WXLogUtils.e("[WXBridgeManager] invokeRegisterComponents ", e);
          WXExceptionUtils.commitCriticalExceptionRT(null,
                  WXErrorCode.WX_KEY_EXCEPTION_INVOKE_REGISTER_CONTENT_FAILED.getErrorCode(),
                  METHOD_REGISTER_COMPONENTS,
                  WXErrorCode.WX_KEY_EXCEPTION_INVOKE_REGISTER_CONTENT_FAILED.getErrorMsg()
                          + args.toString()
                          + WXLogUtils.getStackTrace(e),
                  null);
          }
        }
      
      

      这样这个Components就在WXComponentRegistry中注册完成了。等待调用。

    modules的注册过程

    和Component的注册过程类似。modules的注册,最后是转到了WXModuleManager中的registerModule方法中进行。

     /**
       *
       * @param moduleName module的名称
       * @param factory 生成ModuleFactory 其中提供了TypeModuleFactory这个默认实现的工厂类
       * @param global 是否全局的module。就会去放入全局的缓存当中
       * @return
       * @throws WXException
       */
    public static boolean registerModule(final String moduleName, final ModuleFactory factory, final boolean global) throws WXException {
        if (moduleName == null || factory == null) {
          return false;
        }
        //这里需要注意的module的名字是dom的字样。应该是和内置的dom冲突了?
        if (TextUtils.equals(moduleName,WXDomModule.WXDOM)) {
          WXLogUtils.e("Cannot registered module with name 'dom'.");
          return false;
        }
    
        //和Component相同的套路。
        //execute task in js thread to make sure register order is same as the order invoke register method.
        WXBridgeManager.getInstance()
            .post(new Runnable() {
          @Override
          public void run() {
            if (sModuleFactoryMap.containsKey(moduleName)) {
              WXLogUtils.w("WXComponentRegistry Duplicate the Module name: " + moduleName);
            }
            //可以看到这个global flag的作用。就是是否在全局缓存这个module.
            if (global) {
              //如果是全局缓存的话,则马上构造,并且放入map当中
              try {
                WXModule wxModule = factory.buildInstance();
                wxModule.setModuleName(moduleName);
                sGlobalModuleMap.put(moduleName, wxModule);
              } catch (Exception e) {
                WXLogUtils.e(moduleName + " class must have a default constructor without params. ", e);
              }
            }
            //同样需要向native和js中分别注册module
            try {
              registerNativeModule(moduleName, factory);
            } catch (WXException e) {
              WXLogUtils.e("", e);
            }
            registerJSModule(moduleName, factory);
          }
        });
        return true;
      }
    
    • ModuleFactory

      按照Component养成的惯例,还是先来看看默认实现类TypeModuleFactory

      public class TypeModuleFactory<T extends WXModule> implements ModuleFactory<T> {
        public static final String TAG = "TypeModuleFactory";
        //对比Component这里就直接缓存Class类就可以了。应该没有对应的构造方法需要规定
        Class<T> mClazz;
        //缓存这个类的方法
        Map<String, Invoker> mMethodMap;
      
        public TypeModuleFactory(Class<T> clz) {
          mClazz = clz;
        }
      
        //生成方法
        private void generateMethodMap() {
          if(WXEnvironment.isApkDebugable()) {
            WXLogUtils.d(TAG, "extractMethodNames:" + mClazz.getSimpleName());
          }
          HashMap<String, Invoker> methodMap = new HashMap<>();
          try {
            //同样是开始遍历
            for (Method method : mClazz.getMethods()) {
              // iterates all the annotations available in the method
              for (Annotation anno : method.getDeclaredAnnotations()) {
                if (anno != null) {
                  //这里是同样是通过JsMethod这个注解来标记方法。同时设置是否uiThread执行的
                  if(anno instanceof JSMethod) {
                    JSMethod methodAnnotation = (JSMethod) anno;
                    String name = JSMethod.NOT_SET.equals(methodAnnotation.alias())? method.getName():methodAnnotation.alias();
                    methodMap.put(name, new MethodInvoker(method, methodAnnotation.uiThread()));
                    break;
                  }else if(anno instanceof WXModuleAnno) {
                    //WxModuleAnno这个注解来标注。这个注解等同于JsMethod.现在已经过时了。
                    WXModuleAnno methodAnnotation = (WXModuleAnno)anno;
                    methodMap.put(method.getName(), new MethodInvoker(method,methodAnnotation.runOnUIThread()));
                    break;
                  }
                }
              }
            }
          } catch (Throwable e) {
            WXLogUtils.e("[WXModuleManager] extractMethodNames:", e);
          }
          mMethodMap = methodMap;
        }
        
        //构造实例
         @Override
        public T buildInstance() throws IllegalAccessException, InstantiationException {
          return mClazz.newInstance();
        }
      
        //如果不是global的方法,则从这儿开始反射得到所有的方法
        @Override
        public String[] getMethods() {
          if (mMethodMap == null) {
            generateMethodMap();
          }
          Set<String> keys = mMethodMap.keySet();
          return keys.toArray(new String[keys.size()]);
        }
      
        @Override
        public Invoker getMethodInvoker(String name) {
          if (mMethodMap == null) {
            generateMethodMap();
          }
          return mMethodMap.get(name);
        }
      }
      
    • registerNativeModule()

      //相同的套路。native注册,就是moduleFactory缓存到全局的map当中
        static boolean registerNativeModule(String moduleName, ModuleFactory factory) throws WXException {
          if (factory == null) {
            return false;
          }
      
          try {
            sModuleFactoryMap.put(moduleName, factory);
          }catch (ArrayStoreException e){
            e.printStackTrace();
            //ignore:
            //may throw this exception:
            //java.lang.String cannot be stored in an array of type java.util.HashMap$HashMapEntry[]
          }
          return true;
        }
      

    • registerJsModule()

      //js注册,同样是通过WxSDKManager转发到WxBridgeManager中进行注册 
      static boolean registerJSModule(String moduleName, ModuleFactory factory) {
          Map<String, Object> modules = new HashMap<>();
          modules.put(moduleName, factory.getMethods());
          WXSDKManager.getInstance().registerModules(modules);
          return true;
        }
      

      WXBridgeManager中进行注册

       private void invokeRegisterModules(Map<String, Object> modules, List<Map<String, Object>> failReceiver) {
          if (modules == null || !isJSFrameworkInit()) {
            if (!isJSFrameworkInit()) {
              WXLogUtils.d("[WXinvokeRegisterModulesBridgeManager] invokeRegisterModules: framework.js uninitialized.");
            }
            failReceiver.add(modules);
            return;
          }
      
          WXJSObject[] args = {new WXJSObject(WXJSObject.JSON,
              WXJsonUtils.fromObjectToJSONString(modules))};
          try {
            mWXBridge.execJS("", null, METHOD_REGISTER_MODULES, args);
          } catch (Throwable e) {
          WXExceptionUtils.commitCriticalExceptionRT(null,
                  WXErrorCode.WX_KEY_EXCEPTION_INVOKE_REGISTER_MODULES.getErrorCode(),
                  "invokeRegisterModules", WXErrorCode.WX_KEY_EXCEPTION_INVOKE_REGISTER_MODULES.getErrorMsg() +
                  " \n " + e.getMessage() + modules.entrySet().toString(),
                  null );
      
            WXLogUtils.e("[WXBridgeManager] invokeRegisterModules:", e);
          }
        }
      
    DomObject的注册过程
    • 什么是WXDomObject

      更具注释的介绍,我们知道这类只包含所有所有节点的信息。包括style, attribute and event。它只包含了dom的信息,不包括如何去渲染。

      实际上,每一个Component持有一个androidview的实例和WXDomObject的实例。

    基本上是相同的套路。最后是在WXDomRegistry注册。注册的过程更为简单,只是将class换成起来。

    public class WXDomRegistry {
    
      public static Class<? extends WXDomObject> mDefaultClass = WXDomObject.class;
      private static Map<String, Class<? extends WXDomObject>> sDom = new HashMap<>();
    
      public static boolean registerDomObject(String type, Class<? extends WXDomObject> clazz) throws WXException {
        if (clazz == null || TextUtils.isEmpty(type)) {
          return false;
        }
    
        if (sDom.containsKey(type)) {
          if (WXEnvironment.isApkDebugable()) {
            throw new WXException("WXDomRegistry had duplicate Dom:" + type);
          } else {
            WXLogUtils.e("WXDomRegistry had duplicate Dom: " + type);
            return false;
          }
        }
        sDom.put(type, clazz);
        return true;
      }
    
      public static Class<? extends WXDomObject> getDomObjectClass(String type) {
        if (TextUtils.isEmpty(type)) {
          return mDefaultClass;
        }
        Class<? extends WXDomObject> clazz = sDom.get(type);
        return clazz == null ? mDefaultClass : clazz;
      }
    }
    

    到这里,就完成了WXSDKEngine的默认的初始化过程了

    3. 初始化自定义组件

    通过和上述相同的方式,自定义组件的注册的方式和上面相同。

    Weex 是如何让JS调起原生View

    上一章节我们分析了WXSDKEngine是如何初始化的,那么初始化完成之后,Android Native客户端是如何接收到JS的页面并生成View的呢?这一章节我们来分析分析。

    进入到IndexActivity中,在onCreate()方法中调用super.onCreate()方法中进行初始化,并且render()这样的方法。其实是来到了WXSDKInstance中的对应方法。

    WXSDKInstance

    初始化
      protected void createWeexInstance(){
        destoryWeexInstance();
        //这两行代码其实没啥用
        Rect outRect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
        //new一个instance 并且监听render时间是否完成
        mInstance = new WXSDKInstance(this);
        mInstance.registerRenderListener(this);
      }
    
    //render view创建成功的回调。
    //playground中,将对应点的行为解耦出来成为mWxAnalyzerDelegate,来处理打点分析
       @Override
      public void onViewCreated(WXSDKInstance wxsdkInstance, View view) {
        View wrappedView = null;
        if(mWxAnalyzerDelegate != null){
          wrappedView = mWxAnalyzerDelegate.onWeexViewCreated(wxsdkInstance,view);
        }
        if(wrappedView != null){
          view = wrappedView;
        }
        if (mContainer != null) {
          mContainer.removeAllViews();
          mContainer.addView(view);
        }
      }
    
    
    
      @Override
      public void onRefreshSuccess(WXSDKInstance wxsdkInstance, int i, int i1) {
    
      }
    //render成功的回调
      @Override
      @CallSuper
      public void onRenderSuccess(WXSDKInstance instance, int width, int height) {
        if(mWxAnalyzerDelegate  != null){
          mWxAnalyzerDelegate.onWeexRenderSuccess(instance);
        }
      }
    
    //出错的回调。
      @Override
      @CallSuper
      public void onException(WXSDKInstance instance, String errCode, String msg) {
        if(mWxAnalyzerDelegate != null){
          mWxAnalyzerDelegate.onException(instance,errCode,msg);
        }
      }
    
    

    WXSDKInstance中的构造方法

    public WXSDKInstance(Context context) {
        //自动生成一个instanceId
        mInstanceId = WXSDKManager.getInstance().generateInstanceId();
        //初始化这个实例
        init(context);
     }
    
     public void init(Context context) {
        mContext = context;
        //通过这个helper来调用原生的方法
        mNativeInvokeHelper = new NativeInvokeHelper(mInstanceId);
        //生成对应的性能的log
        mWXPerformance = new WXPerformance();
        mWXPerformance.WXSDKVersion = WXEnvironment.WXSDK_VERSION;
        mWXPerformance.JSLibInitTime = WXEnvironment.sJSLibInitTime;
        //用户信息打点
        mUserTrackAdapter=WXSDKManager.getInstance().getIWXUserTrackAdapter();
      }
    
    Render方法

    IndexActivityonCreate()中,需要设置局域网的IP地址才能连接到本地开发服务器。如果没有的话,会通过加载本地Assets目录下的Js文件进行渲染。

       if (TextUtils.equals(sCurrentIp, DEFAULT_IP)) {
          //如果没有配置,则默认会相同,走到这个位置来加载这个 landing.weex.js文件
          renderPage(WXFileUtils.loadAsset("landing.weex.js", this), getIndexUrl());
        } else {
          renderPageByURL(getIndexUrl());
        }
    
        //注册一个广播。如果有地方发送了这个广播,则本地会刷新这个文件
        mReloadReceiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
            createWeexInstance();
            if (TextUtils.equals(sCurrentIp, DEFAULT_IP)) {
              renderPage(WXFileUtils.loadAsset("landing.weex.js", getApplicationContext()), getIndexUrl());
            } else {
              renderPageByURL(getIndexUrl());
            }
            mProgressBar.setVisibility(View.VISIBLE);
          }
        };
        //注册一个本地广播。比全局广播更加有效率。只在自己的app内能够使用
        LocalBroadcastManager.getInstance(this).registerReceiver(mReloadReceiver, new IntentFilter(WXSDKEngine.JS_FRAMEWORK_RELOAD));
    

    渲染的代码,最终会来到WXSDKInstancerender 方法中

      /**
       * Render template asynchronously
       * 异步的方式来渲染我们的js模板
       * @param pageName, used for performance log. 页面的名称,用在log中
       * @param template bundle js js的模板
       * @param options  os   iphone/android/ipad 设置options的参数。也是用来打点的
       *                 weexversion    Weex version(like 1.0.0)
       *                 appversion     App version(like 1.0.0)
       *                 devid        Device id(like Aqh9z8dRJNBhmS9drLG5BKCmXhecHUXIZoXOctKwFebH)
       *                 sysversion    Device system version(like 5.4.4、7.0.4, should be used with os)
       *                 sysmodel     Device model(like iOS:"MGA82J/A", android:"MI NOTE LTE")
       *                 Time    UNIX timestamp, UTC+08:00
       *                 TTID(Optional)
       *                 MarkertId
       *                 Appname(Optional)  tm,tb,qa
       *                 Bundleurl(Optional)  template url
       * @param jsonInitData Initial data for rendering
       * @param flag     RenderStrategy {@link WXRenderStrategy} 加载的策略有异步的方式APPEND_ASYNC("APPEND_ASYNC")和 仅一次加载 APPEND_ONCE("APPEND_ONCE")两种方式可以选择
       */
      public void render(String pageName, String template, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag) {
        if(WXEnvironment.isApkDebugable() && WXPerformance.DEFAULT.equals(pageName)){
          WXLogUtils.e("WXSDKInstance", "Please set your pageName or your js bundle url !!!!!!!");
    
          if (getUIContext() != null) {
            new AlertDialog.Builder(getUIContext())
                .setTitle("Error: Missing pageName")
                .setMessage("We highly recommend you to set pageName. Call" +
                    "\nWXSDKInstance#render(String pageName, String template, Map<String, Object> options, String jsonInitData, WXRenderStrategy flag)\n" +
                    "to fix it.")
                .show();
          }
    
          return;
        }
         //继续进入下一个方法
        renderInternal(pageName,template,options,jsonInitData,flag);
      }
    
    
     private void renderInternal(String pageName,
                                  String template,
                                  Map<String, Object> options,
                                  String jsonInitData,
                                  WXRenderStrategy flag){
         //如果已经渲染成功,则停止
        if (mRendered || TextUtils.isEmpty(template)) {
          return;
        }
        //设置performance pageName
        mWXPerformance.pageName = (TextUtils.isEmpty(pageName) ? "defaultBundleUrl":pageName);
        if (TextUtils.isEmpty(mBundleUrl)) {
          mBundleUrl = mWXPerformance.pageName;
        }
    
        WXLogUtils.d("WXSDKInstance", "Start render page: " + pageName);
        
        //监控数据监控
        if (WXTracing.isAvailable()) {
          WXTracing.TraceEvent traceEvent = WXTracing.newEvent("executeBundleJS", mInstanceId, -1);
          traceEvent.traceId = mExecJSTraceId;
          traceEvent.iid = mInstanceId;
          traceEvent.tname = "JSThread";
          traceEvent.ph = "B";
          traceEvent.submit();
          mRenderStartNanos = System.nanoTime();
        }
        //创建一个render出来view的锚点容器。一个持有改instance弱应用的FrameLayout-->mRenderContainer
        ensureRenderArchor();
    
        Map<String, Object> renderOptions = options;
        if (renderOptions == null) {
          renderOptions = new HashMap<>();
        }
        //如果是动态模式,并且动态的url不为空,则会去加载url
        if (WXEnvironment.sDynamicMode && !TextUtils.isEmpty(WXEnvironment.sDynamicUrl) && renderOptions.get("dynamicMode") == null) {
          renderOptions.put("dynamicMode", "true");
          renderByUrl(pageName, WXEnvironment.sDynamicUrl, renderOptions, jsonInitData, flag);
          return;
        }
        //数据统计
        mWXPerformance.JSTemplateSize = template.length() / 1024f;
    
        mRenderStartTime = System.currentTimeMillis();
        mRenderStrategy = flag;
    
        WXSDKManager.getInstance().setCrashInfo(WXEnvironment.WEEX_CURRENT_KEY,pageName);
        //这里通过WXSDKManager来创建instance
        WXSDKManager.getInstance().createInstance(this, template, renderOptions, jsonInitData);
        mRendered = true;
      }
    

    WXSDKManagercreateInstance()

    void createInstance(WXSDKInstance instance, String code, Map<String, Object> options, String jsonInitData) {
      //注册到renderManager中
      mWXRenderManager.registerInstance(instance);
      //通过WXBridgeManager createInstance向jsf通信
      mBridgeManager.createInstance(instance.getInstanceId(), code, options, jsonInitData);
      //回调
      if (mLifeCycleCallbacks != null) {
        for (InstanceLifeCycleCallbacks callbacks : mLifeCycleCallbacks) {
          callbacks.onInstanceCreated(instance.getInstanceId());
        }
      }
    }
    

    下面兵分两路,分别来看看都做了什么事情

    WXRenderManager

    WXRenderManager是一个render操作的管理类,并且是线程安全的。主要用来管理RenderActionContextImpl

    public class WXRenderManager {
    //线程安全的map.存放RenderActionContextImpl
      private ConcurrentHashMap<String, RenderActionContextImpl> mRegistries;
        //其实就是一个ui线程的handler
      private WXRenderHandler mWXRenderHandler;
    
      public WXRenderManager() {
        mRegistries = new ConcurrentHashMap<>();
        mWXRenderHandler = new WXRenderHandler();
      }
    
      public RenderActionContext getRenderContext(String instanceId) {
        return mRegistries.get(instanceId);
      }
    
      //上面来注册,就是创建个RenderActionContextImpl实例,放到map当中。
      //这个RenderActionContextImpl就如它的名字一样,是instance内保存Component信息的类。
       //通过这个类,来完成Component的layout
      public void registerInstance(WXSDKInstance instance) {
        mRegistries.put(instance.getInstanceId(), new RenderActionContextImpl(instance));
      }
    
      //先省略若干方法
    }
    
    
    /**
    * 再来看一下RenderActionContextImpl到底是什么
     * 这个类就是用来renderingView的。而且和WXDomStatement很相似
     */
    class RenderActionContextImpl implements RenderActionContext {
    
      private Map<String, WXComponent> mRegistry;
      private WXSDKInstance mWXSDKInstance;
      /**
       * The container for weex root view.
       */
    
      public RenderActionContextImpl(WXSDKInstance instance) {
        mWXSDKInstance = instance;
        mRegistry = new HashMap<>();
      }
    
      /**
       * @see com.taobao.weex.dom.WXDomStatement#destroy()
       */
      public void destroy() {
        mWXSDKInstance = null;
        mRegistry.clear();
      }
    
      public WXSDKInstance getWXSDKInstance() {
        return mWXSDKInstance;
      }
    
      /**
       * set layout information of View
       * 设置布局的信息。
       * ref 是 改节点的标识
       * WXDomObject 
       */
      void setLayout(String ref, WXDomObject domObject) {
        WXComponent component = mRegistry.get(ref);
        if (component == null) {
          return;
        }
        component.setLayout(domObject);
      }
    
      /**
       * set extra information of View
       */
      void setExtra(String ref, Object extra) {
        WXComponent component = mRegistry.get(ref);
        if (component == null) {
          return;
        }
        component.updateExtra(extra);
      }
    
      @Override
      public WXSDKInstance getInstance() {
        return mWXSDKInstance;
      }
    
      @Override
      public WXComponent getComponent(String ref) {
        return mRegistry.get(ref);
      }
    
      //注册
      public void registerComponent(String ref, WXComponent comp) {
        mRegistry.put(ref,comp);
      }
    
      //清除
      public WXComponent unregisterComponent(String ref) {
        return mRegistry.remove(ref);
      }
    }
    
    
    WXBridgeManager
     /**
       * Create instance.
       */
      public void createInstance(final String instanceId, final String template,
                                 final Map<String, Object> options, final String data) {
        final WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
        if (instance == null) {
          WXLogUtils.e("WXBridgeManager", "createInstance failed, SDKInstance is not exist");
          return;
        }
         //如果任一个为空,则直接报错
        if (TextUtils.isEmpty(instanceId) || TextUtils.isEmpty(template) || mJSHandler == null) {
          instance.onRenderError(
                  WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorCode(),
                  WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorMsg() +
          " instanceId==" + instanceId + " template ==" + template + " mJSHandler== " + mJSHandler.toString()
          );
          return;
        }
    
          //报错检查
        if (!isJSFrameworkInit() && reInitCount == 1 && !WXEnvironment.sDebugServerConnectable) {
          instance.onRenderError(
                  WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorCode(),
                  WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorMsg() +
          " isJSFrameworkInit==" + isJSFrameworkInit() + " reInitCount == 1" );
          post(new Runnable() {
            @Override
            public void run() {
              initFramework("");
            }
          }, instanceId);
          return;
        }
    
        //创建一个ModuleManager。由上一章知道,moduleManager是缓存和管理module的类
        WXModuleManager.createDomModule(instance);
        //通过JSThread发送
        post(new Runnable() {
          @Override
          public void run() {
            long start = System.currentTimeMillis();
            //执行在JSThread中真实的方法
            invokeCreateInstance(instance, template, options, data);
            final long totalTime = System.currentTimeMillis() - start;
            //回调到ui线程的创建结束的方法
            WXSDKManager.getInstance().postOnUiThread(new Runnable() {
    
                @Override
                public void run() {
                   //这里的回调,在playground app中,只是为了打点
                instance.createInstanceFinished(totalTime);
              }
            }, 0);
          }
        }, instanceId);
      }
    
    
      private void invokeCreateInstance(@NonNull WXSDKInstance instance, String template,
                                        Map<String, Object> options, String data) {
        //如果未初始化jsf,则报错
        initFramework("");
    
        if (mMock) {
          mock(instance.getInstanceId());
        } else {
          if (!isJSFrameworkInit()) {
            String err = "[WXBridgeManager] invokeCreateInstance: framework.js uninitialized.";
            instance.onRenderError(
                    WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorCode(),
                    WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorMsg()
            );
            WXLogUtils.e(err);
            return;
          }
          try {
            if (WXEnvironment.isOpenDebugLog()) {
              WXLogUtils.d("createInstance >>>> instanceId:" + instance.getInstanceId()
                  + ", options:"
                  + WXJsonUtils.fromObjectToJSONString(options)
                  + ", data:" + data);
            }
            //创建通信的WXJSObject对象数组进行同时
            WXJSObject instanceIdObj = new WXJSObject(WXJSObject.String,
                instance.getInstanceId());
            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};
            instance.setTemplate(template);
            //将上面的命令,转成了Js的function,调用执行JS的命令,进行创建!
            invokeExecJS(instance.getInstanceId(), null, METHOD_CREATE_INSTANCE, args, false);
          } catch (Throwable e) {
            String err = "[WXBridgeManager] invokeCreateInstance " + e.getCause()
                    + instance.getTemplateInfo();
    
            instance.onRenderError(
                    WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorCode(),
                    WXRenderErrorCode.DegradPassivityCode.WX_DEGRAD_ERR_INSTANCE_CREATE_FAILED.getDegradErrorMsg() + err);
            WXLogUtils.e(err);
          }
        }
      }
    
    //这个方法其实就是为了输出一个log.然后同执行execJs
     public void invokeExecJS(String instanceId, String namespace, String function,
                               WXJSObject[] args, boolean logTaskDetail) {
         if (WXEnvironment.isOpenDebugLog()) {
          mLodBuilder.append("callJS >>>> instanceId:").append(instanceId)
              .append("function:").append(function);
          if (logTaskDetail) {
            mLodBuilder.append(" tasks:").append(argsToJSON(args));
          }
          WXLogUtils.d(mLodBuilder.substring(0));
          mLodBuilder.setLength(0);
        }
        //Execute JavaScript function
        mWXBridge.execJS(instanceId, namespace, function, args);
      }
    

    这里可以看到,最后是用我们的参数,调用了mWXBridge.execJS(instanceId, namespace, function, args);native方法传递给JSF

    callNative(...)

    execJsnative方法,会反射调用callNative(...)方法。通过这样的方式来回调native方法。

      /**
       * JavaScript uses this methods to call Android code
       *
       * @param instanceId
       * @param tasks
       * @param callback
       */
    
      public int callNative(String instanceId, byte [] tasks, String callback) {
        try {
         return callNative(instanceId,(JSONArray)WXJsonUtils.parseWson(tasks),callback);
        } catch (Throwable e) {
          //catch everything during call native.
          // if(WXEnvironment.isApkDebugable()){
          WXLogUtils.e(TAG,"callNative throw exception:"+e.getMessage());
          // }
          return 0;
        }
      }
    
     public int callNative(String instanceId, JSONArray tasks, String callback) {
        long start = System.currentTimeMillis();
        WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
        if(instance != null) {
          instance.firstScreenCreateInstanceTime(start);
        }
        int errorCode = IWXBridge.INSTANCE_RENDERING;
        try {
           //再到WXBridgeManager中进行调用
          errorCode = WXBridgeManager.getInstance().callNative(instanceId, tasks, callback);
        }catch (Throwable e){
          //catch everything during call native.
          // if(WXEnvironment.isApkDebugable()){
            WXLogUtils.e(TAG,"callNative throw exception:"+e.getMessage());
          // }
        }
    
        if(instance != null) {
          instance.callNativeTime(System.currentTimeMillis() - start);
        }
        if(WXEnvironment.isApkDebugable()){
          if(errorCode == IWXBridge.DESTROY_INSTANCE){
            WXLogUtils.w("destroyInstance :"+instanceId+" JSF must stop callNative");
          }
        }
        return errorCode;
      }
    

    接下来会进行一些native的布局操作,在这编文章内就暂时不深究了。

    总结

    最后,先看一下注册过程的类结构图


    注册过程中的类图.jpg

    虽然看了很多源码,但是形成的印象还是很笼统。
    上面这个例子中,JSFramework的工作原理基本就展现出来了。大体流程如下图:

    接下来详细总结一下JSFramework在整个Native端是如何工作的。

    1. Weex本身是由JSFramework和Native Render、和虚拟的Dom,这三大部分组成。本文覆盖的范围还主要在jsf的初始化和native render调用的开始。
    2. 首先,JSFramework是全局单例,但是WXSDKInstance是每个页面自己的实例。
    3. Weex内,我们看到的线程就存在了JSThread、UiThread的两种线程相互工作
    4. 渲染的核心最根本的在于native通过execJs的native方法,调用js的function进行工作。然后再通过callNative的方法进行回调native对应的代码

    更多

    本篇文章只大概讲述了Weex是如何在Android Native端跑起来的原理,但是关于Weex其实还有很多很多疑问没有弄清。
    Weex内的线程模型,线程内相互是如何通信的?

    比如说在Vue.js页面更改了一个页面元素,是怎么能让Native页面及时的变更?

    Weex的页面是怎么通过FlexBox算法进行渲染的?

    前端页面是如何打包成JS bundle的?

    .we和.vue文件是怎么通过DSL被翻译的?

    如何利用JS的Runtime写一些强大的JSService?

    webpackBootstrap和weex-loader是如何生成最终的JS代码的,中间有哪些优化?……

    相关文章

      网友评论

        本文标题:Weex是如何在Android客户端上跑起来的

        本文链接:https://www.haomeiwen.com/subject/xxainxtx.html