有没有一种方法可以在Android上将JavaScript的数组缓冲区传递给java?

我被困在这个案子上了一会儿。

我有Android 4.4.3的webview,我有一个web应用程序拥有包含二进制数据的float32array 。 我想通过与JavascriptInterface绑定的函数将该array传递给Java Android。 然而,看起来像在Java中,我只能传递像Stringint等原始types…

有没有办法给Java这个arrayBuffer?

谢谢 !

Related of "有没有一种方法可以在Android上将JavaScript的数组缓冲区传递给java?"

好的,在与Google工程师聊天之后,在阅读代码之后,我得出了以下结论。

高效地传递二进制数据是不可能的

通过@JavascriptInterface在JavaScript和Java之间传递二进制数据是不可能的:

在Java方面:

 @JavascriptInterface void onBytes(byte[] bytes) { // bytes available here } 

而在JavaScript方面:

 var byteArray = new Uint8Array(buffer); var arr = new Uint8Array(byteArray.length); for(var i = 0; i < byteArray.length; i++) { arr[i] = byteArray[i]; } javaObject.onBytes(arr); 

在上面的代码(从我的旧回答)和在亚历克斯的 – 对数组执行转换是残酷的:

 case JavaType::TypeArray: if (value->IsType(base::Value::Type::DICTIONARY)) { result.l = CoerceJavaScriptDictionaryToArray( env, value, target_type, object_refs, error); } else if (value->IsType(base::Value::Type::LIST)) { result.l = CoerceJavaScriptListToArray( env, value, target_type, object_refs, error); } else { result.l = NULL; } break; 

这又将每个数组元素强制转换为一个Java对象 :

 for (jsize i = 0; i < length; ++i) { const base::Value* value_element = null_value.get(); list_value->Get(i, &value_element); jvalue element = CoerceJavaScriptValueToJavaValue( env, value_element, target_inner_type, false, object_refs, error); SetArrayElement(env, result, target_inner_type, i, element); 

因此,对于一个1024 * 1024 * 10 Uint8Array ,每一遍都会创build并销毁一千万个Java对象,从而在仿真器上产生10秒钟的CPU时间。

创build一个HTTP服务器

我们尝试的一件事是创build一个HTTP服务器,并通过XMLHttpRequest将结果POST给它。 这工作 – 但最终耗费大约200毫秒的延迟,并引入了一个讨厌的内存泄漏 。

MessageChannels很慢

Android API 23添加了对MessageChannel的支持,可以通过createWebMessageChannel()来使用,如本答案所示。 这是非常缓慢的,仍然序列化与GIN(如@JavascriptInterface方法),并引起额外的延迟。 我无法得到这个合理的performance工作。

值得一提的是,Google表示,他们相信这是前进的方向,希望通过@JavascriptInterface来推广消息渠道。

传递一个string的作品

在阅读转换代码之后,人们可以看到(Google已经证实这一点),避免许多转换的唯一方法是传递一个String值。 这只能通过:

 case JavaType::TypeString: { std::string string_result; value->GetAsString(&string_result); result.l = ConvertUTF8ToJavaString(env, string_result).Release(); break; } 

它将结果一次转换为UTF8,然后再转换成一个Javastring。 这仍然意味着数据(在这种情况下为10MB)被复制三次 – 但是可以在“仅”60ms中传递10MB数据 – 这比上述数组方法需要10秒更合理。

佩特卡提出了使用8859编码的思想,可以将单个字节转换为单个字母。 不幸的是,在JavaScript的TextDecoder API中不支持,所以可以使用另一种1字节编码的Windows-1252 。

在JavaScript方面可以做到:

 var a = new Uint8Array(1024 * 1024 * 10); // your buffer var b = a.buffer // actually windows-1252 - but called iso-8859 in TextDecoder var e = new TextDecoder("iso-8859-1"); var dec = e.decode(b); proxy.onBytes(dec); // this is in the Java side. 

然后,在Java方面:

 @JavascriptInterface public void onBytes(String dec) throws UnsupportedEncodingException byte[] bytes = dec.getBytes("windows-1252"); // work with bytes here } 

在直接序列化的1/8左右运行。 它仍然不是很快(因为string被填充到16位,而不是8位,然后通过UTF8,然后再到UTF16)。 但是,与其他select相比,它运行速度合理。

在与维护这些代码的相关方进行交stream之后,他们告诉我,这与现有的API一样好。 我被告知我是第一个要求这个(快速JavaScript到Java序列化)的人。

这很简单

初始部分

  JavaScriptInterface jsInterface = new JavaScriptInterface(this); webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(jsInterface, "JSInterface"); 

JavaScriptInterface

 public class JavaScriptInterface { private Activity activity; public JavaScriptInterface(Activity activiy) { this.activity = activiy; } @JavascriptInterface public void putData(byte[] bytes){ //do whatever } } 

Js部分

 <script> function putAnyBinaryArray(arr) { var uint8 = Uint8Array.from(arr); window.JSInterface.putData(uint8); }; </script> 

TypedArray.from填充如果需要: https : //developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from

将数据序列化为string,然后在您的应用程序中反序列化。

克隆ArrayBuffer使其工作 – 关于一个用ArrayBuffer支持的TypedArray的东西不能很好地适应Android。

如果将ArrayBuffer复制到新的TypedArray中,则可以避免昂贵的序列化开销。

在读者:

 @JavascriptInterface void onBytes(byte[] bytes) { // bytes available here } 

而在JS方面:

 var byteArray = new Uint8Array(buffer); var arr = new Uint8Array(byteArray.length); for(var i = 0; i < byteArray.length; i++) { arr[i] = byteArray[i]; } javaObject.onBytes(arr); 

工作完美:)