- 关键信息:
画布:尝试使用回收的位图android.graphics.Bitmap@eff9b3e
- 具体日志:
Fatal Exception: java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@eff9b3e
at android.graphics.BaseCanvas.throwIfCannotDraw(BaseCanvas.java:77)
at android.graphics.RecordingCanvas.throwIfCannotDraw(RecordingCanvas.java:278)
at android.graphics.BaseRecordingCanvas.drawBitmap(BaseRecordingCanvas.java:91)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:548)
at android.view.View.getDrawableRenderNode(View.java:24265)
at android.view.View.drawBackground(View.java:24170)
at android.view.View.draw(View.java:23893)
at android.view.View.updateDisplayListIfDirty(View.java:22776)
at android.view.View.draw(View.java:23631)
- 描述
该问题的本质原因就是关键信息中所说的:尝试使用一个已经被回收的bitmap
- 分析
看注释描述,能提取的有用信息是,创建的bitmap可能是同一个对象。看下面的代码,更能说明次问题。
/**
* Returns an immutable bitmap from subset of the source bitmap,
* transformed by the optional matrix. The new bitmap may be the
* same object as source, or a copy may have been made. It is
* initialized with the same density as the original bitmap.
*
* If the source bitmap is immutable and the requested subset is the
* same as the source bitmap itself, then the source bitmap is
* returned and no new bitmap is created.
*/
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter) {
//...
// check if we can just return our argument unchanged
if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
height == source.getHeight() && (m == null || m.isIdentity())) {
return source;
}
//...
return bitmap;
}
- 结果
当满足上述的两个条件之后,创建了同一个bitmap, 在调用createBitmap之后 直接调用recycle(), 那后续操作的时候肯定会出问题
- 重点场景
recyclerView 列表中,存在大量复用图片,该问题会更加明显
我代码中的一个场景
fun rotateBitmap(bitmap: Bitmap, rotation: Int): Bitmap? {
if (rotation == 0) return bitmap
val matrix = Matrix()
matrix.setRotate(rotation.toFloat())
try {
val bmRotated =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
bitmap.recycle()
return bmRotated
} catch (e: OutOfMemoryError) {
e.printStackTrace()
return null
}
}
当两次调用的bitmap 相同时,我们第一次进行createBitmap, 然后将原bitmap 销毁了,此时刚好bitmap满足上述条件,那将会爆发此问题。
- 处理方法
fun rotateBitmap(bitmap: Bitmap, rotation: Int): Bitmap? {
if (rotation == 0) return bitmap
val matrix = Matrix()
matrix.setRotate(rotation.toFloat())
try {
val bmRotated =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
if (bitmap != bmRotated) { //判断两个bitmap 不相同时进行释放
bitmap.recycle()
}
return bmRotated
} catch (e: OutOfMemoryError) {
e.printStackTrace()
return null
}
}
总结
在使用Bitmap 相关时一定要仔细,它是一个直接关系内存,关系应用是否会卡顿的指标,其次,应用目前对于位图的使用可谓是重中之重,哪一个APP没有图片,真的是丝毫不敢出错啊