Event分类

整体的Event分为三类

1、fireEvent

主要是针对某个组件,发出对应的事件(一般在组件内部用),从而触发js中的自定义事件。

  1. <text test-id="button" class="btn" @click="handleOnclick">{{btnTitle}}</text>

如上述代码中的 “@click�” 事件,对应到android 中就是通过View.OnClickListener来实现
具体的实现方案见 WxComponent#OnClickListenerImp

private class OnClickListenerImp implements 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.getInstanceViewPortWidth()));
    position.put("y", WXViewUtils.getWebPxByWidth(location[1], mInstance.getInstanceViewPortWidth()));
    position.put("width", WXViewUtils.getWebPxByWidth(getLayoutWidth(), mInstance.getInstanceViewPortWidth()));
    position.put("height", WXViewUtils.getWebPxByWidth(getLayoutHeight(), mInstance.getInstanceViewPortWidth()));
    param.put(Constants.Name.POSITION, position);
    //发射事件给js处理  
    fireEvent(Constants.Event.CLICK, param);
  }
};

2、fireModuleEvent

主要是针对某个Module处理事件,当注册了某个组件后,js中就可以像全局事件一样监听,使用

发事件->java

//WeexModule 代表已经注册的组件名字
if (mWXSDKInstance.checkModuleEventRegistered("WeexModule",this)) {
    //拼接组件携带的参数
    HashMap<String,Object> params=new HashMap<>();
    params.put("param1","param1");
    params.put("param2","param2");
    params.put("param3","param3");
    //像WeexModule中发射组件事件
    mWXSDKInstance.fireModuleEvent("WeexModule", this, params);
}

收事件->js�

weex.requireModule('WeexModule').addEventListener("WeexModule",function (e) {
  //JSON.stringify(e) 可以将传的参数解析成string
输出示例:
{"data":{"param3":"param3","param1":"param1","param2":"param2"},"module":"WeexModule","type":"WeexModule"}
})

1、通过weex.requireModule(‘WeexModule’)先获取到注册的module
2、通过addEventListener添加对某个组件的事件监听

3、fireGlobalEventCallback

全局事件,随时随地发送,只要js端监听即可。其实与module event类似,只是预先注册了而已

发事件->java

默认情况下已经在weex初始化的时候注册了名为”globalEvent”的组件

registerModule("globalEvent",WXGlobalEventModule.class);


发送:

//参数准备
Map<String,Object> params=new HashMap<String,Object>();
params.put("eventParam","value");

//event表示在js中注解监听的js全局事件名字
mWXSDKInstance.fireGlobalEventCallback(event, params);

收事件->js

//globalEvent添加对showText事件的监听,也就是上述提到的event名字
weex.requireModule("globalEvent").addEventListener("showText", function (e) {
  nativeModule.logger("globalEvent --> " + e.eventParam)
});

js处理native回调

当js需要native有callback回调时,需要在native 的方法中传参 JSCallback

@JSMethod(uiThread = true)
public void fireNativeGlobalEvent(String event, JSCallback callback){
    Map<String,Object> params=new HashMap<String,Object>();
    params.put("eventParam","value");
    //发事件
    mWXSDKInstance.fireGlobalEventCallback(event, params);
    if(null != callback){ 
        //回调事件给js
        Map<String, Object> result = new HashMap<>();
        result.put("ok", true);
        callback.invoke(result);
    }
}

�js中定义对应的function处理callback

weex.requireModule('WeexModule').fireNativeGlobalEvent("showText", function (e) {
  if (Boolean(e.ok)) {//获取上面传递的参数key=ok
    。。。
  }
})

Event分发流程

native分发

无论上述三种event的如何分发,最终都会执行到

  • WxBridgeManager#invokeCallJSBatch

  • WxBridgeManager#invokeExecJS

  • WxBridge#execJS

  • WxBridge#nativeExecJS

通用处理Android到JavaScript的调用,都位于WxBridgeManager中

