有没有办法将数据缓冲区从javascript传递到Android上的java?

you*_*upi 14 javascript java binding android

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

我有一个webview Android 4.4.3,我有一个float32array包含二进制数据的webapp .我想array通过绑定的函数将其传递给Java Android JavascriptInterface.然而,这似乎是在Java中,我只能通过原始的类型,如String,int等...

有没有办法给Java这个arrayBuffer?

谢谢 !

Ben*_*aum 18

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

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

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

在Java方面:

@JavascriptInterface
void onBytes(byte[] bytes) {
   // bytes available here
}
Run Code Online (Sandbox Code Playgroud)

在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);
Run Code Online (Sandbox Code Playgroud)

在上面的代码(来自我的旧答案)和Alex的代码中 - 为数组执行的转换是残酷的:

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;
Run Code Online (Sandbox Code Playgroud)

它反过来将每个数组元素强制转换为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);
Run Code Online (Sandbox Code Playgroud)

因此,1024 * 1024 * 10 Uint8Array每次通过时会创建和销毁一千万个Java对象,从而在我的模拟器上产生10秒的CPU时间.

创建HTTP服务器

我们尝试过的一件事是创建一个HTTP服务器并POST通过一个结果将结果发送给它XMLHttpRequest.这有效 - 但最终花费了大约200毫秒的延迟,并引入了令人讨厌的内存泄漏.

MessageChannels很慢

Android API 23增加了对MessageChannels的支持,可以通过此答案中createWebMessageChannel()显示的方式使用.这非常慢,仍然使用GIN进行序列化(如方法)并产生额外的延迟.我无法以合理的性能使其工作.@JavascriptInterface

值得一提的是,谷歌表示他们相信这是前进的方向,并希望@JavascriptInterface在某些时候推广消息渠道.

传递字符串有效

阅读转换代码后 - 人们可以看到(这已得到Google确认),避免多次转化的唯一方法是传递一个String值.这只能通过:

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

将结果一次转换为UTF8,然后再转换为Java字符串.这仍然意味着数据(在这种情况下为10MB)被复制三次 - 但是可以在"仅"60ms内传递10MB的数据 - 这比上述数组方法花费的10秒更合理.

Petka提出了使用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.
Run Code Online (Sandbox Code Playgroud)

然后,在Java方面:

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

其中大约在直接序列化的1/8时运行.它仍然不是很快(因为字符串被填充到16位而不是8位,然后再通过UTF8再到UTF16).但是,与替代方案相比,它以合理的速度运行.

在与维护此代码的相关方面交谈后 - 他们告诉我,它与当前的API一样好.有人告诉我,我是第一个要求这个问题的人(快速JavaScript到Java序列化).


mar*_*rkt 3

将数据序列化为字符串,然后在应用程序中反序列化。