Google Maps大量标记引发OutOfMemoryError问题求助
嗨,我之前也碰到过类似的Google Maps标记内存溢出问题,太头疼了!结合你说的情况——20个标记没问题,50个就崩,根源确实大概率是每个标记都生成了独立的Bitmap,这些Bitmap堆在一起很快就把内存吃光了。下面分享几个亲测有效的解决思路:
1. 复用相同样式的Bitmap
如果多个标记用的是同一种图标样式,绝对不要每个标记都new一个Bitmap!把常用的Bitmap提前初始化好,存在全局变量或者单例里,所有同样式标记都复用同一个实例。比如:
// 提前初始化一次,全局复用 Bitmap markerBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.marker_icon); // 所有同样式标记都使用这个Bitmap实例 MarkerOptions markerOptions = new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(markerBitmap));
这样不管加多少个同样式标记,内存里只存一份Bitmap,内存占用直接大幅降低。
2. 压缩Bitmap尺寸与格式
很多时候我们的图标资源本身尺寸太大,比如本来只需要32x32的标记,却用了256x256的原图,这会浪费大量内存。可以用BitmapFactory.Options来做压缩优化:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 4; // 把宽高压缩为原来的1/4,内存占用变为原来的1/16 options.inPreferredConfig = Bitmap.Config.RGB_565; // 比默认的ARGB_8888省一半内存 Bitmap compressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.marker_icon, options);
注意inSampleSize最好设为2的幂数(1、2、4、8...),如果需要更精确的压缩,可以先decode一次获取原图尺寸,再计算合适的inSampleSize值。
3. 及时回收不再使用的Bitmap
当标记被移除、地图切换页面或者Activity销毁时,主动回收Bitmap的内存,避免内存泄漏:
// 移除标记时回收对应Bitmap if (markerBitmap != null && !markerBitmap.isRecycled()) { markerBitmap.recycle(); markerBitmap = null; }
另外在Activity的onDestroy()方法里,也要记得回收全局持有的Bitmap实例,彻底释放内存。
4. 用矢量Drawable替代Bitmap(API 21+)
如果你的App最低支持API 21及以上,直接用矢量图(VectorDrawable)作为标记图标是更优选择——矢量图不会因为缩放占用额外内存,而且文件体积小:
MarkerOptions markerOptions = new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker_vector));
如果要兼容更低版本,可以用AndroidX的AppCompatResources将矢量图转成Bitmap,但记得复用这个转换后的Bitmap实例,不要每次都重新转换。
5. 标记聚合(Marker Clustering)
如果标记数量真的很多(比如几百上千个),即使优化Bitmap也可能扛不住,这时候标记聚合是最优解。把地图上距离较近的标记合并成一个聚合标记,只有当用户放大地图到一定级别时,才显示单个标记。Google Maps官方提供了ClusterManager库,集成起来很方便:
// 初始化ClusterManager ClusterManager<MyItem> clusterManager = new ClusterManager<>(this, mMap); mMap.setOnCameraIdleListener(clusterManager); mMap.setOnMarkerClickListener(clusterManager); // 添加聚合项 List<MyItem> items = new ArrayList<>(); items.add(new MyItem(lat, lng, "Marker 1")); // ... 添加更多标记项 clusterManager.addItems(items);
这样即使有几千个标记,地图上显示的聚合标记数量也很少,内存压力瞬间缓解。
关于largeHeap的补充
你提到开启了largeHeap但没效果,其实largeHeap只是临时扩大了内存上限,并没有解决“Bitmap无节制创建”这个根本问题。而且滥用largeHeap可能会掩盖其他内存泄漏问题,还可能导致App在低内存设备上更易被系统杀死,所以不推荐作为核心解决方案。
内容的提问来源于stack exchange,提问作者Baudev

