This commit is contained in:
Horis
2025-05-15 17:43:24 +08:00
parent bbfe0fd76d
commit 6ae4371e5b
8 changed files with 23 additions and 106 deletions
@@ -90,7 +90,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
* 存储异常和参数信息 * 存储异常和参数信息
*/ */
private val paramsMap by lazy { private val paramsMap by lazy {
val map = HashMap<String, String>() val map = LinkedHashMap<String, String>()
kotlin.runCatching { kotlin.runCatching {
//获取系统信息 //获取系统信息
map["MANUFACTURER"] = Build.MANUFACTURER map["MANUFACTURER"] = Build.MANUFACTURER
@@ -104,6 +104,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
e.toString() e.toString()
} }
map["packageName"] = appCtx.packageName map["packageName"] = appCtx.packageName
map["heapSize"] = Runtime.getRuntime().maxMemory().toString()
//获取app版本信息 //获取app版本信息
AppConst.appInfo.let { AppConst.appInfo.let {
map["versionName"] = it.versionName map["versionName"] = it.versionName
@@ -6,6 +6,7 @@ import io.legado.app.BuildConfig
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefInt import io.legado.app.utils.getPrefInt
import io.legado.app.utils.getPrefLong import io.legado.app.utils.getPrefLong
@@ -37,7 +38,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
var clickActionBR = appCtx.getPrefInt(PreferKey.clickActionBR, 1) var clickActionBR = appCtx.getPrefInt(PreferKey.clickActionBR, 1)
var themeMode = appCtx.getPrefString(PreferKey.themeMode, "0") var themeMode = appCtx.getPrefString(PreferKey.themeMode, "0")
var useDefaultCover = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false) 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) var recordLog = appCtx.getPrefBoolean(PreferKey.recordLog)
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
@@ -87,8 +89,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
PreferKey.useDefaultCover -> useDefaultCover = PreferKey.useDefaultCover -> useDefaultCover =
appCtx.getPrefBoolean(PreferKey.useDefaultCover, false) appCtx.getPrefBoolean(PreferKey.useDefaultCover, false)
PreferKey.optimizeRender -> optimizeRender = PreferKey.optimizeRender -> optimizeRender = CanvasRecorderFactory.isSupport
appCtx.getPrefBoolean(PreferKey.optimizeRender, false) && appCtx.getPrefBoolean(PreferKey.optimizeRender, false)
PreferKey.recordLog -> recordLog = appCtx.getPrefBoolean(PreferKey.recordLog) PreferKey.recordLog -> recordLog = appCtx.getPrefBoolean(PreferKey.recordLog)
@@ -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.ReadBookActivity
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.widget.number.NumberPickerDialog 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.dpToPx
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import io.legado.app.utils.removePref
import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.setEdgeEffectColor
class MoreConfigDialog : BasePrefDialogFragment() { class MoreConfigDialog : BasePrefDialogFragment() {
@@ -83,6 +85,10 @@ class MoreConfigDialog : BasePrefDialogFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_config_read) addPreferencesFromResource(R.xml.pref_config_read)
upPreferenceSummary(PreferKey.pageTouchSlop, slopSquare.toString()) upPreferenceSummary(PreferKey.pageTouchSlop, slopSquare.toString())
if (!CanvasRecorderFactory.isSupport) {
removePref(PreferKey.optimizeRender)
preferenceScreen.removePreferenceRecursively(PreferKey.optimizeRender)
}
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -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.LayoutProgressListener
import io.legado.app.ui.book.read.page.provider.TextPageFactory import io.legado.app.ui.book.read.page.provider.TextPageFactory
import io.legado.app.utils.activity 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.invisible
import io.legado.app.utils.longToastOnUi import io.legado.app.utils.longToastOnUi
import io.legado.app.utils.showDialogFragment import io.legado.app.utils.showDialogFragment
@@ -485,7 +484,6 @@ class ReadView(context: Context, attrs: AttributeSet) :
pageDelegate?.onDestroy() pageDelegate?.onDestroy()
curPage.cancelSelect() curPage.cancelSelect()
invalidateTextPage() invalidateTextPage()
BitmapPool.clear()
} }
/** /**
@@ -124,6 +124,7 @@ object LogUtils {
} }
append("WebViewUserAgent=").append(userAgent).append("\n") append("WebViewUserAgent=").append(userAgent).append("\n")
append("packageName=").append(appCtx.packageName).append("\n") append("packageName=").append(appCtx.packageName).append("\n")
append("heapSize=").append(Runtime.getRuntime().maxMemory()).append("\n")
//获取app版本信息 //获取app版本信息
AppConst.appInfo.let { AppConst.appInfo.let {
append("versionName=").append(it.versionName).append("\n") append("versionName=").append(it.versionName).append("\n")
@@ -7,6 +7,7 @@ object CanvasRecorderFactory {
private val atLeastApi24 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N private val atLeastApi24 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
private val atLeastApi29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q private val atLeastApi29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
val isSupport = atLeastApi24
// issue 3868 // issue 3868
fun create(locked: Boolean = false): CanvasRecorder { fun create(locked: Boolean = false): CanvasRecorder {
@@ -3,8 +3,9 @@ package io.legado.app.utils.canvasrecorder
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color 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 io.legado.app.utils.canvasrecorder.pools.CanvasPool
import splitties.init.appCtx
class CanvasRecorderImpl : BaseCanvasRecorder() { class CanvasRecorderImpl : BaseCanvasRecorder() {
@@ -19,14 +20,14 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
return return
} }
if (bitmap == null) { 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 (bitmap!!.width != width || bitmap!!.height != height) {
if (canReconfigure(width, height)) { if (bitmap!!.isMutable && canReconfigure(width, height)) {
bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888) bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888)
} else { } else {
BitmapPool.recycle(bitmap!!) bitmapPool.put(bitmap!!)
bitmap = BitmapPool.obtain(width, height) bitmap = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888)
} }
} }
} }
@@ -57,12 +58,13 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
override fun recycle() { override fun recycle() {
super.recycle() super.recycle()
val bitmap = bitmap ?: return val bitmap = bitmap ?: return
BitmapPool.recycle(bitmap) bitmapPool.put(bitmap)
this.bitmap = null this.bitmap = null
} }
companion object { companion object {
private val canvasPool = CanvasPool(2) private val canvasPool = CanvasPool(2)
private val bitmapPool = Glide.get(appCtx).bitmapPool
} }
} }
@@ -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<Bitmap> = 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
}
}
}