This commit is contained in:
Horis 2024-01-25 22:08:24 +08:00
parent 5324dfcec3
commit d1064c5b4c
17 changed files with 176 additions and 4 deletions

View File

@ -144,6 +144,7 @@ object PreferKey {
const val volumeKeyPage = "volumeKeyPage" const val volumeKeyPage = "volumeKeyPage"
const val volumeKeyPageOnPlay = "volumeKeyPageOnPlay" const val volumeKeyPageOnPlay = "volumeKeyPageOnPlay"
const val mouseWheelPage = "mouseWheelPage" const val mouseWheelPage = "mouseWheelPage"
const val recordHeapDump = "recordHeapDump"
const val cPrimary = "colorPrimary" const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent" const val cAccent = "colorAccent"

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Debug
import android.os.Looper import android.os.Looper
import android.webkit.WebSettings import android.webkit.WebSettings
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
@ -12,13 +13,24 @@ import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.model.ReadAloud import io.legado.app.model.ReadAloud
import io.legado.app.utils.* import io.legado.app.utils.FileDoc
import io.legado.app.utils.FileUtils
import io.legado.app.utils.createFileIfNotExist
import io.legado.app.utils.createFolderReplace
import io.legado.app.utils.externalCache
import io.legado.app.utils.getFile
import io.legado.app.utils.longToastOnUiLegacy
import io.legado.app.utils.stackTraceStr
import io.legado.app.utils.writeText
import splitties.init.appCtx import splitties.init.appCtx
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Date
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
/** /**
* 异常管理类 * 异常管理类
@ -69,6 +81,9 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
LocalConfig.appCrash = true LocalConfig.appCrash = true
//保存日志文件 //保存日志文件
saveCrashInfo2File(ex) saveCrashInfo2File(ex)
if (ex is OutOfMemoryError && AppConfig.recordHeapDump) {
doHeapDump()
}
context.longToastOnUiLegacy(ex.stackTraceStr) context.longToastOnUiLegacy(ex.stackTraceStr)
Thread.sleep(3000) Thread.sleep(3000)
} }
@ -148,6 +163,19 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
} }
} }
/**
* 进行堆转储
*/
fun doHeapDump() {
val heapDir = appCtx
.externalCache
.getFile("heapDump")
heapDir.createFolderReplace()
val heapFile = heapDir.getFile("heap-dump-${System.currentTimeMillis()}.hprof")
val heapDumpName = heapFile.absolutePath
Debug.dumpHprofData(heapDumpName)
}
} }
} }

View File

@ -449,6 +449,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog) val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog)
val recordHeapDump get() = appCtx.getPrefBoolean(PreferKey.recordHeapDump, false)
val loadCoverOnlyWifi get() = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false) val loadCoverOnlyWifi get() = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false)
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true) val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)

View File

@ -12,6 +12,7 @@ import io.legado.app.R
import io.legado.app.constant.AppConst.appInfo import io.legado.app.constant.AppConst.appInfo
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.help.AppUpdate import io.legado.app.help.AppUpdate
import io.legado.app.help.CrashHandler
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.help.coroutine.Coroutine
import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.ui.widget.dialog.TextDialog
@ -61,6 +62,7 @@ class AboutFragment : PreferenceFragmentCompat() {
"gzGzh" -> requireContext().sendToClip(getString(R.string.legado_gzh)) "gzGzh" -> requireContext().sendToClip(getString(R.string.legado_gzh))
"crashLog" -> showDialogFragment<CrashLogsDialog>() "crashLog" -> showDialogFragment<CrashLogsDialog>()
"saveLog" -> saveLog() "saveLog" -> saveLog()
"saveHeapDump" -> saveHeapDump()
} }
return super.onPreferenceTreeClick(preference) return super.onPreferenceTreeClick(preference)
} }
@ -137,10 +139,50 @@ class AboutFragment : PreferenceFragmentCompat() {
} }
} }
} }
val heapFile = FileDoc.fromFile(File(appCtx.externalCacheDir, "heapDump")).list()
?.firstOrNull()
if (heapFile != null) {
doc.find("heapDump")?.delete()
val heapDumpDoc = doc.createFolderIfNotExist("heapDump")
heapFile.openInputStream().getOrNull()?.use { input ->
heapDumpDoc.createFileIfNotExist(heapFile.name).openOutputStream().getOrNull()
?.use {
input.copyTo(it)
}
}
}
toastOnUi("已保存至备份目录") toastOnUi("已保存至备份目录")
}.onError { }.onError {
AppLog.put("保存日志出错\n${it.localizedMessage}", it, true) AppLog.put("保存日志出错\n${it.localizedMessage}", it, true)
} }
} }
private fun saveHeapDump() {
Coroutine.async {
val backupPath = AppConfig.backupPath ?: let {
toastOnUi("未设置备份目录")
return@async
}
toastOnUi("开始保存堆转储")
CrashHandler.doHeapDump()
val heapFile = FileDoc.fromFile(File(appCtx.externalCacheDir, "heapDump")).list()
?.firstOrNull() ?: let {
toastOnUi("未找到堆转储文件")
return@async
}
val doc = FileDoc.fromUri(Uri.parse(backupPath), true)
doc.find("heapDump")?.delete()
val heapDumpDoc = doc.createFolderIfNotExist("heapDump")
heapFile.openInputStream().getOrNull()?.use { input ->
heapDumpDoc.createFileIfNotExist(heapFile.name).openOutputStream().getOrNull()
?.use {
input.copyTo(it)
}
}
toastOnUi("已保存至备份目录")
}.onError {
AppLog.put("保存堆转储失败\n${it.localizedMessage}", it)
}
}
} }

