This commit is contained in:
Horis 2024-02-15 15:56:17 +08:00
parent 9d1aa1c5d6
commit cc99eb1f03
8 changed files with 5 additions and 158 deletions

View File

@ -117,7 +117,6 @@ object PreferKey {
const val welcomeShowIconDark = "welcomeShowIconDark" const val welcomeShowIconDark = "welcomeShowIconDark"
const val pageTouchSlop = "pageTouchSlop" const val pageTouchSlop = "pageTouchSlop"
const val showAddToShelfAlert = "showAddToShelfAlert" const val showAddToShelfAlert = "showAddToShelfAlert"
const val asyncLoadImage = "asyncLoadImage"
const val ignoreAudioFocus = "ignoreAudioFocus" const val ignoreAudioFocus = "ignoreAudioFocus"
const val parallelExportBook = "parallelExportBook" const val parallelExportBook = "parallelExportBook"
const val progressBarBehavior = "progressBarBehavior" const val progressBarBehavior = "progressBarBehavior"

View File

@ -460,8 +460,6 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true) val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)
val asyncLoadImage get() = appCtx.getPrefBoolean(PreferKey.asyncLoadImage, false)
val ignoreAudioFocus get() = appCtx.getPrefBoolean(PreferKey.ignoreAudioFocus, false) val ignoreAudioFocus get() = appCtx.getPrefBoolean(PreferKey.ignoreAudioFocus, false)
val onlyLatestBackup get() = appCtx.getPrefBoolean(PreferKey.onlyLatestBackup, true) val onlyLatestBackup get() = appCtx.getPrefBoolean(PreferKey.onlyLatestBackup, true)

View File

