Weex Android SDK源码分析之界面渲染(下)

Posted 王永迪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Weex Android SDK源码分析之界面渲染(下)相关的知识,希望对你有一定的参考价值。

前言

在你读本篇文章之前请确认您已经阅读 Weex Android SDK源码分析之界面渲染(上),否则建议您先阅读上篇文章有利于您更深刻的了解~

流程图

源码分析

回忆一下上篇文章的讲解,最终由WxBridgeManage调用WxBridge调用了so中native代码进行JS代码解析,那么解析完成之后是如何返回给android代码的呢?so返回的是什么数据呢?数据时如何转换的呢?数据与最终的UI有什么关系呢?最终是如何转换成view的呢?如何传递到activity中的呢?
带着这些的疑问,我们开始今天得讲解~

一、WXBridge —> 解析JS回调Android code

    /**
     * Js 使用此方法回调Android代码
     * @param instanceId 
     * @param tasks 任务
     * @param callback 下一个任务的ID
     */
    public void callNative(String instanceId, String tasks, String callback) 
        WXBridgeManager.getInstance().callNative(instanceId, tasks, callback);
    

二、WXBridgeManager — > 分配渲染任务

    /**
     * Dispatch the native task to be executed.
     *
     * @param instanceId @link WXSDKInstance#mInstanceId
     * @param tasks      tasks to be executed
     * @param callback   next tick id 下一个挂钩子ID,so执行需要的
     */
    void callNative(String instanceId, String tasks, String callback) 
        if (TextUtils.isEmpty(tasks)) 
            return;
        

        if (WXEnvironment.isApkDebugable()) 
            mLodBuilder.append("[WXBridgeManager] callNative >>>> 
            instanceId:").append(instanceId)
                       .append(", tasks:")
                       .append(tasks).append(", callback:").append(callback);
            WXLogUtils.d(mLodBuilder.substring(0));
            mLodBuilder.setLength(0);
        

        JSONArray array = JSON.parseArray(tasks);
        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) 
                        WXModuleManager
                        .callModuleMethod(instanceId, (String) task.get("module")
                        , (String) task.get("method")
                        ,(JSONArray) task.get("args"));
                    
                
             catch (Exception e) 
            
        

        if (UNDEFINED.equals(callback)
            || mDestroyedInstanceId.equals(instanceId)) 
            return;
        
        // get next tick
        getNextTick(instanceId, callback);
    

    // 获取下一个需要渲染的JS数据
    private void getNextTick(final String instanceId, final String callback) 
        addNextTickTask(instanceId, callback, "");
        sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH);
    

    // 添加渲染数据
    private void addNextTickTask(String instanceId, Object... args) 
        if (args == null || args.length == 0) 
            return;
        

        ArrayList<Object> argsList = new ArrayList<>();
        for (Object arg : args) 
            argsList.add(arg);
        

        WXHashMap<String, Object> task = new WXHashMap<>();
        task.put(WXConst.KEY_METHOD, METHOD_CALLBACK);
        task.put(WXConst.KEY_ARGS, args);

        if (mNextTickTasks.get(instanceId) == null) 
            ArrayList<WXHashMap<String, Object>> list =
             new ArrayList<WXHashMap<String, Object>>();
            list.add(task);
            mNextTickTasks.put(instanceId, list);
         else 
            mNextTickTasks.get(instanceId).add(task);
        
    

三、WXModuleManager —> 回调module方法

