|
|
|
@ -6,15 +6,18 @@ import android.util.Size |
|
|
|
|
import androidx.collection.LruCache |
|
|
|
|
import io.legado.app.R |
|
|
|
|
import io.legado.app.constant.AppLog.putDebug |
|
|
|
|
import io.legado.app.constant.PageAnim |
|
|
|
|
import io.legado.app.data.entities.Book |
|
|
|
|
import io.legado.app.data.entities.BookSource |
|
|
|
|
import io.legado.app.exception.NoStackTraceException |
|
|
|
|
import io.legado.app.help.BookHelp |
|
|
|
|
import io.legado.app.help.config.AppConfig |
|
|
|
|
import io.legado.app.help.coroutine.Coroutine |
|
|
|
|
import io.legado.app.model.ReadBook |
|
|
|
|
import io.legado.app.model.localBook.EpubFile |
|
|
|
|
import io.legado.app.utils.* |
|
|
|
|
import kotlinx.coroutines.Dispatchers.IO |
|
|
|
|
import kotlinx.coroutines.Dispatchers.Main |
|
|
|
|
import kotlinx.coroutines.withContext |
|
|
|
|
import splitties.init.appCtx |
|
|
|
|
import java.io.File |
|
|
|
@ -32,6 +35,7 @@ object ImageProvider { |
|
|
|
|
*/ |
|
|
|
|
private const val M = 1024 * 1024 |
|
|
|
|
val cacheSize get() = AppConfig.bitmapCacheSize * M |
|
|
|
|
var triggerRecycled = false |
|
|
|
|
val bitmapLruCache = object : LruCache<String, Bitmap>(cacheSize) { |
|
|
|
|
|
|
|
|
|
override fun sizeOf(filePath: String, bitmap: Bitmap): Int { |
|
|
|
@ -47,6 +51,7 @@ object ImageProvider { |
|
|
|
|
//错误图片不能释放,占位用,防止一直重复获取图片 |
|
|
|
|
if (oldBitmap != errorBitmap) { |
|
|
|
|
oldBitmap.recycle() |
|
|
|
|
triggerRecycled = true |
|
|
|
|
putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath") |
|
|
|
|
putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes") |
|
|
|
|
} |
|
|
|
@ -110,8 +115,9 @@ object ImageProvider { |
|
|
|
|
book: Book, |
|
|
|
|
src: String, |
|
|
|
|
width: Int, |
|
|
|
|
height: Int? = null |
|
|
|
|
): Bitmap { |
|
|
|
|
height: Int? = null, |
|
|
|
|
block: (() -> Unit)? = null |
|
|
|
|
): Bitmap? { |
|
|
|
|
//src为空白时 可能被净化替换掉了 或者规则失效 |
|
|
|
|
if (book.getUseReplaceRule() && src.isBlank()) { |
|
|
|
|
book.setUseReplaceRule(false) |
|
|
|
@ -123,6 +129,30 @@ object ImageProvider { |
|
|
|
|
//bitmapLruCache的key同一改成缓存文件的路径 |
|
|
|
|
val cacheBitmap = bitmapLruCache.get(vFile.absolutePath) |
|
|
|
|
if (cacheBitmap != null) return cacheBitmap |
|
|
|
|
if (height != null && ReadBook.pageAnim() == PageAnim.scrollPageAnim) { |
|
|
|
|
Coroutine.async { |
|
|
|
|
kotlin.runCatching { |
|
|
|
|
val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height) |
|
|
|
|
?: throw NoStackTraceException(appCtx.getString(R.string.error_decode_bitmap)) |
|
|
|
|
withContext(Main) { |
|
|
|
|
bitmapLruCache.put(vFile.absolutePath, bitmap) |
|
|
|
|
} |
|
|
|
|
}.onFailure { |
|
|
|
|
//错误图片占位,防止重复获取 |
|
|
|
|
withContext(Main) { |
|
|
|
|
bitmapLruCache.put(vFile.absolutePath, errorBitmap) |
|
|
|
|
} |
|
|
|
|
putDebug( |
|
|
|
|
"ImageProvider: decode bitmap failed. path: ${vFile.absolutePath}\n$it", |
|
|
|
|
it |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
withContext(Main) { |
|
|
|
|
block?.invoke() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
@Suppress("BlockingMethodInNonBlockingContext") |
|
|
|
|
return kotlin.runCatching { |
|
|
|
|
val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height) |
|
|
|
@ -139,4 +169,17 @@ object ImageProvider { |
|
|
|
|
}.getOrDefault(errorBitmap) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun isImageAlive(book: Book, src: String): Boolean { |
|
|
|
|
val vFile = BookHelp.getImage(book, src) |
|
|
|
|
if (!vFile.exists()) return true // 使用 errorBitmap |
|
|
|
|
val cacheBitmap = bitmapLruCache.get(vFile.absolutePath) |
|
|
|
|
return cacheBitmap != null |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun isTriggerRecycled(): Boolean { |
|
|
|
|
val tmp = triggerRecycled |
|
|
|
|
triggerRecycled = false |
|
|
|
|
return tmp |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|