多表格复用同图致重定位/缩放,安卓重复创图引发掉帧求助
这问题我之前帮同行排查过类似的,Android上的图片资源管理确实容易踩内存泄漏的坑,尤其是反复创建Bitmap的场景!下面给你几个可行的解决方案,你可以根据自己的游戏架构来调整:
实现图片对象池复用
别每次都创建新图片,搞个对象池把不用的图片存起来,下次需要的时候直接从池子里取出来复用就行。比如用一个LinkedList<Bitmap>来维护闲置的图片对象,每次需要新图片时先检查池子里有没有可用的,有就拿出来复用,没有再创建新的;当图片不再需要时,把它放回池子里(记得清空之前的内容或者重置状态)。这样能大幅减少对象创建和销毁的开销,还能避免内存碎片。举个简单的伪代码示例:
private LinkedList<Bitmap> imagePool = new LinkedList<>(); public Bitmap getReusableImage(int width, int height) { if (!imagePool.isEmpty()) { Bitmap bitmap = imagePool.removeFirst(); // 如果尺寸不符合,可以重新调整或者丢弃 if (bitmap.getWidth() == width && bitmap.getHeight() == height) { return bitmap; } else { bitmap.recycle(); } } // 创建新图片 return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } public void recycleImage(Bitmap bitmap) { if (bitmap != null && !bitmap.isRecycled()) { imagePool.addLast(bitmap); } }主动回收Bitmap内存
Android的Bitmap在早期版本是占用Native内存的,GC不会主动及时回收这部分内存。所以当你确定某个图片不再被使用时,一定要主动调用bitmap.recycle(),然后把引用置为null,这样能让系统更快地回收内存。注意:调用recycle()之后这个Bitmap就不能再用了,一定要确保它已经从所有视图和缓存中移除。用LruCache做内存缓存
如果你需要复用的是固定的几张原图,可以用Android提供的LruCache来做内存缓存。它会自动根据你设置的最大内存限制,移除最近最少使用的图片,避免内存溢出。初始化的时候设置合理的最大内存(比如当前可用内存的1/8),然后把加载好的图片缓存起来,每次需要时先从缓存里取,取不到再加载。示例代码:
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; LruCache<String, Bitmap> imageCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 返回Bitmap占用的内存大小(KB) return bitmap.getByteCount() / 1024; } }; // 缓存图片 imageCache.put("menu_bg", menuBitmap); // 获取图片 Bitmap cachedBitmap = imageCache.get("menu_bg");排查内存泄漏
用Android Studio的Profiler工具(Memory面板)来检测内存使用情况,看看是不是有Bitmap对象一直被引用没有被回收。比如有没有静态变量持有了Bitmap,或者菜单的View没有被正确销毁导致图片一直被绑定在视图树上。可以通过Heap Dump来分析对象引用链,找到泄漏的根源。优化图片加载尺寸
如果你的原图尺寸很大,但实际显示在菜单里的尺寸很小,那完全没必要加载全尺寸的图片。用BitmapFactory.Options的inSampleSize参数来压缩图片,根据目标显示尺寸计算合适的采样率,这样每个图片占用的内存会小很多,即使偶尔创建新的也不会太影响内存。
总结一下,核心思路就是减少重复创建,复用已有资源,主动管理内存。你可以先试试对象池或者LruCache的方案,然后用Profiler监控内存变化,反复进出菜单测试帧率是否恢复正常。
内容的提问来源于stack exchange,提问作者Ignas Ražanskas

