Ответ 1
Хорошо, поэтому после беседы с инженерами Google и после прочтения кода я сделал следующие выводы.
Эффективность передачи двоичных данных невозможна
Невозможно эффективно передавать двоичные данные между JavaScript и Java через @JavascriptInterface:
На стороне 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);
Итак, для 1024 * 1024 * 10
Uint8Array
- десять миллионов Java-объектов создаются и уничтожаются на каждом проходе, что приводит к 10-секундному времени процессора на моем эмуляторе.
Создание HTTP-сервера
Мы попытались создать HTTP-сервер и POST
получить результат с помощью XMLHttpRequest
. Это сработало - но в итоге стоило около 200 мс латентности, а также ввело неприятную утечку памяти.
Каналы сообщений медленны
Android API 23 добавила поддержку MessageChannel
s, которую можно использовать с помощью createWebMessageChannel()
, как показано в этом ответе. Это очень медленно, все еще сериализуется с GIN (например, методом @JavascriptInterface
) и наступает дополнительная латентность. Я не смог заставить это работать с разумной производительностью.
Стоит отметить, что Google сказал, что они считают, что это путь вперед и надеется продвинуть каналы сообщений через @JavascriptInterface
в какой-то момент.
Передача строки работает
После прочтения кода преобразования - можно видеть (и это было подтверждено Google), что единственный способ избежать многих преобразований - передать значение String
. Это происходит только через:
case JavaType::TypeString: {
std::string string_result;
value->GetAsString(&string_result);
result.l = ConvertUTF8ToJavaString(env, string_result).Release();
break;
}
Что преобразует результат один раз в UTF8, а затем снова в строку Java. Это по-прежнему означает, что данные (в этом случае 10 МБ) копируются три раза, но можно передавать 10 МБ данных только в "только" 60 мс, что намного более разумно, чем 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. Мне сказали, что я первый человек, который попросит об этом (быстрая сериализация Java на Java).