Просмотр исходного кода

[修复] Bitmap 未正确回收导致的闪退问题

HapeLee 8 месяцев назад
Родитель
Сommit
b8003e9175

+ 9 - 7
app/src/main/java/io/legado/app/model/ImageProvider.kt

@@ -70,12 +70,9 @@ object ImageProvider {
                     removeCount++
                 }
             }
-            //错误图片不能释放,占位用,防止一直重复获取图片
-            if (oldValue != errorBitmap) {
-                oldValue.recycle()
-                //putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath")
-                //putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes")
-            }
+            // 移除 oldValue.recycle()。
+            // 在 Android 8.0+ 像素内存由 GC 自动管理,手动回收容易导致 Canvas 绘制时崩溃。
+            // 特别是在阅读页返回书架时,DisplayList 可能仍持有 Bitmap 引用。
         }
 
     }
@@ -86,7 +83,12 @@ object ImageProvider {
     }
 
     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? {

+ 1 - 5
app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.Canvas
 import android.graphics.RectF
-import android.graphics.drawable.BitmapDrawable
 import android.os.Build
 import android.util.AttributeSet
 import android.view.MotionEvent
@@ -617,13 +616,10 @@ class ReadView(context: Context, attrs: AttributeSet) :
      * 更新背景
      */
     fun upBg() {
-        val oldBg = ReadBookConfig.upBg(width, height)
+        ReadBookConfig.upBg(width, height)
         curPage.upBg()
         prevPage.upBg()
         nextPage.upBg()
-        // 所有视图背景更新完成后再 recycle 旧 bitmap,
-        // 防止视图仍持有旧 BitmapDrawable 引用时 bitmap 已被 recycle 导致崩溃
-        (oldBg as? BitmapDrawable)?.bitmap?.recycle()
     }
 
     /**

+ 17 - 9
app/src/main/java/io/legado/app/ui/book/read/page/delegate/FadePageDelegate.kt

@@ -25,6 +25,7 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
     }
 
     private fun setBitmap() {
+        if (!readView.isAttachedToWindow) return
         when (mDirection) {
             PageDirection.PREV -> {
                 prevPage.screenshot(prevRecorder)
@@ -60,7 +61,6 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
                 if (!isMoved || mDirection == PageDirection.NONE) return
                 // 超过阈值自动翻页,否则回弹
                 val shouldFlip = fadeProgress >= flipThreshold
-                if (shouldFlip) 1f else 0f
                 isCancel = !shouldFlip
                 onAnimStart(readView.defaultAnimationSpeed)
             }
@@ -97,13 +97,15 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
     }
 
     override fun onDraw(canvas: Canvas) {
-        curRecorder.draw(canvas)
+        if (!readView.isAttachedToWindow) return
+        
         if (mDirection == PageDirection.NONE) {
-            // 直接绘制 curPage,不使用 curRecorder
             curPage.draw(canvas)
             return
         }
 
+        curRecorder.draw(canvas)
+
         val alpha = (fadeProgress * 255).toInt().coerceIn(0, 255)
         val paint = Paint().apply { this.alpha = alpha }
 
@@ -143,11 +145,12 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
             readView.fillPage(mDirection)
             // 延迟更新 curRecorder,确保 curPage 已经刷新
             readView.post {
-                try {
-                    curPage.screenshot(curRecorder)
-                    readView.invalidate()
-                } catch (_: Exception) {
-                    // 捕获可能的异常,避免闪退
+                if (readView.isAttachedToWindow) {
+                    try {
+                        curPage.screenshot(curRecorder)
+                        readView.invalidate()
+                    } catch (_: Exception) {
+                    }
                 }
             }
         }
@@ -168,12 +171,17 @@ class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
         isMoved = false
         isRunning = false
         if (!scroller.isFinished) {
+            readView.isAbortAnim = true
             scroller.abortAnimation()
             if (!isCancel) {
                 readView.fillPage(mDirection)
-                curPage.screenshot(curRecorder) // 同步 curRecorder
+                if (readView.isAttachedToWindow) {
+                    curPage.screenshot(curRecorder)
+                }
                 readView.invalidate()
             }
+        } else {
+            readView.isAbortAnim = false
         }
     }
 

+ 7 - 5
app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderImpl.kt

@@ -51,15 +51,17 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
     }
 
     override fun draw(canvas: Canvas) {
-        if (bitmap == null) return
-        canvas.drawBitmap(bitmap!!, 0f, 0f, null)
+        val b = bitmap ?: return
+        if (!b.isRecycled) {
+            canvas.drawBitmap(b, 0f, 0f, null)
+        }
     }
 
     override fun recycle() {
         super.recycle()
-        val bitmap = bitmap ?: return
-        bitmapPool.put(bitmap)
-        this.bitmap = null
+        val b = bitmap ?: return
+        bitmap = null
+        bitmapPool.put(b)
     }
 
     companion object {