0

我正在尝试与 JS api 交互,一旦视频加载/加载失败/被查看,我想使用 EventSystem 发回一个事件。我得到了第一部分的工作,我称之为原生 JS api:

@JsType(namespace = JsPackage.GLOBAL, name = "ApiRequestHandler", isNative = true)
public class ApiRequestHandler {

    public native void loadVideo();
    public native void showVideo();
}
ApiRequestHandler= function () {
    var preloadedVideo = null;
}
ApiRequestHandler.prototype.loadVideo = function () {
    Api.getVideoAsync().then(function(video) {
      // Load asynchronously
      preloadedVideo = video;
      return preloadedVideo.loadAsync();
    }).then(function() {
      console.log('Video preloaded');
    }).catch(function(err){
      console.error('Video failed to preload: ' + err.message);
    });
}
ApiRequestHandler.prototype.showVideo = function () {
    preloadedVideo.showAsync()
    .then(function() {
      // Perform post-ad success operation
      console.log('Video watched successfully');
    })
    .catch(function(e) {
      console.error(e.message);
    });
}

我的问题是从 JS 调用 Java 对象,我附带了一个响应处理程序类,它依赖于 EventSystem。如何在此类中初始化 EventSystem,然后让 JS 承诺解析调用sendEvent?(为了说明清楚,上面的JS代码控制台调用,需要改为调用eventSystem,来报告成功或错误)。

@JsType(isNative = true)
public class ApiResponseHandler {
    EventSystem eventSystem;

    public void sendEvent(int msg) {
        final Event event = new Event();
        event.message = msg;
        eventSystem.dispatch(event);
    }

    @JsOverlay
    public void setEventSystem(EventSystem eventSystem) {
        this.eventSystem = eventSystem;
    }
}
4

2 回答 2

1

虽然您的答案应该可以正常工作(尽管我认为您的用法Function.call(..)不正确,请参阅我添加的评论),但另一种选择是仅对Api类型及其返回的视频进行建模。仅根据您的问题和答案中的信息,api看起来是这样的:

api

具有单个静态方法 的类getVideoAsync(),它返回 a 的承诺Video

视频

这种类型实际上从未命名,所以我在猜测名称。因此,我将其定义为用于 Java 目的的接口,因此在 JS 中不会发生类型检查。这种类型似乎支持我们感兴趣的两种方法,loadAsync()它们返回某种 Promise 和showAsync(),它也返回某种 Promise。对于这两个,我将它建模为持有一些对象,以防你以后需要更好地澄清它。


我们使用 jsinterop 对这些进行建模,让我们进行 Java 调用,看起来或多或少像您的 JS 可能是什么。我们将用于elemental2-promise能够在 Java 中执行异步 Promise 操作。Api此代码假定从全局范围访问对象(从 Java 的角度来看可能是具有单个静态方法的类)不需要额外的设置。

@JsType(namespace = JsPackage.GLOBAL, isNative = true)
public class Api {
  public native static Promise<Video> getVideoAsync();
}
@JsType(namespace = JsPackage.GLOBAL, isNative = true)
public interface Video {
  public native Promise<Object> loadAsync();
  public native Promise<Object> showAsync();
}

使用 Java 代码中定义的这些类型,不需要额外的“胶水”js 类型ApiRequestHandler及其 jsinterop 包装器 - 尽管您当然可以在上述类型上编写这样的 Java 包装器,但纯粹在 Java 中实现它而不是切换到JS 了解详情。相反,虽然我只是建模了ApiRequestHandlerWrapper,但我认为您会看到可以用其他方式编写它,包括直接向应用程序的其余部分公开 Promise 而不是事件。

(附注:Promise 和 Events 有非常不同的语义——一个 Promise 只会解决拒绝一次,而不是可能发生多次,所以在某些情况下,跟踪 Promise 本身而不是订阅会更简洁到事件。也就是说,如果你只需要知道事情发生了,你可能并不关心给定承诺的细节或生命周期。)

public class ApiRequestHandlerWrapper {
    // Same event system, you can implement that as you see fit
    private EventSystem eventSystem;
    // Instead of holding the stateful ApiRequestHandler, we'll
    // just track the state it would have tracked - the current
    // video instance, if any
    private Video preloadedVideo;

    public void loadVideo() {
        Api.getVideoAsync().then(video -> {
            // NOTE: assigning before load, as the other code did, is
            //       this a bug? Perhaps assign in the next then().
            preloadedVideo = video;
    
            // Returning a promise from a then() means we wait for it
            // automatically.
            return video.loadAsync();
        }).then(success -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
        }, fail -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
            //logging errors
        });
    }

    @Override
    public void showVideo() {
        if (preloadedVideo == null) {
            throw new IllegalStateException("No preloaded video");
        }
        preloadedVideo.showAsync().then(success -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
        }, error -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
            //logging errors
        });
    }
}
于 2022-02-21T20:00:50.890 回答
0

我提出了一个解决方案,但我不确定它是否是最佳的,所以如果有人有更好的解决方案,我会留下这个问题。

首先,一个单一方法的接口,在 JS 'world' 中作为回调函数

@JsFunction
public interface ResponseCallback {
    void call();
}

我实际上包含了另一个用于失败回调的接口,唯一的区别是该方法采用错误字符串:void call(String error)

然后 ApiRequestHandler 更改为:

@JsType(namespace = JsPackage.GLOBAL, name = "ApiRequestHandler", isNative = true)
public class ApiRequestHandler {

    public native void loadVideo(ResponseCallback success, FailCallback fail);
    public native void showVideo(ResponseCallback success, FailCallback fail);
}

我有这个对象的包装类,它只调用本机 JS 函数,但也提供回调实现(以 lambda 的形式)并引用 EventSystem,所以我可以从回调发送我的事件:

public class ApiRequestHandlerWrapper {

    private EventSystem eventSystem;
    private ApiRequestHandler apiRequestHandler = new ApiRequestHandler();

    @Override
    public void loadVideo() {
        apiRequestHandler.loadVideo(() -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
            
        }, error -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
            //logging errors
        });
    }

    @Override
    public void showVideo() {
        apiRequestHandler.showVideo(() -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
        }, error -> {
            //Handle event creation, then dispatch it
            eventSystem.dispatch(event);
            //logging errors
        });
    }
}

最后,对JS代码的改动:

ApiRequestHandler = function () {
    var preloadedVideo = null;
}
ApiRequestHandler.prototype.loadVideo = function (success, fail) {
    Api.getVideoAsync().then(function(video) {
      // Load asynchronously
      preloadedVideo = video;
      return preloadedVideo.loadAsync();
    }).then(function() {
      success.call();
    }).catch(function(err){
      fail.call(err.message);
    });
}
ApiRequestHandler.prototype.showVideo = function (success, fail) {
    preloadedVideo.showAsync()
    .then(function() {
      success.call();
    })
    .catch(function(e) {
      fail.call(err.message);
    });
}
于 2022-02-18T00:12:20.927 回答