@ -6,9 +6,7 @@ import android.os.Build
import android.util.Size import android.util.Size
import androidx.collection.LruCache import androidx.collection.LruCache
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.constant.AppLog.putDebug 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.Book
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
@ -16,10 +14,8 @@ import io.legado.app.help.book.BookHelp
import io.legado.app.help.book.isEpub import io.legado.app.help.book.isEpub
import io.legado.app.help.book.isPdf import io.legado.app.help.book.isPdf
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.localBook.EpubFile import io.legado.app.model.localBook.EpubFile
import io.legado.app.model.localBook.PdfFile import io.legado.app.model.localBook.PdfFile
import io.legado.app.utils.BitmapCache
import io.legado.app.utils.BitmapUtils import io.legado.app.utils.BitmapUtils
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.SvgUtils import io.legado.app.utils.SvgUtils
@ -70,8 +66,7 @@ object ImageProvider {
) { ) {
//错误图片不能释放,占位用,防止一直重复获取图片 //错误图片不能释放,占位用,防止一直重复获取图片
if (oldBitmap != errorBitmap) { if (oldBitmap != errorBitmap) {
BitmapCache.add(oldBitmap) oldBitmap.recycle()
//oldBitmap.recycle()
//putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath") //putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath")
//putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes") //putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes")
} }
@ -160,9 +155,8 @@ object ImageProvider {
book: Book, book: Book,
src: String, src: String,
width: Int, width: Int,
height: Int? = null, height: Int? = null
block: (() -> Unit)? = null ): Bitmap {
): Bitmap? {
//src为空白时 可能被净化替换掉了 或者规则失效 //src为空白时 可能被净化替换掉了 或者规则失效
if (book.getUseReplaceRule() && src.isBlank()) { if (book.getUseReplaceRule() && src.isBlank()) {
book.setUseReplaceRule(false) book.setUseReplaceRule(false)
@ -174,32 +168,6 @@ object ImageProvider {
//bitmapLruCache的key同一改成缓存文件的路径 //bitmapLruCache的key同一改成缓存文件的路径
val cacheBitmap = getNotRecycled(vFile.absolutePath) val cacheBitmap = getNotRecycled(vFile.absolutePath)
if (cacheBitmap != null) return cacheBitmap if (cacheBitmap != null) return cacheBitmap
if (height != null && AppConfig.asyncLoadImage && ReadBook.pageAnim() == PageAnim.scrollPageAnim) {
if (asyncLoadingImages.contains(vFile.absolutePath)) {
return null
}
asyncLoadingImages.add(vFile.absolutePath)
Coroutine.async {
BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
?: SvgUtils.createBitmap(vFile.absolutePath, width, height)
?: throw NoStackTraceException(appCtx.getString(R.string.error_decode_bitmap))
}.onSuccess {
bitmapLruCache.run {
if (maxSize() < maxCacheSize && size() + it.byteCount > maxSize() && putCount() - evictionCount() < 5) {
resize(min(maxCacheSize, maxSize() + it.byteCount))
AppLog.put("图片缓存太小,自动扩增至${(maxSize() / M)}MB。")
}
}
bitmapLruCache.put(vFile.absolutePath, it)
}.onError {
//错误图片占位,防止重复获取
bitmapLruCache.put(vFile.absolutePath, errorBitmap)
}.onFinally {
asyncLoadingImages.remove(vFile.absolutePath)
block?.invoke()
}
return null
}
return kotlin.runCatching { return kotlin.runCatching {
val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height) val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
?: SvgUtils.createBitmap(vFile.absolutePath, width, height) ?: SvgUtils.createBitmap(vFile.absolutePath, width, height)
@ -214,7 +182,6 @@ object ImageProvider {
fun clear() { fun clear() {
bitmapLruCache.evictAll() bitmapLruCache.evictAll()
BitmapCache.clear()
} }
} }

View File

@ -964,9 +964,7 @@ class ReadBookActivity : BaseReadBookActivity(),
*/ */
override fun pageChanged() { override fun pageChanged() {
pageChanged = true pageChanged = true
runOnUiThread { binding.readView.onPageChange()
binding.readView.onPageChange()
}
handler.post { handler.post {
upSeekBarProgress() upSeekBarProgress()
} }

View File

@ -32,10 +32,7 @@ data class ImageColumn(
src, src,
(end - start).toInt(), (end - start).toInt(),
height.toInt() height.toInt()
) { )
textLine.invalidate()
view.invalidate()
} ?: return
val rectF = if (textLine.isImage) { val rectF = if (textLine.isImage) {
RectF(start, 0f, end, height) RectF(start, 0f, end, height)

View File

@ -1,104 +0,0 @@
package io.legado.app.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.lang.ref.SoftReference
import java.util.concurrent.ConcurrentHashMap
object BitmapCache {
private val reusableBitmaps: MutableSet<SoftReference<Bitmap>> = ConcurrentHashMap.newKeySet()
fun add(bitmap: Bitmap) {
reusableBitmaps.add(SoftReference(bitmap))
trimSize()
}
fun clear() {
if (reusableBitmaps.isEmpty()) {
return
}
val iterator = reusableBitmaps.iterator()
while (iterator.hasNext()) {
val item = iterator.next().get() ?: continue
item.recycle()
iterator.remove()
}
}
private fun trimSize() {
var byteCount = 0
val iterator = reusableBitmaps.iterator()
while (iterator.hasNext()) {
val item = iterator.next().get() ?: continue
if (byteCount > 128 * 1024 * 1024) {
item.recycle()
iterator.remove()
} else {
byteCount += item.byteCount
}
}
}
fun addInBitmapOptions(options: BitmapFactory.Options) {
// inBitmap only works with mutable bitmaps, so force the decoder to
// return mutable bitmaps.
options.inMutable = true
// Try to find a bitmap to use for inBitmap.
getBitmapFromReusableSet(options)?.also { inBitmap ->
// If a suitable bitmap has been found, set it as the value of
// inBitmap.
options.inBitmap = inBitmap
}
}
private fun getBitmapFromReusableSet(options: BitmapFactory.Options): Bitmap? {
if (reusableBitmaps.isEmpty()) {
return null
}
val iterator = reusableBitmaps.iterator()
while (iterator.hasNext()) {
val item = iterator.next().get() ?: continue
if (item.isMutable) {
// Check to see it the item can be used for inBitmap.
if (canUseForInBitmap(item, options)) {
// Remove from reusable set so it can't be used again.
iterator.remove()
return item
}
} else {
// Remove from the set if the reference has been cleared.
iterator.remove()
}
}
return null
}
private fun canUseForInBitmap(
candidate: Bitmap,
targetOptions: BitmapFactory.Options
): Boolean {
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
// the new bitmap is smaller than the reusable bitmap candidate
// allocation byte count.
val width: Int = targetOptions.outWidth / targetOptions.inSampleSize
val height: Int = targetOptions.outHeight / targetOptions.inSampleSize
val byteCount: Int = width * height * getBytesPerPixel(candidate.config)
return byteCount <= candidate.allocationByteCount
}
/**
* A helper function to return the byte usage per pixel of a bitmap based on its configuration.
*/
private fun getBytesPerPixel(config: Bitmap.Config): Int {
return when (config) {
Bitmap.Config.ARGB_8888 -> 4
Bitmap.Config.RGB_565, Bitmap.Config.ARGB_4444 -> 2
Bitmap.Config.ALPHA_8 -> 1
else -> 1
}
}
}

View File

@ -34,7 +34,6 @@ object BitmapUtils {
BitmapFactory.decodeFileDescriptor(fis.fd, null, op) BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
op.inSampleSize = calculateInSampleSize(op, width, height) op.inSampleSize = calculateInSampleSize(op, width, height)
op.inJustDecodeBounds = false op.inJustDecodeBounds = false
BitmapCache.addInBitmapOptions(op)
BitmapFactory.decodeFileDescriptor(fis.fd, null, op) BitmapFactory.decodeFileDescriptor(fis.fd, null, op)
} }
} }

View File

@ -136,13 +136,6 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:isBottomBackground="true" /> app:isBottomBackground="true" />
<io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="false"
android:key="asyncLoadImage"
android:title="@string/async_load_image"
app:iconSpaceReserved="false"
app:isBottomBackground="true" />
<io.legado.app.lib.prefs.SwitchPreference <io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:key="noAnimScrollPage" android:key="noAnimScrollPage"