为TileProvider创建自定义Bitmap时如何优化内存占用?
解决Tile绘制时Bitmap引发的OutOfMemoryException优化方案
问题根源
每次调用objectImage都会创建新的Bitmap,当迭代绘制大量对象时,短时间内会生成大量内存占用高的Bitmap实例,GC无法及时回收,最终触发OOM。你考虑的Bitmap池是针对性的解决方案,同时还有更直接的优化手段。
优化方案
1. 直接在Tile画布上绘制,消除中间Bitmap(最优解)
完全跳过objectImage中创建中间Bitmap的步骤,将对象绘制逻辑直接迁移到Tile的Canvas上,彻底避免中间Bitmap的内存开销。
修改后的代码示例:
class ObjectTileProvider @Inject constructor( val application: Application, val repository: ObjectRepository ) : TileProvider { override fun getTile(x: Int, y: Int, z: Int): Tile { val objects = repository.getObjectsInTile(x, y, z) val bitmap = Bitmap.createBitmap(512, 512, Bitmap.Config.RGB_565) // 改用更省内存的配置 val canvas = Canvas(bitmap) objects.forEach { obj -> drawObjectToCanvas(obj, canvas) // 直接在Tile画布上绘制 } return Tile(z, x, y, bitmap.toByteArray()) // 补充Tile返回逻辑(原代码遗漏) } private fun drawObjectToCanvas(obj: Object, canvas: Canvas) { // 原objectImage中的绘制逻辑,直接操作传入的canvas // 比如绘制形状、文字等,无需创建新Bitmap canvas.drawRect(...) canvas.drawText(...) } }
2. 实现线程安全的Bitmap复用池
如果必须保留中间Bitmap(比如对象需要预渲染后再组合),用复用池重复利用相同尺寸的Bitmap,避免频繁创建销毁。
示例实现(基于LruCache):
class ObjectTileProvider @Inject constructor( val application: Application, val repository: ObjectRepository ) : TileProvider { // 初始化Bitmap复用池,根据内存限制设置缓存大小 private val bitmapPool = LruCache<Int, Bitmap>(calculateMaxCacheSize()) override fun getTile(x: Int, y: Int, z: Int): Tile { val objects = repository.getObjectsInTile(x, y, z) val bitmap = Bitmap.createBitmap(512, 512, Bitmap.Config.RGB_565) val canvas = Canvas(bitmap) objects.forEach { obj -> val objBitmap = getReusableBitmap(size, size) val objCanvas = Canvas(objBitmap) // 绘制对象到objBitmap drawObjectToCanvas(obj, objCanvas) // 绘制到Tile画布 canvas.drawBitmap(objBitmap, ...) // 用完放回池 recycleBitmapToPool(objBitmap) } return Tile(z, x, y, bitmap.toByteArray()) } private fun getReusableBitmap(width: Int, height: Int): Bitmap { val key = width * 1000 + height // 用尺寸作为缓存key var bitmap = bitmapPool.get(key) if (bitmap == null || bitmap.isRecycled) { bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565) bitmapPool.put(key, bitmap) } else { // 重置Bitmap内容,避免残留绘制痕迹 bitmap.eraseColor(Color.TRANSPARENT) } return bitmap } private fun recycleBitmapToPool(bitmap: Bitmap) { if (!bitmap.isRecycled) { val key = bitmap.width * 1000 + bitmap.height bitmapPool.put(key, bitmap) } } private fun calculateMaxCacheSize(): Int { // 获取应用可用内存的1/8作为缓存上限 val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt() return maxMemory / 8 } private fun drawObjectToCanvas(obj: Object, canvas: Canvas) { // 原绘制逻辑 } }
3. 优化Bitmap配置
将ARGB_8888替换为RGB_565,每个像素内存占用从4字节降到2字节,直接减少一半内存开销。如果不需要透明通道,这个优化效果显著。
4. 及时清理无用Bitmap
如果无法复用Bitmap,在使用完毕后立即调用bitmap.recycle()并置为null,主动告知GC可以回收该内存,但注意不要回收仍在被Canvas或其他组件引用的Bitmap。
内容的提问来源于stack exchange,提问作者lostintranslation
相关产品推荐
相关产品推荐

