feat: 应用崩溃页面
This commit is contained in:
parent
d38a9d3357
commit
9861c468f8
7 changed files with 199 additions and 0 deletions
|
|
@ -24,6 +24,9 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.CrashActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -2,6 +2,7 @@ package cn.super12138.todo
|
|||
|
||||
import android.app.Application
|
||||
import cn.super12138.todo.logic.database.TodoDatabase
|
||||
import cn.super12138.todo.ui.pages.crash.CrashHandler
|
||||
|
||||
class TodoApp : Application() {
|
||||
private val database by lazy { TodoDatabase.getDatabase(this) }
|
||||
|
|
@ -12,6 +13,10 @@ class TodoApp : Application() {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
db = database
|
||||
|
||||
val crashHandler = CrashHandler(applicationContext)
|
||||
Thread.setDefaultUncaughtExceptionHandler(crashHandler)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package cn.super12138.todo.ui.activities
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cn.super12138.todo.R
|
||||
import cn.super12138.todo.ui.pages.crash.CrashPage
|
||||
import cn.super12138.todo.ui.theme.ToDoTheme
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
||||
class CrashActivity : ComponentActivity() {
|
||||
companion object {
|
||||
const val BRAND_PREFIX = "Brand: "
|
||||
const val MODEL_PREFIX = "Model: "
|
||||
const val DEVICE_SDK_PREFIX = "Device SDK: "
|
||||
const val CRASH_TIME_PREFIX = "Crash time: "
|
||||
const val BEGINNING_CRASH = "======beginning of crash======"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val crashLogs = intent.getStringExtra("crash_logs")
|
||||
|
||||
val deviceBrand = Build.BRAND
|
||||
val deviceModel = Build.MODEL
|
||||
val sdkLevel = Build.VERSION.SDK_INT
|
||||
val currentDateTime = Calendar.getInstance().time
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||
val formattedDateTime = formatter.format(currentDateTime)
|
||||
|
||||
val deviceInfo = StringBuilder().apply {
|
||||
append(BRAND_PREFIX).append("").append(deviceBrand).append('\n')
|
||||
append(MODEL_PREFIX).append(deviceModel).append('\n')
|
||||
append(DEVICE_SDK_PREFIX).append(sdkLevel).append('\n').append('\n')
|
||||
append(CRASH_TIME_PREFIX).append(formattedDateTime).append('\n').append('\n')
|
||||
append(BEGINNING_CRASH).append('\n')
|
||||
}
|
||||
|
||||
val crashLogFormatted = StringBuilder().apply {
|
||||
append(deviceInfo)
|
||||
append(crashLogs)
|
||||
}.toString()
|
||||
|
||||
setContent {
|
||||
ToDoTheme {
|
||||
val view = LocalView.current
|
||||
CrashPage(
|
||||
crashLog = if (crashLogs == null) stringResource(R.string.tip_no_crash_logs) else crashLogFormatted,
|
||||
exitApp = {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
finishAffinity()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.super12138.todo.ui.pages.crash
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Process
|
||||
import cn.super12138.todo.ui.activities.CrashActivity
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class CrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler {
|
||||
private val defaultUEH = Thread.getDefaultUncaughtExceptionHandler()
|
||||
|
||||
override fun uncaughtException(thread: Thread, ex: Throwable) {
|
||||
val stackTrace = ex.stackTraceToString()
|
||||
|
||||
// 启动新的 Activity 来显示崩溃日志
|
||||
val intent = Intent(context, CrashActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
putExtra("crash_logs", stackTrace)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
|
||||
// 传递异常给默认的异常处理器
|
||||
defaultUEH?.uncaughtException(thread, ex)
|
||||
|
||||
// 杀掉崩溃的应用程序进程
|
||||
Process.killProcess(Process.myPid())
|
||||
exitProcess(10)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package cn.super12138.todo.ui.pages.crash
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeContent
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ExitToApp
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cn.super12138.todo.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CrashPage(
|
||||
crashLog: String,
|
||||
exitApp: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
Scaffold(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.page_crash),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = exitApp,
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.ExitToApp,
|
||||
contentDescription = stringResource(R.string.action_exit_app)
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.action_exit_app)
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeContent
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Spacer(Modifier.height(5.dp))
|
||||
SelectionContainer {
|
||||
Text(
|
||||
text = crashLog,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,4 +22,7 @@
|
|||
<string name="subject_others">其它</string>
|
||||
<string name="title_edit_task">修改待办</string>
|
||||
<string name="tip_remain_tasks">剩余 %s 项任务</string>
|
||||
<string name="page_crash">应用程序出现错误</string>
|
||||
<string name="tip_no_crash_logs">没有日志传入</string>
|
||||
<string name="action_exit_app">退出应用</string>
|
||||
</resources>
|
||||
|
|
@ -21,4 +21,7 @@
|
|||
<string name="subject_others">Others</string>
|
||||
<string name="title_edit_task">Edit Task</string>
|
||||
<string name="tip_remain_tasks">%s tasks remaining</string>
|
||||
<string name="page_crash">Oops! App went wrong</string>
|
||||
<string name="tip_no_crash_logs">No crash logs</string>
|
||||
<string name="action_exit_app">Exit app</string>
|
||||
</resources>
|
||||
Loading…
Reference in a new issue