Weex事件傳遞機制(二)
阿新 • • 發佈:2019-01-26
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具體負責後面的渲染重繪工作。後面的處理流程跟渲染過程差不多,不再細談。