// 执行module中的方法
static boolean callModuleMethod(String instanceId, String moduleStr
    , String methodStr, JSONArray args) 
    final WXModule wxModule = findModule(instanceId, moduleStr, methodStr);
    if (wxModule == null) 
      return false;
    
    wxModule.mWXSDKInstance =
     WXSDKManager.getInstance().getSDKInstance(instanceId);
    HashMap<String, Method> methodsMap = sGlobalModuleMethodMap.get(wxModule);
    methodsMap = methodsMap ==
     null ? sInstanceModuleMethodMap.get(wxModule) : methodsMap;
    if (methodsMap == null) 
      return false;
    
    final Method moduleMethod = methodsMap.get(methodStr);
    try 
      Type[] paramClazzs = moduleMethod.getGenericParameterTypes();
      final Object[] params = new Object[paramClazzs.length];
      Object value;
      Type paramClazz;
      for (int i = 0; i < paramClazzs.length; i++) 
        paramClazz = paramClazzs[i];
        value = args.get(i);

        if (paramClazz == JSONObject.class) 
          params[i] = value;
         else 
          String sValue;
          if (value instanceof String) 
            sValue = (String) value;
           else 
            sValue = JSON.toJSONString(value);
          

          if (paramClazz == int.class) 
            params[i] = Integer.parseInt(sValue);
           else if (paramClazz == String.class) 
            params[i] = sValue;
           else if (paramClazz == long.class) 
            params[i] = Long.parseLong(sValue);
           else if (paramClazz == double.class) 
            params[i] = Double.parseDouble(sValue);
           else if (paramClazz == float.class) 
            params[i] = Float.parseFloat(sValue);
           else if (ParameterizedType.class.
          isAssignableFrom(paramClazz.getClass())) 
            params[i] = JSON.parseObject(sValue, paramClazz);
           else if (IWXObject.class.isAssignableFrom(paramClazz.getClass())) 
            params[i] = JSON.parseObject(sValue, paramClazz);
           else 
            params[i] = JSON.parseObject(sValue, paramClazz);
          
        
      
      // *** 找到道注解的方法体,进行执行 *** 
      WXModuleAnno anno = moduleMethod.getAnnotation(WXModuleAnno.class);
      if (anno.runOnUIThread()) 
        WXSDKManager.getInstance().postOnUiThread(new Runnable() 
          @Override
          public void run() 
            try 
              // *** 执行module方法 ***
              moduleMethod.invoke(wxModule, params);
             catch (Exception e) 
           
          
        , 0);
       else 
        moduleMethod.invoke(wxModule, params);
      
     catch (Exception e) 
      return false;
     finally 
      if (wxModule instanceof WXDomModule) 
        wxModule.mWXSDKInstance = null;
      
    
    return true;
  

  // 找到module 例如 WXDOMModule
  private static WXModule findModule(String instanceId, String moduleStr, String methodStr) 
    // find WXModule
    WXModule wxModule = sGlobalModuleMap.get(moduleStr);
    Class<? extends WXModule> moduleClass = sModuleClazzMap.get(moduleStr);
    if (wxModule == null) 
      HashMap<String, WXModule> moduleMap = sInstanceModuleMap.get(instanceId);
      if (moduleMap == null) 
        moduleMap = new HashMap<>();
        sInstanceModuleMap.put(instanceId, moduleMap);
      
      // if cannot find the Module, create a new Module and save it
      wxModule = moduleMap.get(moduleStr);
      if (wxModule == null) 
        try 
          wxModule = moduleClass.getConstructor().newInstance();
         catch (Exception e) 
          return null;
        
        moduleMap.put(moduleStr, wxModule);
        // set instance
        wxModule.mWXSDKInstance = 
        WXSDKManager.getInstance().getSDKInstance(instanceId);
      
    

    // find module method
    HashMap<String, Method> methodsMap = sGlobalModuleMethodMap.get(wxModule);
    methodsMap = methodsMap == null ? 
    sInstanceModuleMethodMap.get(wxModule) : methodsMap;
    if (methodsMap == null) 
      methodsMap = getModuleMethods2Map(moduleClass);
      sInstanceModuleMethodMap.put(wxModule, methodsMap);
    
    return wxModule;
  

四、WXDomModule — > module 方法被触发

public final class WXDomModule extends WXModule 

  /**
   *  创建 Body UI
   * @param element info about how to create a body
   */
  @WXModuleAnno(moduleMethod = true, runOnUIThread = false)
  public void createBody(JSONObject element) 
    if (element == null) 
      return;
    
    Message msg = Message.obtain();
    WXDomTask task = new WXDomTask();
    task.instanceId = mWXSDKInstance.getInstanceId();
    task.args = new ArrayList<>();
    task.args.add(element);
    msg.what = WXDomHandler.MsgType.WX_DOM_CREATE_BODY;
    msg.obj = task;
    // *** 发送createBody消息 ***
    WXSDKManager.getInstance().getWXDomManager().sendMessage(msg);
  
  

