[修复] Bitmap 未正确回收导致的闪退问题
This commit is contained in:
@@ -70,12 +70,9 @@ object ImageProvider {
|
|||||||
removeCount++
|
removeCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//错误图片不能释放,占位用,防止一直重复获取图片
|
// 移除 oldValue.recycle()。
|
||||||
if (oldValue != errorBitmap) {
|
// 在 Android 8.0+ 像素内存由 GC 自动管理,手动回收容易导致 Canvas 绘制时崩溃。
|
||||||
oldValue.recycle()
|
// 特别是在阅读页返回书架时,DisplayList 可能仍持有 Bitmap 引用。
|
||||||
//putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath")
|
|
||||||
//putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -86,7 +83,12 @@ object ImageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun get(key: String): Bitmap? {
|
fun get(key: String): Bitmap? {
|
||||||
return bitmapLruCache[key]
|
val bitmap = bitmapLruCache[key] ?: return null
|
||||||
|
if (bitmap.isRecycled) {
|
||||||
|
bitmapLruCache.remove(key)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(key: String): Bitmap? {
|
fun remove(key: String): Bitmap? {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
@@ -617,13 +616,10 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
|||||||
* 更新背景
|
* 更新背景
|
||||||
*/
|
*/
|
||||||
fun upBg() {
|
fun upBg() {
|
||||||
val oldBg = ReadBookConfig.upBg(width, height)
|
ReadBookConfig.upBg(width, height)
|
||||||
curPage.upBg()
|
curPage.upBg()
|
||||||
prevPage.upBg()
|
prevPage.upBg()
|
||||||
nextPage.upBg()
|
nextPage.upBg()
|
||||||
// 所有视图背景更新完成后再 recycle 旧 bitmap,
|
|
||||||
// 防止视图仍持有旧 BitmapDrawable 引用时 bitmap 已被 recycle 导致崩溃
|
|
||||||
(oldBg as? BitmapDrawable)?.bitmap?.recycle()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setBitmap() {
|
private fun setBitmap() {
|
||||||
|
if (!readView.isAttachedToWindow) return
|
||||||
when (mDirection) {
|
when (mDirection) {
|
||||||
PageDirection.PREV -> {
|
PageDirection.PREV -> {
|
||||||
prevPage.screenshot(prevRecorder)
|
prevPage.screenshot(prevRecorder)
|
||||||
@@ -60,7 +61,6 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
|
|||||||
if (!isMoved || mDirection == PageDirection.NONE) return
|
if (!isMoved || mDirection == PageDirection.NONE) return
|
||||||
// 超过阈值自动翻页,否则回弹
|
// 超过阈值自动翻页,否则回弹
|
||||||
val shouldFlip = fadeProgress >= flipThreshold
|
val shouldFlip = fadeProgress >= flipThreshold
|
||||||
if (shouldFlip) 1f else 0f
|
|
||||||
isCancel = !shouldFlip
|
isCancel = !shouldFlip
|
||||||
onAnimStart(readView.defaultAnimationSpeed)
|
onAnimStart(readView.defaultAnimationSpeed)
|
||||||
}
|
}
|
||||||
@@ -97,13 +97,15 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
curRecorder.draw(canvas)
|
if (!readView.isAttachedToWindow) return
|
||||||
|
|
||||||
if (mDirection == PageDirection.NONE) {
|
if (mDirection == PageDirection.NONE) {
|
||||||
// 直接绘制 curPage,不使用 curRecorder
|
|
||||||
curPage.draw(canvas)
|
curPage.draw(canvas)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curRecorder.draw(canvas)
|
||||||
|
|
||||||
val alpha = (fadeProgress * 255).toInt().coerceIn(0, 255)
|
val alpha = (fadeProgress * 255).toInt().coerceIn(0, 255)
|
||||||
val paint = Paint().apply { this.alpha = alpha }
|
val paint = Paint().apply { this.alpha = alpha }
|
||||||
|
|
||||||
@@ -143,11 +145,12 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
|
|||||||
readView.fillPage(mDirection)
|
readView.fillPage(mDirection)
|
||||||
// 延迟更新 curRecorder,确保 curPage 已经刷新
|
// 延迟更新 curRecorder,确保 curPage 已经刷新
|
||||||
readView.post {
|
readView.post {
|
||||||
try {
|
if (readView.isAttachedToWindow) {
|
||||||
curPage.screenshot(curRecorder)
|
try {
|
||||||
readView.invalidate()
|
curPage.screenshot(curRecorder)
|
||||||
} catch (_: Exception) {
|
readView.invalidate()
|
||||||
// 捕获可能的异常,避免闪退
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,12 +171,17 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
|
|||||||
isMoved = false
|
isMoved = false
|
||||||
isRunning = false
|
isRunning = false
|
||||||
if (!scroller.isFinished) {
|
if (!scroller.isFinished) {
|
||||||
|
readView.isAbortAnim = true
|
||||||
scroller.abortAnimation()
|
scroller.abortAnimation()
|
||||||
if (!isCancel) {
|
if (!isCancel) {
|
||||||
readView.fillPage(mDirection)
|
readView.fillPage(mDirection)
|
||||||
curPage.screenshot(curRecorder) // 同步 curRecorder
|
if (readView.isAttachedToWindow) {
|
||||||
|
curPage.screenshot(curRecorder)
|
||||||
|
}
|
||||||
readView.invalidate()
|
readView.invalidate()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
readView.isAbortAnim = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,15 +51,17 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun draw(canvas: Canvas) {
|
override fun draw(canvas: Canvas) {
|
||||||
if (bitmap == null) return
|
val b = bitmap ?: return
|
||||||
canvas.drawBitmap(bitmap!!, 0f, 0f, null)
|
if (!b.isRecycled) {
|
||||||
|
canvas.drawBitmap(b, 0f, 0f, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
val bitmap = bitmap ?: return
|
val b = bitmap ?: return
|
||||||
bitmapPool.put(bitmap)
|
bitmap = null
|
||||||
this.bitmap = null
|
bitmapPool.put(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
Reference in New Issue
Block a user