You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android地图自定义Marker转Bitmap时偶现OutOfMemoryError求助

解决地图Marker自定义Bitmap导致的OutOfMemoryError问题

首先得说清楚,你遇到的OOM问题,根源其实不是最后缩放的100100 Bitmap,而是你在加载网络图片时直接用BitmapFactory.decodeStream加载了原图——哪怕你后面把它缩成100100,原始的大Bitmap已经在内存里占了大量空间,这才是导致OOM的关键。另外,你的Bitmap回收和缓存逻辑也有优化空间,咱们一步步来解决:

1. 先对网络图片进行采样压缩,从根源减少内存占用

不要直接加载原图,先获取图片的宽高,计算合适的采样率,再加载缩小后的Bitmap,这样一开始占用的内存就小很多。示例代码:

private Bitmap getSampledBitmapFromUrl(String imageUrl, int targetWidth, int targetHeight) throws IOException {
    URL url = new URL(imageUrl);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.connect();
    
    // 先获取图片的宽高,不加载到内存
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(connection.getInputStream(), null, options);
    
    // 计算采样率
    options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight);
    
    // 重新打开流,加载采样后的Bitmap
    connection.disconnect();
    connection = (HttpURLConnection) url.openConnection();
    options.inJustDecodeBounds = false;
    options.inPreferredConfig = Bitmap.Config.RGB_565; // 用RGB_565比ARGB_8888节省一半内存
    Bitmap bitmap = BitmapFactory.decodeStream(connection.getInputStream(), null, options);
    
    // 最后缩到目标尺寸(如果采样后的尺寸还是比目标大的话)
    return Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, true);
}

private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;
        while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

用这个方法替代你原来的BitmapFactory.decodeStream,就能避免加载超大的原始Bitmap到内存。

2. 优化Bitmap的回收与缓存

  • 用完Bitmap及时回收:当Marker被移除(比如地图视野变化、页面销毁)时,调用bitmap.recycle(),并置为null,帮助GC回收内存。
  • 用LruCache缓存Bitmap:避免重复下载和创建相同的Marker Bitmap,减少内存波动。示例:
private LruCache<String, Bitmap> mMarkerBitmapCache;

// 在初始化时设置缓存(比如Activity的onCreate)
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8; // 用1/8的内存作为缓存
mMarkerBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
        return bitmap.getByteCount() / 1024;
    }
};

// 缓存Bitmap
public void addBitmapToCache(String key, Bitmap bitmap) {
    if (getBitmapFromCache(key) == null) {
        mMarkerBitmapCache.put(key, bitmap);
    }
}

// 获取缓存的Bitmap
public Bitmap getBitmapFromCache(String key) {
    return mMarkerBitmapCache.get(key);
}

3. 优化drawableToIcon方法,避免不必要的内存开销

你的方法里用了buildDrawingCache,记得用完要清理,不然会残留内存:

public Bitmap drawableToIcon(@NonNull Context context, Bitmap image, String status) {
    View marker = LayoutInflater.from(context).inflate(R.layout.custom_marker_layout, null);
    ImageView imageView = marker.findViewById(R.id.imageView1);
    imageView.setImageBitmap(image);

    // 测量布局
    marker.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    marker.layout(0, 0, marker.getMeasuredWidth(), marker.getMeasuredHeight());

    // 创建Bitmap并绘制
    Bitmap returnedBitmap = Bitmap.createBitmap(marker.getMeasuredWidth(), marker.getMeasuredHeight(), Bitmap.Config.RGB_565);
    Canvas canvas = new Canvas(returnedBitmap);
    marker.draw(canvas);

    // 清理DrawingCache,避免内存泄漏
    marker.destroyDrawingCache();

    return returnedBitmap;
}

另外,把Bitmap.Config.ARGB_8888改成RGB_565,能节省一半内存(如果你的Marker不需要透明通道的话)。

4. 推荐用图片加载库替代自己实现(最省心的方案)

比如Glide,它自带内存缓存、自动采样、生命周期管理,能完美解决你的问题,还不用自己处理复杂的Bitmap逻辑。示例代码:

Glide.with(context)
        .asBitmap()
        .load("http://your-image-url.com/image.jpg")
        .override(100, 100) // 直接指定目标尺寸
        .into(new CustomTarget<Bitmap>() {
            @Override
            public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                // 用这个Bitmap创建Marker
                Bitmap markerBitmap = drawableToIcon(context, resource, status);
                MarkerOptions markerOptions = new MarkerOptions()
                        .position(latLng)
                        .icon(BitmapDescriptorFactory.fromBitmap(markerBitmap));
                mMap.addMarker(markerOptions);
                
                // 回收临时Bitmap
                if (!resource.isRecycled()) {
                    resource.recycle();
                }
                // 注意:如果Marker还在地图上显示,不要回收markerBitmap!可以在Marker移除时再回收
                // if (!markerBitmap.isRecycled()) { markerBitmap.recycle(); }
            }

            @Override
            public void onLoadCleared(@Nullable Drawable placeholder) {
                // 清理资源
            }
        });

Glide会自动帮你处理图片的下载、缓存、采样,还会根据页面生命周期自动释放资源,比自己写的代码稳定得多。

最后提醒一下:如果你的地图上有大量Marker,尽量复用Bitmap,避免每个Marker都创建新的Bitmap,这也是减少内存占用的关键。

内容的提问来源于stack exchange,提问作者Kalaimeow meow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:18:21