五、WXDomManager —> 发送createBody等消息

// 发送消息  createBody 或者 refresh等
public void sendMessage(Message msg) 
    if (msg == null || mDomHandler == null || mDomThread == null
        || !mDomThread.isWXThreadAlive() || mDomThread.getLooper() == null) 
      return;
    
    mDomHandler.sendMessage(msg);
  

六、WXDomHandler — > 分发任务类型

// DOM 操作handler
class WXDomHandler implements Handler.Callback 

  /**
   * The batch operation in dom thread will run at most once in 16ms.
   */
  private static final int DELAY_TIME = 16;//ms
  private WXDomManager mWXDomManager;
  private boolean mHasBatch = false;

  public WXDomHandler(WXDomManager domManager) 
    mWXDomManager = domManager;
  

  @Override
  public boolean handleMessage(Message msg) 
    if (msg == null) 
      return false;
    
    int what = msg.what;
    Object obj = msg.obj;
    WXDomTask task = null;
    if (obj instanceof WXDomTask) 
      task = (WXDomTask) obj;
    
    if (!mHasBatch) 
      mHasBatch = true;
      mWXDomManager.
      sendEmptyMessageDelayed(WXDomHandler.MsgType.WX_DOM_BATCH, DELAY_TIME);
    

    switch (what) 
      case MsgType.WX_DOM_CREATE_BODY:
        // *** createBody ***
        mWXDomManager.createBody(task.instanceId, (JSONObject) task.args.get(0));
        break;
      case MsgType.WX_DOM_UPDATE_ATTRS:
        mWXDomManager.updateAttrs(task.instanceId, 
        (String) task.args.get(0), (JSONObject) task.args.get(1));
        break;
      case MsgType.WX_DOM_UPDATE_STYLE:
        mWXDomManager.updateStyle(task.instanceId,
         (String) task.args.get(0), (JSONObject) task.args.get(1));
        break;
      case MsgType.WX_DOM_ADD_DOM:
        mWXDomManager.addDom(task.instanceId, (String) task.args.get(0), 
        (JSONObject) task.args.get(1), (Integer) task.args.get(2));
        break;
      case MsgType.WX_DOM_REMOVE_DOM:
        mWXDomManager.removeDom(task.instanceId, (String) task.args.get(0));
        break;
      case MsgType.WX_DOM_MOVE_DOM:
        mWXDomManager.moveDom(task.instanceId, (String) task.args.get(0), (String) task.args.get(1), (Integer) task.args.get(2));
        break;
      case MsgType.WX_DOM_ADD_EVENT:
        mWXDomManager.addEvent(task.instanceId, (String) task.args.get(0),
         (String) task.args.get(1));
        break;
      case MsgType.WX_DOM_REMOVE_EVENT:
        mWXDomManager.removeEvent(task.instanceId, 
        (String) task.args.get(0), (String) task.args.get(1));
        break;
      case MsgType.WX_DOM_CREATE_FINISH:
        mWXDomManager.createFinish(task.instanceId);
        break;
      case MsgType.WX_DOM_REFRESH_FINISH:
        mWXDomManager.refreshFinish(task.instanceId);
        break;
      case MsgType.WX_DOM_BATCH:
        mWXDomManager.batch();
        mHasBatch = false;
        break;

      case MsgType.WX_DOM_SCROLLTO:
        mWXDomManager.scrollToDom(task.instanceId,
         (String) task.args.get(0), (JSONObject) task.args.get(1));
        break;
      default:
        break;
    
    return true;
  


  public static class MsgType 

    public static final int WX_DOM_CREATE_BODY = 0x0;
    public static final int WX_DOM_UPDATE_ATTRS = 0x01;
    public static final int WX_DOM_UPDATE_STYLE = 0x02;
    public static final int WX_DOM_ADD_DOM = 0x03;
    public static final int WX_DOM_REMOVE_DOM = 0x04;
    public static final int WX_DOM_MOVE_DOM = 0x05;
    public static final int WX_DOM_ADD_EVENT = 0x06;
    public static final int WX_DOM_REMOVE_EVENT = 0x07;
    public static final int WX_DOM_SCROLLTO = 0x08;
    public static final int WX_DOM_CREATE_FINISH = 0x09;
    public static final int WX_DOM_REFRESH_FINISH = 0x0a;

    public static final int WX_DOM_BATCH = 0xff;
  

