JsbBridgeWrapper 基于原生反射机制的事件处理

JsbBridgeWrapper 是基于事件机制,用于 JS 层与原生层进行通信的接口。

建立于 JsbBridge 上的事件分发机制

JsbBridgeWrapper 是封装在 JsbBridge 之上的事件派发机制,相对于 JsbBridge 而言它更方便易用。开发者不需要手动去实现一套消息收发机制就可以进行多事件的触发。但它不具有多线程稳定性或者是 100% 安全。如果遇到复杂需求场景,仍然建议自己实现对应的事件派发。

JsbBridgeWrapper 接口介绍

如同之前的 jsb-bridge,它的接口被声明在 jsb.d.ts 文件中。

  1. /**
  2. * Listener for jsbBridgeWrapper's event.
  3. * It takes one argument as string which is transferred by jsbBridge.
  4. */
  5. export type OnNativeEventListener = (arg: string) => void;
  6. export namespace jsbBridgeWrapper {
  7. /** If there's no event registered, the wrapper will create one */
  8. export function addNativeEventListener(eventName: string, listener: OnNativeEventListener);
  9. /**
  10. * Dispatch the event registered on Objective-C, Java etc.
  11. * No return value in JS to tell you if it works.
  12. */
  13. export function dispatchEventToNative(eventName: string, arg?: string);
  14. /**
  15. * Remove all listeners relative.
  16. */
  17. export function removeAllListenersForEvent(eventName: string);
  18. /**
  19. * Remove the listener specified
  20. */
  21. export function removeNativeEventListener(eventName: string, listener: OnNativeEventListener);
  22. /**
  23. * Remove all events, use it carefully!
  24. */
  25. export function removeAllListeners();
  26. }

OnNativeEventListener 是实际注册的 回调(callback) 类型,为了防止因为类型不匹配导致的低级错误,因此使用显示声明该类型。addNativeEventListener 中的第二个参数即为传入的 callback。当然也可以使用匿名函数代替。代码示例如下:

  1. // 当事件 “A” 触发时, ‘this.A’ 方法会被调用
  2. jsb.jsbBridgeWrapper.addNativeEventListener("A", (usr: string) => {
  3. this.A(usr);
  4. });

注意:这里是为了防止 this 指向不明确,所以使用匿名函数封装一层作用域。

JsbBridgeWrapper 接口说明

addNativeEventListener

增加一个事件监听。

参数:

  • eventName: string 事件名称
  • listener: OnNativeEventListener 回调函数

dispatchEventToNative

派发一个事件到原生层。

参数:

  • eventName: string 事件名称
  • arg?: string 参数

removeNativeEventListener

删除事件监听。

参数:

  • eventName: string 事件名称
  • listener: OnNativeEventListener 要删除的回调函数

removeAllListeners

删除所有的事件监听。

原生平台 JsbBridgeWrapper 的实现

JsbBridgeWrapper 在不同平台有不同的实现,开发者可以通过下列方式进行查看:

  • 在 Objective-C 端,可查看 JsbBridgeWrapper.h

    1. //In Objective-C
    2. typedef void (^OnScriptEventListener)(NSString*);
    3. @interface JsbBridgeWrapper : NSObject
    4. /**
    5. * Get the instance of JsbBridgetWrapper
    6. */
    7. + (instancetype)sharedInstance;
    8. /**
    9. * Add a listener to specified event, if the event does not exist, the wrapper will create one. Concurrent listener will be ignored
    10. */
    11. - (void)addScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener;
    12. /**
    13. * Remove listener for specified event, concurrent event will be deleted. Return false only if the event does not exist
    14. */
    15. - (bool)removeScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener;
    16. /**
    17. * Remove all listener for event specified.
    18. */
    19. - (void)removeAllListenersForEvent:(NSString*)eventName;
    20. /**
    21. * Remove all event registered. Use it carefully!
    22. */
    23. - (void)removeAllListeners;
    24. /**
    25. * Dispatch the event with argument, the event should be registered in javascript, or other script language in future.
    26. */
    27. - (void)dispatchEventToScript:(NSString*)eventName arg:(NSString*)arg;
    28. /**
    29. * Dispatch the event which is registered in javascript, or other script language in future.
    30. */
    31. - (void)dispatchEventToScript:(NSString*)eventName;
    32. @end
  • 安卓可查看 JsbBridgeWrapper.java

    1. // In JAVA
    2. public class JsbBridgeWrapper {
    3. public interface OnScriptEventListener {
    4. void onScriptEvent(String arg);
    5. }
    6. /**
    7. * Add a listener to specified event, if the event does not exist, the wrapper will create one. Concurrent listener will be ignored
    8. */
    9. public void addScriptEventListener(String eventName, OnScriptEventListener listener);
    10. /**
    11. * Remove listener for specified event, concurrent event will be deleted. Return false only if the event does not exist
    12. */
    13. public boolean removeScriptEventListener(String eventName, OnScriptEventListener listener);
    14. /**
    15. * Remove all listener for event specified.
    16. */
    17. public void removeAllListenersForEvent(String eventName);
    18. /**
    19. * Remove all event registered. Use it carefully!
    20. */
    21. public void removeAllListeners() {
    22. this.eventMap.clear();
    23. }
    24. /**
    25. * Dispatch the event with argument, the event should be registered in javascript, or other script language in future.
    26. */
    27. public void dispatchEventToScript(String eventName, String arg);
    28. /**
    29. * Dispatch the event which is registered in javascript, or other script language in future.
    30. */
    31. public void dispatchEventToScript(String eventName);
    32. }
  • Huawei HarmonyOS 也可以通过 JsbBridgeWrapper.java 查看其实现方式。

