diff --git a/app/src/main/java/io/legado/app/help/CrashHandler.kt b/app/src/main/java/io/legado/app/help/CrashHandler.kt index 9b1ab9b4f..f431be0a2 100644 --- a/app/src/main/java/io/legado/app/help/CrashHandler.kt +++ b/app/src/main/java/io/legado/app/help/CrashHandler.kt @@ -90,7 +90,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler { * 存储异常和参数信息 */ private val paramsMap by lazy { - val map = HashMap() + val map = LinkedHashMap() kotlin.runCatching { //获取系统信息 map["MANUFACTURER"] = Build.MANUFACTURER @@ -104,6 +104,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler { e.toString() } map["packageName"] = appCtx.packageName + map["heapSize"] = Runtime.getRuntime().maxMemory().toString() //获取app版本信息 AppConst.appInfo.let { map["versionName"] = it.versionName diff --git a/app/src/main/java/io/legado/app/help/config/AppConfig.kt b/app/src/main/java/io/legado/app/help/config/AppConfig.kt index fd0d151bf..35267b8c5 100644 --- a/app/src/main/java/io/legado/app/help/config/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/AppConfig.kt @@ -6,6 +6,7 @@ import io.legado.app.BuildConfig import io.legado.app.constant.AppConst import io.legado.app.constant.PreferKey import io.legado.app.data.appDb +import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefInt import io.legado.app.utils.getPrefLong @@ -37,7 +38,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { var clickActionBR = appCtx.getPrefInt(PreferKey.clickActionBR, 1) var themeMode = appCtx.getPrefString(PreferKey.themeMode, "0") var useDefaultCover = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false) - var optimizeRender = appCtx.getPrefBoolean(PreferKey.optimizeRender, false) + var optimizeRender = CanvasRecorderFactory.isSupport + && appCtx.getPrefBoolean(PreferKey.optimizeRender, false) var recordLog = appCtx.getPrefBoolean(PreferKey.recordLog) override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { @@ -87,8 +89,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { PreferKey.useDefaultCover -> useDefaultCover = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false) - PreferKey.optimizeRender -> optimizeRender = - appCtx.getPrefBoolean(PreferKey.optimizeRender, false) + PreferKey.optimizeRender -> optimizeRender = CanvasRecorderFactory.isSupport + && appCtx.getPrefBoolean(PreferKey.optimizeRender, false) PreferKey.recordLog -> recordLog = appCtx.getPrefBoolean(PreferKey.recordLog) diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt index 9f00cb263..61f63b7c5 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt @@ -25,9 +25,11 @@ import io.legado.app.model.ReadBook import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.widget.number.NumberPickerDialog +import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory import io.legado.app.utils.dpToPx import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.postEvent +import io.legado.app.utils.removePref import io.legado.app.utils.setEdgeEffectColor class MoreConfigDialog : BasePrefDialogFragment() { @@ -83,6 +85,10 @@ class MoreConfigDialog : BasePrefDialogFragment() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_config_read) upPreferenceSummary(PreferKey.pageTouchSlop, slopSquare.toString()) + if (!CanvasRecorderFactory.isSupport) { + removePref(PreferKey.optimizeRender) + preferenceScreen.removePreferenceRecursively(PreferKey.optimizeRender) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt index 306270a27..ea8ca44d5 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt @@ -35,7 +35,6 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.LayoutProgressListener import io.legado.app.ui.book.read.page.provider.TextPageFactory import io.legado.app.utils.activity -import io.legado.app.utils.canvasrecorder.pools.BitmapPool import io.legado.app.utils.invisible import io.legado.app.utils.longToastOnUi import io.legado.app.utils.showDialogFragment @@ -485,7 +484,6 @@ class ReadView(context: Context, attrs: AttributeSet) : pageDelegate?.onDestroy() curPage.cancelSelect() invalidateTextPage() - BitmapPool.clear() } /** diff --git a/app/src/main/java/io/legado/app/utils/LogUtils.kt b/app/src/main/java/io/legado/app/utils/LogUtils.kt index cf1a1ca88..2f04c259b 100644 --- a/app/src/main/java/io/legado/app/utils/LogUtils.kt +++ b/app/src/main/java/io/legado/app/utils/LogUtils.kt @@ -124,6 +124,7 @@ object LogUtils { } append("WebViewUserAgent=").append(userAgent).append("\n") append("packageName=").append(appCtx.packageName).append("\n") + append("heapSize=").append(Runtime.getRuntime().maxMemory()).append("\n") //获取app版本信息 AppConst.appInfo.let { append("versionName=").append(it.versionName).append("\n") diff --git a/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt b/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt index 7c7479479..f6c550372 100644 --- a/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt +++ b/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderFactory.kt @@ -7,6 +7,7 @@ object CanvasRecorderFactory { private val atLeastApi24 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N private val atLeastApi29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + val isSupport = atLeastApi24 // issue 3868 fun create(locked: Boolean = false): CanvasRecorder { diff --git a/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderImpl.kt b/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderImpl.kt index 3bc296818..08bcd9ecd 100644 --- a/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderImpl.kt +++ b/app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderImpl.kt @@ -3,8 +3,9 @@ package io.legado.app.utils.canvasrecorder import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color -import io.legado.app.utils.canvasrecorder.pools.BitmapPool +import com.bumptech.glide.Glide import io.legado.app.utils.canvasrecorder.pools.CanvasPool +import splitties.init.appCtx class CanvasRecorderImpl : BaseCanvasRecorder() { @@ -19,14 +20,14 @@ class CanvasRecorderImpl : BaseCanvasRecorder() { return } if (bitmap == null) { - bitmap = BitmapPool.obtain(width, height) + bitmap = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888) } if (bitmap!!.width != width || bitmap!!.height != height) { - if (canReconfigure(width, height)) { + if (bitmap!!.isMutable && canReconfigure(width, height)) { bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888) } else { - BitmapPool.recycle(bitmap!!) - bitmap = BitmapPool.obtain(width, height) + bitmapPool.put(bitmap!!) + bitmap = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888) } } } @@ -57,12 +58,13 @@ class CanvasRecorderImpl : BaseCanvasRecorder() { override fun recycle() { super.recycle() val bitmap = bitmap ?: return - BitmapPool.recycle(bitmap) + bitmapPool.put(bitmap) this.bitmap = null } companion object { private val canvasPool = CanvasPool(2) + private val bitmapPool = Glide.get(appCtx).bitmapPool } } diff --git a/app/src/main/java/io/legado/app/utils/canvasrecorder/pools/BitmapPool.kt b/app/src/main/java/io/legado/app/utils/canvasrecorder/pools/BitmapPool.kt deleted file mode 100644 index 33d6e3be5..000000000 --- a/app/src/main/java/io/legado/app/utils/canvasrecorder/pools/BitmapPool.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.legado.app.utils.canvasrecorder.pools - -import android.graphics.Bitmap -import io.legado.app.help.globalExecutor -import java.util.concurrent.ConcurrentHashMap - -object BitmapPool { - - private val reusableBitmaps: MutableSet = ConcurrentHashMap.newKeySet() - - fun recycle(bitmap: Bitmap) { - reusableBitmaps.add(bitmap) - trimSize() - } - - fun clear() { - if (reusableBitmaps.isEmpty()) { - return - } - globalExecutor.execute { - val iterator = reusableBitmaps.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - iterator.remove() - item.recycle() - } - } - } - - private fun trimSize() { - globalExecutor.execute { - var byteCount = 0 - val iterator = reusableBitmaps.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - if (byteCount > 64 * 1024 * 1024) { - iterator.remove() - item.recycle() - } else { - byteCount += item.byteCount - } - } - } - } - - fun obtain(width: Int, height: Int): Bitmap { - if (reusableBitmaps.isEmpty()) { - return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - } - val iterator = reusableBitmaps.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - if (item.isMutable) { - // Check to see it the item can be used for inBitmap. - if (canReconfigure(item, width, height)) { - // Remove from reusable set so it can't be used again. - iterator.remove() - item.reconfigure(width, height, Bitmap.Config.ARGB_8888) - return item - } - } else { - // Remove from the set if the reference has been cleared. - iterator.remove() - } - } - return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - } - - private fun canReconfigure( - candidate: Bitmap, - width: Int, - height: Int - ): 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 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 - } - } - -}