[优化] 新增崩溃包报告页面(因为崩溃太多了孩子们
This commit is contained in:
8 files changed
+206
-5
No files matched your search
@@ -222,6 +222,11 @@
|
|||||||
android:name=".ui.about.AboutActivity"
|
android:name=".ui.about.AboutActivity"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:launchMode="singleTask" />
|
android:launchMode="singleTask" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.about.CrashReportActivity"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask" />
|
||||||
<!-- 书源管理 -->
|
<!-- 书源管理 -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.book.source.manage.BookSourceActivity"
|
android:name=".ui.book.source.manage.BookSourceActivity"
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package io.legado.app.help
|
package io.legado.app.help
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
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.Debug
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.Process
|
||||||
import android.webkit.WebSettings
|
import android.webkit.WebSettings
|
||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
@@ -28,6 +30,7 @@ import java.io.StringWriter
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异常管理类
|
* 异常管理类
|
||||||
@@ -53,10 +56,14 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
Looper.loop()
|
Looper.loop()
|
||||||
} else {
|
} else {
|
||||||
ReadAloud.stop(context)
|
ReadAloud.stop(context)
|
||||||
handleException(ex)
|
if (handleException(ex)) {
|
||||||
|
Process.killProcess(Process.myPid())
|
||||||
|
exitProcess(10)
|
||||||
|
} else {
|
||||||
mDefaultHandler?.uncaughtException(thread, ex)
|
mDefaultHandler?.uncaughtException(thread, ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun shouldAbsorb(e: Throwable): Boolean {
|
private fun shouldAbsorb(e: Throwable): Boolean {
|
||||||
return when {
|
return when {
|
||||||
@@ -73,19 +80,39 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
/**
|
/**
|
||||||
* 处理该异常
|
* 处理该异常
|
||||||
*/
|
*/
|
||||||
private fun handleException(ex: Throwable?) {
|
private fun handleException(ex: Throwable?): Boolean {
|
||||||
if (ex == null) return
|
if (ex == null) return false
|
||||||
LocalConfig.appCrash = true
|
LocalConfig.appCrash = true
|
||||||
//保存日志文件
|
//保存日志文件
|
||||||
saveCrashInfo2File(ex)
|
val crashFileName = saveCrashInfo2File(ex)
|
||||||
if ((ex is OutOfMemoryError || ex.cause is OutOfMemoryError) && AppConfig.recordHeapDump) {
|
if ((ex is OutOfMemoryError || ex.cause is OutOfMemoryError) && AppConfig.recordHeapDump) {
|
||||||
doHeapDump()
|
doHeapDump()
|
||||||
}
|
}
|
||||||
|
return if (startCrashReport(crashFileName)) {
|
||||||
|
LocalConfig.appCrash = false
|
||||||
|
true
|
||||||
|
} else {
|
||||||
context.longToastOnUiLegacy(ex.stackTraceStr)
|
context.longToastOnUiLegacy(ex.stackTraceStr)
|
||||||
Thread.sleep(3000)
|
Thread.sleep(3000)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startCrashReport(fileName: String): Boolean {
|
||||||
|
return runCatching {
|
||||||
|
val intent = Intent()
|
||||||
|
.setClassName(context.packageName, CRASH_REPORT_ACTIVITY)
|
||||||
|
.putExtra(EXTRA_CRASH_FILE_NAME, fileName)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}.isSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val EXTRA_CRASH_FILE_NAME = "crashFileName"
|
||||||
|
|
||||||
|
private const val CRASH_REPORT_ACTIVITY = "io.legado.app.ui.about.CrashReportActivity"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存储异常和参数信息
|
* 存储异常和参数信息
|
||||||
*/
|
*/
|
||||||
@@ -123,7 +150,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
/**
|
/**
|
||||||
* 保存错误信息到文件中
|
* 保存错误信息到文件中
|
||||||
*/
|
*/
|
||||||
fun saveCrashInfo2File(ex: Throwable) {
|
fun saveCrashInfo2File(ex: Throwable): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
for ((key, value) in paramsMap) {
|
for ((key, value) in paramsMap) {
|
||||||
sb.append(key).append("=").append(value).append("\n")
|
sb.append(key).append("=").append(value).append("\n")
|
||||||
@@ -165,6 +192,19 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
.writeText(crashLog)
|
.writeText(crashLog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readCrashLog(fileName: String?): String? {
|
||||||
|
return runCatching {
|
||||||
|
val crashDir = appCtx.externalCacheDir?.resolve("crash") ?: return null
|
||||||
|
val file = fileName
|
||||||
|
?.let { crashDir.resolve(it) }
|
||||||
|
?.takeIf { it.isFile }
|
||||||
|
?: crashDir.listFiles { file -> file.isFile }
|
||||||
|
?.maxByOrNull { it.name }
|
||||||
|
file?.readText()
|
||||||
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package io.legado.app.ui.about
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import io.legado.app.base.BaseComposeActivity
|
||||||
|
import io.legado.app.help.CrashHandler
|
||||||
|
import io.legado.app.ui.main.MainActivity
|
||||||
|
import io.legado.app.utils.sendToClip
|
||||||
|
|
||||||
|
class CrashReportActivity : BaseComposeActivity() {
|
||||||
|
|
||||||
|
private val crashText: String by lazy {
|
||||||
|
CrashHandler.readCrashLog(
|
||||||
|
intent.getStringExtra(CrashHandler.EXTRA_CRASH_FILE_NAME)
|
||||||
|
).orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
CrashReportScreen(
|
||||||
|
errorText = crashText,
|
||||||
|
onCopy = { sendToClip(crashText) },
|
||||||
|
onRestart = { restartApp() },
|
||||||
|
onClose = { finish() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restartApp() {
|
||||||
|
startActivity(
|
||||||
|
Intent(this, MainActivity::class.java)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package io.legado.app.ui.about
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.legado.app.R
|
||||||
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
|
import io.legado.app.ui.widget.components.AppScaffold
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||||
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
|
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun CrashReportScreen(
|
||||||
|
errorText: String,
|
||||||
|
onCopy: () -> Unit,
|
||||||
|
onRestart: () -> Unit,
|
||||||
|
onClose: () -> Unit
|
||||||
|
) {
|
||||||
|
val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
|
||||||
|
val displayText = errorText.ifBlank {
|
||||||
|
stringResource(R.string.crash_report_empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
AppScaffold(
|
||||||
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
GlassMediumFlexibleTopAppBar(
|
||||||
|
title = stringResource(R.string.crash_report),
|
||||||
|
navigationIcon = {
|
||||||
|
TopBarNavigationButton(
|
||||||
|
onClick = onClose,
|
||||||
|
imageVector = AppIcons.Close
|
||||||
|
)
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(padding)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
AppText(
|
||||||
|
text = stringResource(R.string.crash_report_message),
|
||||||
|
style = LegadoTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
SelectionContainer(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = displayText,
|
||||||
|
style = LegadoTheme.typography.bodySmall,
|
||||||
|
fontFamily = FontFamily.Monospace
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = onRestart,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.restart_app))
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = onCopy,
|
||||||
|
enabled = errorText.isNotBlank(),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.copy_text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1209,6 +1209,10 @@
|
|||||||
<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="create_heap_dump">创建堆转储</string>
|
<string name="create_heap_dump">创建堆转储</string>
|
||||||
|
<string name="crash_report">崩溃报告</string>
|
||||||
|
<string name="crash_report_message">应用发生崩溃。报告问题时,请复制以下错误信息。</string>
|
||||||
|
<string name="crash_report_empty">未找到崩溃信息。</string>
|
||||||
|
<string name="restart_app">重启应用</string>
|
||||||
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
<string name="record_heap_dump_t">记录堆转储</string>
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
<string name="font_weight_text">字重</string>
|
<string name="font_weight_text">字重</string>
|
||||||
|
|||||||
@@ -1178,6 +1178,10 @@
|
|||||||
<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="create_heap_dump">创建堆转储</string>
|
<string name="create_heap_dump">创建堆转储</string>
|
||||||
|
<string name="crash_report">崩潰報告</string>
|
||||||
|
<string name="crash_report_message">應用程式發生崩潰。回報問題時,請複製以下錯誤資訊。</string>
|
||||||
|
<string name="crash_report_empty">找不到崩潰資訊。</string>
|
||||||
|
<string name="restart_app">重新啟動應用程式</string>
|
||||||
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
<string name="record_heap_dump_t">记录堆转储</string>
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
<string name="font_weight_text">字重</string>
|
<string name="font_weight_text">字重</string>
|
||||||
|
|||||||
@@ -1181,6 +1181,10 @@
|
|||||||
<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="create_heap_dump">创建堆转储</string>
|
<string name="create_heap_dump">创建堆转储</string>
|
||||||
|
<string name="crash_report">崩潰報告</string>
|
||||||
|
<string name="crash_report_message">應用程式發生崩潰。回報問題時,請複製以下錯誤資訊。</string>
|
||||||
|
<string name="crash_report_empty">找不到崩潰資訊。</string>
|
||||||
|
<string name="restart_app">重新啟動應用程式</string>
|
||||||
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
<string name="record_heap_dump_s">当应用发生OOM崩溃时保存堆转储</string>
|
||||||
<string name="record_heap_dump_t">记录堆转储</string>
|
<string name="record_heap_dump_t">记录堆转储</string>
|
||||||
<string name="font_weight_text">字重</string>
|
<string name="font_weight_text">字重</string>
|
||||||
|
|||||||
@@ -1212,6 +1212,10 @@
|
|||||||
<string name="key_page_on_long_press">Press and Hold the Key to Turn Page</string>
|
<string name="key_page_on_long_press">Press and Hold the Key to Turn Page</string>
|
||||||
<string name="save_log">Save logs</string>
|
<string name="save_log">Save logs</string>
|
||||||
<string name="create_heap_dump">Create Heap Dump</string>
|
<string name="create_heap_dump">Create Heap Dump</string>
|
||||||
|
<string name="crash_report">Crash report</string>
|
||||||
|
<string name="crash_report_message">The app crashed. Copy the error information below when reporting the issue.</string>
|
||||||
|
<string name="crash_report_empty">No crash information found.</string>
|
||||||
|
<string name="restart_app">Restart app</string>
|
||||||
<string name="record_heap_dump_s">Saving a heap dump when the application crashes by OOM</string>
|
<string name="record_heap_dump_s">Saving a heap dump when the application crashes by OOM</string>
|
||||||
<string name="record_heap_dump_t">Capture heap dump</string>
|
<string name="record_heap_dump_t">Capture heap dump</string>
|
||||||
<string name="font_weight_text">N/B/L</string>
|
<string name="font_weight_text">N/B/L</string>
|
||||||
|
|||||||
Reference in new issue
Block a user