1. 程式人生 > >Weex事件傳遞機制(二)

Weex事件傳遞機制(二)

Weex事件傳遞機制

1.Weex元件事件繫結過程

Weex元件的事件繫結發生在WXComponent的方法applyLayoutAndEvent上:

  public void applyLayoutAndEvent(WXComponent component) {
    if(!isLazy()) {
      if (component == null) {
        component = this;
      }
      setLayout(component.getDomObject());
      setPadding(component.getDomObject().getPadding(), component.getDomObject().getBorder());
      addEvents();

    }
  }

提取和component一一對應的mDomObj物件,設定layout和padding。最後設定事件到自己的成員變數set中:

  private void addEvents() {
    int count = mDomObj.getEvents().size();
    for (int i = 0; i < count; ++i) {
      addEvent(mDomObj.getEvents().get(i));
    }
    setActiveTouchListener();
  }

獲取mDomObj所有的事件,設定到addEvent

  public void addEvent(String
type) { if (TextUtils.isEmpty(type)) { return; } mAppendEvents.add(type); View view = getRealView(); /** * 處理1.CLICK 2.FOCUS 3.手勢 4.滑動 */ if (type.equals(Constants.Event.CLICK) && view != null) { addClickListener(mClickEventListener); } else
if ((type.equals( Constants.Event.FOCUS) || type.equals( Constants.Event.BLUR)) ) { addFocusChangeListener(new OnFocusChangeListener() { public void onFocusChange(boolean hasFocus) { Map<String, Object> params = new HashMap<>(); params.put("timeStamp", System.currentTimeMillis()); fireEvent(hasFocus ? Constants.Event.FOCUS : Constants.Event.BLUR, params); } }); } else if (view != null && needGestureDetector(type)) { if (view instanceof WXGestureObservable) { if (wxGesture == null) { wxGesture = new WXGesture(this, mContext); } mGestureType.add(type); ((WXGestureObservable) view).registerGestureListener(wxGesture); } else { WXLogUtils.e(view.getClass().getSimpleName() + " don't implement " + "WXGestureObservable, so no gesture is supported."); } } else { Scrollable scroller = getParentScroller(); if (type.equals(Constants.Event.APPEAR) && scroller != null) { scroller.bindAppearEvent(this); } if (type.equals(Constants.Event.DISAPPEAR) && scroller != null) { scroller.bindDisappearEvent(this); } } }

2.Weex元件事件觸發傳遞

2.1事件傳遞到JS 引擎

模擬普通的點選事件觸發流程:
點選對應view,事件觸發

  private OnClickListener mClickEventListener = new OnClickListener() {
    @Override
    public void onHostViewClick() {
      Map<String, Object> param= WXDataStructureUtil.newHashMapWithExpectedSize(1);
      Map<String, Object> position = WXDataStructureUtil.newHashMapWithExpectedSize(4);
      int[] location = new int[2];
      mHost.getLocationOnScreen(location);
      position.put("x", WXViewUtils.getWebPxByWidth(location[0],mInstance.getViewPortWidth()));
      position.put("y", WXViewUtils.getWebPxByWidth(location[1],mInstance.getViewPortWidth()));
      position.put("width", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutWidth(),mInstance.getViewPortWidth()));
      position.put("height", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutHeight(),mInstance.getViewPortWidth()));
      param.put(Constants.Name.POSITION, position);
      fireEvent(Constants.Event.CLICK,param);
    }
  };

將這次點選事件對應的view座標和寬高打包成param,以fireEvent()傳送出去之後。
傳遞路徑:fireEvent()->WXBridgeManager.getInstance().fireEventOnNode():

  public void fireEventOnNode(final String instanceId, final String ref,
                        final String type, final Map<String, Object> data,final Map<String, Object> domChanges) {
    /**
     *  1.第一步,addJSTask()將剛才的事件再做了一次封裝,扔到mNextTickTasks佇列去等待執行
     *  2.第二步,sendMessage()傳送CALL_JS_BATCH訊息,從mNextTickTasks取出所有事件進行處理
     */
    addJSTask(METHOD_FIRE_EVENT, instanceId, ref, type, data,domChanges);
    sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH);
  }

對應的Handler也是WXBridgeManager自身,handlerMessage()後轉到invokeCallJSBatch去處理()

  private void invokeCallJSBatch(Message message) {
    try {
      Object instanceId = message.obj;

      Object task = null;
      Stack<String> instanceStack = mNextTickTasks.getInstanceStack();
      int size = instanceStack.size();
      for (int i = size - 1; i >= 0; i--) {
        instanceId = instanceStack.get(i);
        task = mNextTickTasks.remove(instanceId);
        if (task != null && !((ArrayList) task).isEmpty()) {
          break;
        }
      }
      task = ((ArrayList) task).toArray();

      WXJSObject[] args = {new WXJSObject(WXJSObject.String, instanceId), new WXJSObject(WXJSObject.JSON, WXJsonUtils.fromObjectToJSONString(task))};

      /**
       *  起主要作用,後面會發起IWXBridge 與JS 引擎進行 jni互動 
       */
      invokeExecJS(String.valueOf(instanceId), null, METHOD_CALL_JS, args);
    } catch (Throwable e) {
    }

    // If task is not empty, loop until it is empty
    if (!mNextTickTasks.isEmpty()) {
      mJSHandler.sendEmptyMessage(WXJSBridgeMsgType.CALL_JS_BATCH);
    }
  }

迴圈取出所有需要執行的指令,一個個發起呼叫.

2.2 JS 引擎的回撥

然後等待JS 引擎處理完畢,通過IWXBridge的callNative()返回處理結果,而IWXBridge的通常例項WXBridge最後也會交給WXBridgeManager的callNative()進行處理,而WXBridgeManager會根據instanceId,也就是WxSDKInstance對應的instanceId交給對應的WxDomModule來處理:

            WXDomModule dom = getDomModule(instanceId);
            dom.callDomMethod(task);
    public void callDomMethod(JSONObject task) {
        if (task == null) {
            return;
        }
        String method = (String) task.get(WXBridgeManager.METHOD);
        JSONArray args = (JSONArray) task.get(WXBridgeManager.ARGS);
        callDomMethod(method, args);
    }

    public Object callDomMethod(String method, JSONArray args) {
        if (method == null) {
            return null;
        }
        try {
            switch (method) {
                case CREATE_BODY:
                    if (args == null) {
                        return null;
                    }
                    createBody((JSONObject) args.get(0));
                    break;
                case UPDATE_ATTRS:
                    if (args == null) {
                        return null;
                    }
                    updateAttrs((String) args.get(0), (JSONObject) args.get(1));
                    break;
                //...省略其他cases
            }

        } catch (IndexOutOfBoundsException e) {
        } catch (ClassCastException cce) {
        }
        return null;
    }

具體按照V-Dom的變化傳送對應的改變事件,這邊簡單的通過模擬點選圖片,改變一個文字的內容,所以會發指令集UPDATE_FINISH+UPDATE_ATTRS過來,WxDomModule將對應的指令發給WxDomHandler,由handler具體負責後面的渲染重繪工作。後面的處理流程跟渲染過程差不多,不再細談。