有没有办法在Android上将数组缓冲区从javascript传递到java?
我在这个案子上卡了一会儿。
我有一个Web视图,关于我在哪里有一个包含二进制数据的web应用程序。我想通过与 绑定的函数将其传递给Java Android。但是,似乎在Java中,我只能传递诸如,等等的基元类型。Android 4.4.3
float32array
array
JavascriptInterface
String
int
有没有办法给Java这个数组Buffer?
谢谢!
我在这个案子上卡了一会儿。
我有一个Web视图,关于我在哪里有一个包含二进制数据的web应用程序。我想通过与 绑定的函数将其传递给Java Android。但是,似乎在Java中,我只能传递诸如,等等的基元类型。Android 4.4.3
float32array
array
JavascriptInterface
String
int
有没有办法给Java这个数组Buffer?
谢谢!
好吧,所以在与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);
在上面的代码(来自我的旧答案)和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;
这反过来又将每个数组元素强制为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);
因此,对于一个 - 每次传递都会创建并销毁一千万个Java对象,从而在我的模拟器上产生10秒的CPU时间。1024 * 1024 * 10
Uint8Array
我们尝试的一件事是创建一个HTTP服务器,并通过.这有效 - 但最终导致大约200ms的延迟,并且还引入了令人讨厌的内存泄漏。POST
XMLHttpRequest
Android API 23 增加了对 MessageChannel
s 的支持,可以通过 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;
}
这会将结果转换为 UTF8 一次,然后再次转换为 Java 字符串。这仍然意味着数据(在本例中为10MB)被复制三次 - 但是可以在“仅”60ms内传递10MB的数据 - 这比上述数组方法所花费的10秒要合理得多。
Petka提出了使用8859编码的想法,该编码可以将单个字节转换为单个字母。不幸的是,JavaScript的TextDecoder API不支持它 - 因此可以使用Windows-1252,这是另一个1字节编码。
在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。它仍然不是很快(因为字符串被填充到16位而不是8位,然后通过UTF8,然后再次填充到UTF16)。但是,与替代方案相比,它以合理的速度运行。
在与维护此代码的相关方交谈后, 他们告诉我,它尽可能好地使用当前的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 polyfill 如果需要 : https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from