七、WXDomManager — > DOM 管理

  /**
   * Invoke @link WXDomStatement for creating body according to the JSONObject.
   * @param instanceId
   *  @link com.taobao.weex.WXSDKInstance#mInstanceId for the instance
   * @param element the jsonObject according to which to create command object.
   */
  void createBody(String instanceId, JSONObject element) 
    if (!isDomThread()) 
      throw new WXRuntimeException
      ("Create body operation must be done in dom thread");
    
    // *** 调用DOM声明进行元素创建
    WXDomStatement statement = new WXDomStatement(instanceId, mWXRenderManager);
    mDomRegistries.put(instanceId, statement);
    statement.createBody(element);
  

八、WXDomStatement — > DOM声明

  /**
   * 创建根据JSONObject创造体命令对象,并且把命令对象添加到队列. 
   * @param element the jsonObject according to which to create command object.
   */
  void createBody(JSONObject element) 
    if (mDestroy) 
      return;
    
    WXSDKInstance instance =
     WXSDKManager.getInstance().getSDKInstance(mInstanceId);
    if (element == null) 
      if (instance != null) 
        instance.commitUTStab(WXConst.DOM_MODULE, 
        WXErrorCode.WX_ERR_DOM_CREATEBODY);
      
      return;
    

    WXDomObject domObject = parseInner(element);
    Map<String, Object> style = new HashMap<>(5);
    if (domObject.style == null || 
    !domObject.style.containsKey(WXDomPropConstant.WX_FLEXDIRECTION)) 
      style.put(WXDomPropConstant.WX_FLEXDIRECTION, "column");
    
    if (domObject.style == null || 
    !domObject.style.containsKey(WXDomPropConstant.WX_BACKGROUNDCOLOR)) 
      style.put(WXDomPropConstant.WX_BACKGROUNDCOLOR, "#ffffff");
    
    //If there is height or width in JS, then that value will override value here.
    if (domObject.style == null || 
    !domObject.style.containsKey(WXDomPropConstant.WX_WIDTH)) 
      style.put(WXDomPropConstant.WX_WIDTH, 
      WXViewUtils.getWebPxByWidth(WXViewUtils.getWeexWidth(mInstanceId)));
    
    if (domObject.style == null || 
    !domObject.style.containsKey(WXDomPropConstant.WX_HEIGHT)) 
      style.put(WXDomPropConstant.WX_HEIGHT, 
      WXViewUtils.getWebPxByWidth(WXViewUtils.getWeexHeight(mInstanceId)));
    
    domObject.ref = WXDomObject.ROOT;
    domObject.updateStyle(style);
    transformStyle(domObject, true);
    final WXComponent component = 
    mWXRenderManager.createBodyOnDomThread(mInstanceId, domObject);
    AddDomInfo addDomInfo = new AddDomInfo();
    addDomInfo.component = component;
    mAddDom.put(domObject.ref, addDomInfo);

    mNormalTasks.add(new IWXRenderTask() 

      @Override
      public void execute() 
        mWXRenderManager.createBody(mInstanceId, component);
      
    );
    mDirty = true;
    mFlushes.add(domObject.ref);

    if (instance != null) 
      instance.commitUTStab(WXConst.DOM_MODULE, WXErrorCode.WX_SUCCESS);
    
  

九、WXRenderManager —-> DOM操作

public WXComponent createBodyOnDomThread(String instanceId, WXDomObject domObject) 
        WXRenderStatement statement = mRegistries.get(instanceId);
        if (statement == null) 
            return null;
        
        return statement.createBodyOnDomThread(domObject);
    

