使用webview.loadUrl
给JavaScript
传Base64
格式的较大图片时,有的Android版本上不反馈任何错误信息,只是页面接收不到任何信息,传入的脚本不执行任何操作。
有的则报错,如下:
1 |
Refusing to load URL as it exceeds 2097152 characters |
如下图:
2097152字节/1024/1024 = 2 兆,这说明调用 loadUrl,最多只能传2M的内容
Base64是将原文按照每3个字节一组分开,这个3字节组中的每一组将被按照位分解成4个部分,每个部分6个位,在这4个部分的每个部分高位加上2个0构成一个新的4字节组,新的字节组中,每个字节只有6位,能表示64个值。
如果原文不是三字节的倍数,可能多出一个字节和两个字节,分别会被转为2字节和3字节的BASE64
编码,这时编码系统应该在形成的BASE64
编码最后添加上填充符”=”,保证BASE64
编码长度是4的倍数。所以在BASE64
编码后添加的填充符”=”可能为0-2个。
2兆 / 4 * 3 = 1.5兆,所以传给JavaScript
的图片最多不能超过1.5兆
webview.evaluateJavascript
通过下面代码进行打印,可以发现evaluateJavascript
加载大于5兆
的图片均正常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
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
。
1 |
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()
但是这些都是不能用来进行判断的,解决方案一般只有分片上传。