使用 JsbBridgeWrapper

常见的需求如数据存放在原生层,当需要将数据取至 JS 层时,可以通过 JsbBridgeWrapper 实现。

下文通过一个示例说明,如何通过原生的回调结果改变 label 内容,当原生层的事件被触发时,将目标文本字符回传给 JS 层。

注册 JS 事件

JS 层需要首先注册一个 changeLabelContent 事件监听。

  1. public changeLabelContent(user: string): void {
  2. console.log("Hello " + user + " I'm K");
  3. this.labelForContent!.string = "Hello " + user + " ! I'm K";
  4. }
  5. jsb.jsbBridgeWrapper.addNativeEventListener("changeLabelContent", (usr: string) => {
  6. this.changeLabelContent(usr);
  7. });

当 JS 层的 changeLabelContent 事件被触发时,标签的内容会变成对应的字符串组合。接下来需要处理原生的事件注册。

原生事件注册与派发

  • 在 Objective-C 端使用下列代码:

    1. // Objective-C
    2. JsbBridgeWrapper* m = [JsbBridgeWrapper sharedInstance];
    3. OnScriptEventListener requestLabelContent = ^void(NSString* arg){
    4. JsbBridgeWrapper* m = [JsbBridgeWrapper sharedInstance];
    5. [m dispatchEventToScript:@"changeLabelContent" arg:@"Charlotte"];
    6. };
    7. [m addScriptEventListener:@"requestLabelContent" listener:requestLabelContent];
  • 在 JAVA 端使用如下代码:

    1. // JAVA
    2. JsbBridgeWrapper jbw = JsbBridgeWrapper.getInstance();
    3. jbw.addScriptEventListener("requestLabelContent", arg ->{
    4. System.out.print("@JAVA: here is the argument transport in" + arg);
    5. jbw.dispatchEventToScript("changeLabelContent","Charlotte");
    6. });

    注意:JAVA 可以通过匿名函数的方法来实现 interface 的需求,此处写法简化。

这里原生的返回值被设置成固定字符,但开发者可以根据需求实现异步亦或是延后的字符赋值,时机并非固定。简而言之,当原生收到 requestLabelContent 的事件时,原生将会反过来触发 JS 层的 changeLabelContent 的事件,并将字符作为事件触发的传参。

在场景中派发事件

最后一步,我们在场景中添加一个按钮和对应的事件。

  1. // 按钮点击事件 SAY HELLO
  2. public sayHelloBtn() {
  3. jsb.jsbBridgeWrapper.dispatchEventToNative("requestLabelContent");
  4. }

最终的效果和 JsbBridge 的测试例效果相同。点击 SAY HELLO 按钮,第一行的内容会改变为打过招呼的信息,否则即为失败。

使用 JsbBridgeWrapper 模块时,开发者不需要自己去维护多余的机制,只需要关心是否正确注册和取消注册即可。