十、WXRenderStatement —> DOM对象转换组件

    WXComponent createBodyOnDomThread(WXDomObject dom) 
        if (mWXSDKInstance == null) 
            return null;
        
        WXDomObject domObject = new WXDomObject();
        domObject.type = WXBasicComponentType.DIV;
        domObject.ref = "god";
        mGodComponent = (WXVContainer) WXComponentFactory.
        newInstance(mWXSDKInstance, domObject, null, mInstanceId);
        mGodComponent.createView(null, -1);
        if (mGodComponent == null) 
            if (WXEnvironment.isApkDebugable()) 
                WXLogUtils.e("rootView failed!");
            
            //TODO error callback
            return null;
        
        FrameLayout frameLayout = (FrameLayout) mGodComponent.getView();
        ViewGroup.LayoutParams layoutParams =
         new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        frameLayout.setLayoutParams(layoutParams);
        frameLayout.setBackgroundColor(Color.TRANSPARENT);

        WXComponent component = generateComponentTree(dom, mGodComponent);
        mGodComponent.addChild(component);
        mRegistry.put(component.getRef(), component);
        return component;
    

//  dom 对象转换 component tree
private WXComponent generateComponentTree(WXDomObject dom, WXVContainer parent) 
        if (dom == null || parent == null) 
            return null;
        
        WXComponent component = WXComponentFactory.newInstance(mWXSDKInstance, dom,
        parent, mInstanceId, parent.isLazy());
        mRegistry.put(dom.ref, component);
        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;
    

十一、WXRenderManager— > WXComponent 转换

    // 得到Wx组件进行最终创建
    public void createBody(String instanceId, WXComponent component) 
        WXRenderStatement statement = mRegistries.get(instanceId);
        if (statement == null) 
            return;
        
        statement.createBody(component);
    

十二、WXRenderStatement —> WX组件生成view

   /**
     * 创建RootView;
     * @see com.taobao.weex.dom.WXDomStatement#createBody(JSONObject)
     */
    void createBody(WXComponent component) 
        long start = System.currentTimeMillis();
        component.createView(mGodComponent, -1);
        start = System.currentTimeMillis();
        component.bind(null);

        if (component instanceof WXScroller) 
            WXScroller scroller = (WXScroller) component;
            if (scroller.getView() instanceof ScrollView) 
                mWXSDKInstance.setRootScrollView((ScrollView) scroller.getView());
            
        
        // *** 创建成果rootview进行返回 *** 
        mWXSDKInstance.setRootView(mGodComponent.getRealView());
        if (mWXSDKInstance.getRenderStrategy() != WXRenderStrategy.APPEND_ONCE) 
            mWXSDKInstance.onViewCreated(mGodComponent);
        
    

十三、WXSDKInstance —> 接受view回调给activity

    public void onViewCreated(final WXComponent component) 
    if (mRenderListener != null && mContext != null) 
      runOnUiThread(new Runnable() 

        @Override
        public void run() 
          if (mRenderListener != null && mContext != null) 
            mRootCom = component;
            mRenderListener.onViewCreated(WXSDKInstance.this, component.getView());
          
        
      );
    
  

十四、IndexActivity —> 接受view绘制UI

    @Override
    public void onViewCreated(WXSDKInstance wxsdkInstance, View view) 
        Log.d("TAG", "onViewCreated");
        if (flContainer != null) 
            flContainer.addView(view);
        
    

结束语

 整个UI绘制流程就是这样的,猜测大家一定会掌握的,希望对大家有所帮助,谢谢您的观看!!!

以上是关于Weex Android SDK源码分析之界面渲染(下)的主要内容,如果未能解决你的问题,请参考以下文章

Weex Android SDK源码分析之Module(navigator)

Weex Android SDK源码分析之Module(modal)

Weex Android SDK源码分析之Module(webview)

Weex Android SDK源码分析之Module(animation)

Weex Android SDK源码分析之Module(animation)

深入Weex系列Weex渲染流程分析