* <li>{@link #createInstance(String, String, Map, String)}</li>
* <li>{@link #destroyInstance(String)}</li>
* <li>{@link #refreshInstance(String, WXRefreshData)}</li>
* <li>{@link #registerModules(Map)}</li>
* <li>{@link #registerComponents(List)}</li>
* <li>{@link #invokeCallJSBatch(Message)}</li>

�invokeCallJSBatch只是其中一种调用方式,涉及到method为

  • METHOD_CALLBACK = “callback”;

  • METHOD_FIRE_EVENT = “fireEvent”;

  • METHD_FIRE_EVENT_SYNC = “fireEventSync”;

  • METHD_COMPONENT_HOOK_SYNC = “componentHook”;

//异步处理,按照 instanceId 分组在 一个map(mNextTickTasks)
addJSTask(METHOD_CALLBACK, instanceId, callback, data, keepAlive);
//消息队列里面去处理
sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH);

js接收&处理

javeScript端的源码在 receiver.js

/**
 * Accept calls from native (event or callback).
 * 接收来之native的 event或者 callback
 * @param  {string} id
 * @param  {array} tasks list with `method` and `args`
 */
export function receiveTasks (id, tasks) {
  const document = getDoc(id)
  if (!document) {
    return new Error(`[JS Framework] Failed to receiveTasks, `
      + `instance (${id}) is not available.`)
  }
  if (Array.isArray(tasks)) {
    return tasks.map(task => {
      switch (task.method) {
        case 'callback': return callback(document, ...task.args)
        case 'fireEventSync':
        case 'fireEvent': return fireEvent(document, ...task.args)
        case 'componentHook': return componentHook(document, ...task.args)
      }
    })
  }
}

fireEvent事件最终在是 document.fireEvent 处理 ;document是用过instanceId获取的node

function fireEvent (document, nodeId, type, event, domChanges, params) {
  const el = document.getRef(nodeId)
  if (el) {
//原生js事件发送event..
    return document.fireEvent(el, type, event, domChanges, params)
  }
  return new Error(`invalid element reference "${nodeId}"`)
}

document.fireEvent就对应到了 js中只要定义了 addEventListener 的监听

document.addEventListener('eventName',function(event){

}}

Event整理&分析

所有的事件常量都定义在 Constants#Event

/**
 * 绘制view时抛异常导致失败
 * 分发事件见{@link com.taobao.weex.ui.view.WXFrameLayout#dispatchDraw(Canvas)}
 */
String LAYEROVERFLOW = "layeroverflow";
/**
 * 点击view事件
 * 分发处见{@link com.taobao.weex.ui.component.WXComponent#OnClickListenerImp}
 */
String CLICK = "click";
/**
 * focus和blur都是focus事件
 * 分发处见{@link com.taobao.weex.ui.component.WXComponent#addFocusChangeListener(WXComponent.OnFocusChangeListener)}
 */
String FOCUS = "focus";
String BLUR = "blur";

/**
 * 所有的手势事件定义都在 {@link com.taobao.weex.ui.view.gesture.WXGestureType}中,都是
 * 通过view的onTouchListener实现 {@link WXComponent#needGestureDetector(String)}
 * 手势分为:
 * LowLevelGesture: touchstart,touchmove,touchend,touchcancel
 * HighLevelGesture:swipe,longpress,panstart,panmove,panend,horizontalpan,verticalpan
 * TODO 具体的实现待分析
 */

/**
 * 目前的方式只能发生在可滚动的view中,也就是继承 {@link com.taobao.weex.ui.component.Scrollable}
 * 然后区分是何种可滚动的view,比如ScrollView,RecyclerView。然后监听滚动事件的发生,并且通过滚动的方向
 * 来计算当前的View是否{@linkplain #APPEAR}或者 {@linkplain #DISAPPEAR},具体的例子可见
 * {@link com.taobao.weex.ui.component.WXScroller#addEvent(String)} 和
 * {@link com.taobao.weex.ui.component.list.template.WXRecyclerTemplateList#addEvent(String)}
 * 其中也处理了像{@linkplain #SCROLL},{@linkplain #SCROLL_START},{@linkplain #SCROLL_END}事件
 * 个人觉得适配很麻烦。可以参考我之前在视频开发时设计的无侵入滚动监听的方式 - 监听 onLayoutChangeListener
 */
String APPEAR = "appear";
String DISAPPEAR = "disappear";
String SCROLL = "scroll";
String SCROLL_START = "scrollstart";
String SCROLL_END = "scrollend";

/**
 * 此两个事件在{@link android.app.Activity} 声明周期回调时发射的事件
 * {@link WXSDKInstance#onActivityResume()} -- viewappear
 * {@link WXSDKInstance#onActivityPause()} ()} -- viewdisappear
 */
String VIEWAPPEAR = "viewappear";
String VIEWDISAPPEAR = "viewdisappear";
� /**
       * 视频播放事件,原生是由{@link com.taobao.weex.ui.view.WXVideoView}实现
       * 用{@link com.taobao.weex.ui.component.WXVideo}包裹
       */
      String START = "start";
      String PAUSE = "pause";
      String FINISH = "finish";
      String FAIL = "fail";
      String ERROR = "error";

      /**
       * {@link com.taobao.weex.ui.view.WXWebView}使用的事件
       * 用{@link com.taobao.weex.ui.component.WXWeb}组件包裹
       */
      String RECEIVEDTITLE = "receivedtitle";
      String PAGEFINISH = "pagefinish";
      String PAGESTART = "pagestart";
      String ONMESSAGE = "message";

      /**
       * 列表中上拉,下拉,加载更多时触发的事件
       */
      String LOADMORE = "loadmore"; //加载更多操作
      String ONREFRESH = "refresh"; //刷新操作
      String ONLOADING = "loading"; //加载操作
      String ONPULLING_DOWN = "pullingdown";//下拉操作
      String ONPULLING_UP = "pullingup";//上拉操作

      /**
       * 图片加载成功后的回调,携带的参数
       * if (imageView != null && imageView instanceof Measurable) {
       *             size.put("naturalWidth", ((Measurable) imageView).getNaturalWidth());
       *             size.put("naturalHeight", ((Measurable) imageView).getNaturalHeight());
       *           } else {
       *             size.put("naturalWidth", 0);
       *             size.put("naturalHeight", 0);
       *           }
       *
       *           if (containsEvent(Constants.Event.ONLOAD)) {
       *             params.put("success", result);
       *             params.put("size", size);
       *             fireEvent(Constants.Event.ONLOAD, params);
       *           }
       */
      String ONLOAD = "load";

      /**
       * 这个设计好不合理呀,在四个地方都有使用,居然不区分
       * {@link com.taobao.weex.ui.component.WXInput}
       * {@link com.taobao.weex.ui.component.AbstractEditComponent}
       * {@link com.taobao.weex.ui.component.WXSlider}
       * {@link com.taobao.weex.ui.component.WXSwitch}
       * 每个事件发生都有对应不同的值
       */
      String INPUT = "input";
      String CHANGE = "change";

      /**
       * 点击返回键时的时间,前提是要在自己的页面中的返回逻辑中加上
       * {@link WXSDKInstance#onBackPressed()}
       */
      String CLICKBACKITEM = "clickbackitem";

      /**
       * Activity生命周期中执行onResume和onPause的时候的事件回调
       */
      String RESUME_EVENT = "WXApplicationDidBecomeActiveEvent";
      String PAUSE_EVENT = "WXApplicationWillResignActiveEvent";

      /**
       * {@link com.taobao.weex.ui.component.AbstractEditComponent}中输入完成后
       * 执行的事件 {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE}
       */
      String RETURN = "return";

      /**
       * 软件盘显示,隐藏的事件
       */
      String KEYBOARD = "keyboard";

      /**
       * RecyclerView sticky item粘黏与否的事件
       */
      String UNSTICKY = "unsticky";
      String STICKY = "sticky";

      /**
       * 初始化动画或者 layoutAnimation执行完成后的事件
       */
      String ON_TRANSITION_END = "transitionEnd";

    interface SLOT_LIFECYCLE {
      String CREATE = "create";
      String ATTACH = "attach";
      String DETACH = "detach";
      String DESTORY = "destroy";
    }

      /**
       * 事件冒泡
       */
    String STOP_PROPAGATION = "stopPropagation";
    String STOP_PROPAGATION_RAX = "stoppropagation";

      /**
       * 点击返回键的事件 优先级高于 {@link #CLICKBACKITEM}
       */
    String NATIVE_BACK = "nativeback";