我正在尝试通过 JSNI 调用 parallels.js。 Parallels 围绕 Web Workers 提供了一个很好的 API,我编写了一些轻量级包装器代码,为 GWT 的 Workers 提供了比 Elemental 更方便的界面。但是我遇到了一个让我困惑的错误:
com.google.gwt.core.client.JavaScriptException: (DataCloneError) @io.mywrapper.workers.Parallel::runParallel([Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)([Java 对象: [Ljava.lang.String;@1922352522, JavaScript 对象(3006), JavaScript 对象(3008)]): 无法克隆对象。
这来自托管模式:
在 com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:249) 在 com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136) 在 com.google。 gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:571) 位于 com.google.gwt.dev.shell.ModuleSpace.invokeNativeVoid(ModuleSpace.java:299) 位于 com.google.gwt.dev.shell.JavaScriptHost。在 io.mywrapper.workers.Parallel.runParallel(Parallel.java) 处调用NativeVoid(JavaScriptHost.java:107)
这是我的代码:
创建工作线程的客户端调用示例:
Workers.spawnWorker(new String[]{"hello"}, new Worker() {
@Override
public String[] work(String[] data) {
return data;
}
@Override
public void done(String[] data) {
int i = data.length;
}
});
提供通用接口(interface)的API:
public class Workers {
public static void spawnWorker(String[] data, Worker worker) {
Parallel.runParallel(data, workFunction(worker), callbackFunction(worker));
}
/**
* Create a reference to the work function.
*/
public static native JavaScriptObject workFunction(Worker worker) /*-{
return worker == null ? null : $entry(function(x) {
<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9cebf3eef7f9eeb2dcf5f3b2f1e5ebeefdececf9eeb2ebf3eef7f9eeefb2cbf3eef7f9ee" rel="noreferrer noopener nofollow">[email protected]</a>::work([Ljava/lang/String;)(x);
});
}-*/;
/**
* Create a reference to the done function.
*/
public static native JavaScriptObject callbackFunction(Worker worker) /*-{
return worker == null ? null : $entry(function(x) {
<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e3948c91888691cda38a8ccd8e9a94918293938691cd948c9188869190cdb48c91888691" rel="noreferrer noopener nofollow">[email protected]</a>::done([Ljava/lang/String;)(x);
});
}-*/;
}
worker :
public interface Worker extends Serializable {
/**
* Called to perform the work.
* @param data
* @return
*/
public String[] work(String[] data);
/**
* Called with the result of the work.
* @param data
*/
public void done(String[] data);
}
最后是 Parallels 包装器:
public class Parallel {
/**
* @param data Data to be passed to the function
* @param work Function to perform the work, given the data
* @param callback Function to be called with result
* @return
*/
public static native void runParallel(String[] data, JavaScriptObject work, JavaScriptObject callback) /*-{
var p = new $wnd.Parallel(data);
p.spawn(work).then(callback);
}-*/;
}
这是什么原因造成的?
JSNI 文档说,关于数组:
opaque value that can only be passed back into Java code
这非常简洁,但最终我的数组被传递回 Java 代码,所以我认为这些没问题。
编辑 - 好吧,错误的假设。尽管表面上只是将数组传递回 Java 代码,但这些数组却导致了错误(这很奇怪,因为在 Google 上几乎没有关于 DataCloneError 的信息。)将它们更改为 String 是可行的;但是,String 不足以满足我的需求。看起来对象面临着与数组相同的问题;我在另一个 StackOverflow 线程中看到 Thomas 对 JSArrayUtils 的引用,但我不知道如何使用字符串数组调用它(它需要一个 JavaScriptObjects 数组作为非原始类型的输入,这对我没有好处。)有什么巧妙的方法可以解决这个问题吗?
编辑 2 - 在我使用 String[] 的地方更改为使用 JSArrayString。新问题;这次没有堆栈跟踪,但在控制台中出现错误:Uncaught ReferenceError:未定义 __gwt_makeJavaInvoke。当我单击开发人员工具中生成的脚本的 url 时,我得到以下代码片段:
self.onmessage = function(e) {self.postMessage((function (){
try {
return __gwt_makeJavaInvoke(3)(null, 65626, jsFunction, this, arguments);
}
catch (e) {
throw e;
}
})(e.data))}
我看到 _gwt_makeJavaInvoke 是 JSNI 类的一部分;那么为什么找不到呢?
最佳答案
您可以在这里找到 GWT 和 WebWorkers 的工作示例:https://github.com/tomekziel/gwtwwlinker/
这是一项初步工作,但使用此模式,我能够使用 AutoBeanFactory 提供的序列化将 GWT 对象传递给 Webworker 或从 Webworker 传递 GWT 对象。
关于GWT/JSNI - "DataCloneError - An object could not be cloned"- 如何调试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21538047/