业务需求是从相册中选择一张小于5兆的图片,然后到第二个页面进行编辑,测试过程中发现当选择大一些的图片时,程序可能会出现崩溃。
选择一张3.6兆的图片进行测试,发现内存增长到了4个G。
由于项目中使用的是GetX进行页面跳转的。一开始以为是Getx框架出现的问题,阅读源码,发现最终调用的也是flutter原生的路由。
于是尝试使用使用原生跳转传参方式
Navigator.pushNamed(
Get.context!,
'/target3',
arguments: {'imageBytes': imageBytes},
);
经过测试发现和使用Getx进行路由跳转一样会内存爆增。
跟踪Flutter源码,路由参数会封装成 RouteSettings
final RouteSettings settings = RouteSettings(
name: name,
arguments: arguments,
);
重写了toString方法,将arguments转化为了String。
@immutable
class RouteSettings {
const RouteSettings({
this.name,
this.arguments,
});
final String? name;
final Object? arguments;
@override
String toString() => '${objectRuntimeType(this, 'RouteSettings')}(${name == null ? 'none' : '"$name"'}, $arguments)';
}
在dart中,字符串模式使用UTF-16 编码,每个字符占用 2 字节,经过测试,一张3.6MB的jpg图片 转成位图之后是48.59MB,转成字符串之后是231.07MB。在路由跳转的时候还会打印参数内容,当231.07MB的字符串数据提交到日志系统时,应用就会变得非常卡顿,严重的直接造成崩溃。
如果修改toString源码,arguments不包含进去,再重新测试,就会发现可以少占用2G左右的内存。
class RouteSettings {
const RouteSettings({
this.name,
this.arguments,
});
final String? name;
final Object? arguments;
@override
String toString() => 'print test';
}
除了日志打印之外,路由跳转的过程中,还多次进行了json encode
settingsJsonable['arguments'] = jsonEncode(
settings.arguments,
toEncodable: (Object? object) => '$object',
);
每拷贝一个对象都会多231兆的内存占用,所以在路由跳转的过程中,如果携带过大的byte数据,会导致内存剧增,超出内存边界时,应用直接崩溃。