View File

@ -2,12 +2,19 @@ package io.legado.app.ui.login
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri
import android.net.http.SslError import android.net.http.SslError
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.webkit.* import android.webkit.CookieManager
import android.webkit.SslErrorHandler
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseFragment import io.legado.app.base.BaseFragment
@ -16,8 +23,10 @@ import io.legado.app.data.entities.BaseSource
import io.legado.app.databinding.FragmentWebViewLoginBinding import io.legado.app.databinding.FragmentWebViewLoginBinding
import io.legado.app.help.http.CookieStore import io.legado.app.help.http.CookieStore
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.gone
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.gone
import io.legado.app.utils.longSnackbar
import io.legado.app.utils.openUrl
import io.legado.app.utils.snackbar import io.legado.app.utils.snackbar
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
@ -89,6 +98,33 @@ class WebViewLoginFragment : BaseFragment(R.layout.fragment_web_view_login) {
super.onPageFinished(view, url) super.onPageFinished(view, url)
} }
override fun shouldOverrideUrlLoading(
view: WebView,
request: WebResourceRequest
): Boolean {
return shouldOverrideUrlLoading(request.url)
}
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION", "KotlinRedundantDiagnosticSuppress")
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
return shouldOverrideUrlLoading(Uri.parse(url))
}
private fun shouldOverrideUrlLoading(url: Uri): Boolean {
when (url.scheme) {
"http", "https" -> {
return false
}
else -> {
binding.root.longSnackbar(R.string.jump_to_another_app, R.string.confirm) {
context?.openUrl(url)
}
return true
}
}
}
@SuppressLint("WebViewClientOnReceivedSslError") @SuppressLint("WebViewClientOnReceivedSslError")
override fun onReceivedSslError( override fun onReceivedSslError(
view: WebView?, view: WebView?,

View File

@ -1141,4 +1141,7 @@
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1144,4 +1144,7 @@
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1144,4 +1144,7 @@
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1140,4 +1140,7 @@ Còn </string>
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1141,4 +1141,7 @@
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1143,4 +1143,7 @@
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1143,4 +1143,7 @@
<string name="clear_webview_data_success">清除成功3秒后自动重启应用</string> <string name="clear_webview_data_success">清除成功3秒后自动重启应用</string>
<string name="key_page_on_long_press">按键长按翻页</string> <string name="key_page_on_long_press">按键长按翻页</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -1144,4 +1144,7 @@
<string name="clear_webview_data_success">Cleared successfully, automatically restarts the application after 3 seconds</string> <string name="clear_webview_data_success">Cleared successfully, automatically restarts the application after 3 seconds</string>
<string name="key_page_on_long_press">Press and hold the key to turn the page</string> <string name="key_page_on_long_press">Press and hold the key to turn the page</string>
<string name="save_log">保存日志</string> <string name="save_log">保存日志</string>
<string name="save_heap_dump">保存堆转储</string>
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
<string name="record_heap_dump_t">记录堆转储</string>
</resources> </resources>

View File

@ -39,6 +39,11 @@
android:title="@string/save_log" android:title="@string/save_log"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legado.app.lib.prefs.Preference
android:key="saveHeapDump"
android:title="@string/save_heap_dump"
app:iconSpaceReserved="false" />
<io.legado.app.lib.prefs.Preference <io.legado.app.lib.prefs.Preference
android:key="privacyPolicy" android:key="privacyPolicy"
android:title="@string/privacy_policy" android:title="@string/privacy_policy"

View File

@ -188,6 +188,13 @@
android:title="@string/record_log" android:title="@string/record_log"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="false"
android:key="recordHeapDump"
android:summary="@string/record_heap_dump_s"
android:title="@string/record_heap_dump_t"
app:iconSpaceReserved="false" />
</io.legado.app.lib.prefs.PreferenceCategory> </io.legado.app.lib.prefs.PreferenceCategory>

View File

@ -3,6 +3,8 @@
*/ */
package com.script package com.script
import org.mozilla.javascript.Scriptable
abstract class CompiledScript { abstract class CompiledScript {
abstract fun getEngine(): ScriptEngine abstract fun getEngine(): ScriptEngine
@ -10,6 +12,9 @@ abstract class CompiledScript {
@Throws(ScriptException::class) @Throws(ScriptException::class)
abstract fun eval(context: ScriptContext): Any? abstract fun eval(context: ScriptContext): Any?
@Throws(ScriptException::class)
abstract fun eval(scope: Scriptable): Any?
@Throws(ScriptException::class) @Throws(ScriptException::class)
fun eval(bindings: Bindings?): Any? { fun eval(bindings: Bindings?): Any? {
var ctxt = getEngine().context var ctxt = getEngine().context

View File

@ -69,4 +69,26 @@ internal class RhinoCompiledScript(
return result return result
} }
override fun eval(scope: Scriptable): Any? {
val cx = Context.enter()
val result: Any?
try {
val ret = script.exec(cx, scope)
result = engine.unwrapReturnValue(ret)
} catch (re: RhinoException) {
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
val msg: String = if (re is JavaScriptException) {
re.value.toString()
} else {
re.toString()
}
val se = ScriptException(msg, re.sourceName(), line)
se.initCause(re)
throw se
} finally {
Context.exit()
}
return result
}
} }