有没有办法在Android上将数组缓冲区从javascript传递到java? [英] Is there a way to pass an arraybuffer from javascript to java on Android?

查看:145
本文介绍了有没有办法在Android上将数组缓冲区从javascript传递到java?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm stuck for a moment on this case.

我在 Android 4.4.3上有一个网页浏览我的网络应用程序包含 float32array ,其中包含二进制数据。我想通过绑定 JavascriptInterface 的函数将数组传递给Java Android。
但是,似乎在Java中,我只能传递原始类型,如 String int 等。 ..

I have a webview on Android 4.4.3 where I have a webapp who has float32array containing binary data. I would like to pass that array to the Java Android via a function binded with JavascriptInterface. However, it seems like in Java, I can only pass primitive types like String, int etc...

有没有办法给Java这个arrayBuffer?

Is there a way to give to Java this arrayBuffer ?

谢谢!

推荐答案

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

Ok, so following a chat with Google engineering and after reading the code I've reached the following conclusions.

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

It is impossible to pass binary data efficiently between JavaScript and Java through a @JavascriptInterface:

在Java方面:

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

在JavaScript方面:

And on the JavaScript side:

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);

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

In the code above (from my old answer) and in Alex's - the conversion performed for the array is brutal:

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 - 每次传递都会创建和销毁一千万个Java对象,导致模拟器上有10秒的CPU时间。

So, for a 1024 * 1024 * 10 Uint8Array - ten million Java objects are created and destroyed on each pass resulting in 10 seconds of CPU time on my emulator.

我们尝试的一件事是创建一个HTTP服务器并将 POST 结果输出到它通过 XMLHttpRequest 。这很有效 - 但最终耗费了大约200毫秒的延迟,并且还引入了讨厌的内存泄漏

One thing we tried was creating an HTTP server and POSTing the result to it via an XMLHttpRequest. This worked - but ended up costing about 200ms of latency and also introduced a nasty memory leak.

Android API 23增加了对 MessageChannel s,可以通过 createWebMessageChannel() ,如此答案所示。这非常慢,仍然使用GIN进行序列化(如 @JavascriptInterface 方法)并产生额外的延迟。我无法以合理的性能发挥作用。

Android API 23 added support for MessageChannels, which can be used via createWebMessageChannel() as shown in this answer. This is very slow, still serializes with GIN (like the @JavascriptInterface method) and incurs additional latency. I was not able to get this to work with reasonable performance.

值得一提的是,Google表示他们相信这是前进的方向,并希望通过<推广消息渠道code> @JavascriptInterface 在某些时候。

It is worth mentioning that Google said they believe this is the way forward and hopes to promote message channels over @JavascriptInterface at some point.

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

After reading the conversion code - one can see (and this was confirmed by Google) that the only way to avoid many conversions is to pass a String value. This only goes through:

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

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

Which converts the result once to UTF8 and then again to a Java string. This still means the data (10MB in this case) is copied three times - but it is possible to pass 10MB of data in "only" 60ms - which is a lot more reasonable than the 10 seconds the above array method takes.

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

Petka came up with the idea of using 8859 encoding which can convert a single byte to a single letter. Unfortunately it is not supported in JavaScript's TextDecoder API - so Windows-1252 which is another 1 byte encoding can be used instead.

在JavaScript方面可以做:

On the JavaScript side one can do:

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方面:

Then, in the Java side:

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

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

Which runs in about 1/8th the time of direct serialization. It's still not very fast (since the string is padded to 16 bits instead of 8, then through UTF8 and then to UTF16 again). However, it runs in reasonable speed compared to the alternative.

在与维护此代码的相关方面交谈后 - 他们告诉我它可以获得它的好处使用当前的API。我被告知我是第一个要求这个问题的人(快速JavaScript到Java序列化)。

After speaking with the relevant parties who are maintaining this code - they told me that it's as good as it can get with the current API. I was told I'm the first person to ask for this (fast JavaScript to Java serialization).

这篇关于有没有办法在Android上将数组缓冲区从javascript传递到java?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