Android与JavaScript交互数据上限(Base64图片传输问题)

使用`webview.loadUrl` 给`JavaScript`传`Base64`格式的较大图片时,有的Android版本上不反馈任何错误信息,只是页面接收不到任何信息,传入的脚本不执行任何操作。

有的则报错,如下:

Refusing to load URL as it exceeds 2097152 characters

如下图:


2097152字节/1024/1024 = 2 兆,这说明调用 loadUrl,最多只能传2M的内容

Base64是将原文按照每3个字节一组分开,这个3字节组中的每一组将被按照位分解成4个部分,每个部分6个位,在这4个部分的每个部分高位加上20构成一个新的4字节组,新的字节组中,每个字节只有6位,能表示64个值。

如果原文不是三字节的倍数,可能多出一个字节和两个字节,分别会被转为2字节和3字节的`BASE64`编码,这时编码系统应该在形成的`BASE64`编码最后添加上填充符”=”,保证`BASE64`编码长度是4的倍数。所以在`BASE64`编码后添加的填充符”=”可能为0-2个。

2兆 / 4 * 3 = 1.5兆,所以传给`JavaScript`的图片最多不能超过1.5兆

webview.evaluateJavascript

通过下面代码进行打印,可以发现`evaluateJavascript`加载大于`5兆`的图片均正常

new Thread(() -> {
	//String data = BitmapUtil.toBase64(activity, uri);

	StringBuilder sb = new StringBuilder();
	for(int i=0;i<5 * 1048576;i++) {
		sb.append("1");
	}
	String data = sb.toString();

	webView.post(() -> {
		//webView.loadUrl("javascript:console.log('" + data + ".length')");
		//webView.evaluateJavascript("javascript:console.log('" + data + ".length')", value -> { });
	});
}).start();

但是,当“字符串常量”替换成“Base64”的图片数据时,却报错了

我就想,会不会是由于Base64的转换过程中,有某些特殊字符,导致`evaluateJavascript`无法解析

下面是图片转`Base64`的方法,`FLAGS`用的是默认的`Base64.DEFAULT`

private static final int BASE64_FLAGS = Base64.DEFAULT;

/**
 * 从指定路径获取图片并转为Base64
 */
public static String toBase64(Context context, Uri uri) {
    Bitmap bitmap = fromUri(context, uri);
    if (bitmap != null) {
        return toBase64(bitmap);
    }
    return null;
}

/**
 * 将图片转为Base64
 */
public static String toBase64(Bitmap bitmap) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Bitmap.CompressFormat format = Bitmap.CompressFormat.PNG;
    String mimeType = "image/png";
    if(bitmap.getConfig() == Bitmap.Config.RGB_565) {
        format = Bitmap.CompressFormat.JPEG;
        mimeType = "image/jpeg";
    }
    bitmap.compress(format, 100, baos);
    byte[] buffer = baos.toByteArray();
    return toBase64(buffer, mimeType);
}

/**
 * 将图片转为Base64
 */
public static String toBase64(byte[] buffer, String mimeType) {
    String str = Base64.encodeToString(buffer, BASE64_FLAGS);
    str = "data:" + mimeType + ";base64," + str;
    return str;
}

将`Base64`的前`10`位和后`10`位进行拼接,同样用`console.log`打印出来,发现正常。

再看一下打印,突然发现打印出来的`Base64`有换行符

这是因为`Base64.DEFAULT`,当字符串长度超过`76`会自动添加换行符,将`Base64`的`FLAGS`改成`Base64.NO_WRAP`,再跑一遍,可以了 ~ ~

`3.5`兆多的图片,可以被前端正常获取并显示了。

但是注意,传大图给前端要花很长的时间!!!

因为它需要将图片数据按字节做一次转换,然后前端获取之后设置到属性里,显示的时候需要再转一次(混合开发内耗严重),加载一张图片,要让用户等很久,体验很差。

因此建议的做法是,图片压缩之后传给前端,然后要上传时,直接调用`Android`接口,从本地上传

最大限制

即使使用了`webview.evaluateJavascript`也不是可以无限制的传递数据。如果数据太大,也会导致`OOM`。

java.lang.OutOfMemoryError: Failed to allocate a 207812440 byte allocation with 25165824 free bytes and 144MB until OOM, max allowed footprint 410782184, growth limit 536870912

java获取不到可用的内存上限。
网上有`activityManager.getMemoryClass()`和`runtime.totalMemory()`但是这些都是不能用来进行判断的,解决方案一般只有分片上传。

参考链接


Android 与 JS 交互数据上限(Base64图片传输问题)

发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注