mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2026-02-04 20:27:16 +08:00
Compare commits
7 Commits
master
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
092f50308d | ||
|
|
b89a4f3550 | ||
|
|
c4108069a9 | ||
|
|
3cfb1dc950 | ||
|
|
3cc37a68ee | ||
|
|
94bc3dc066 | ||
|
|
fd2c563089 |
@@ -59,7 +59,7 @@ androidComponents {
|
|||||||
// Workaround for GitHub Actions.
|
// Workaround for GitHub Actions.
|
||||||
// Strongly transfer type to [String].
|
// Strongly transfer type to [String].
|
||||||
@Suppress("UNNECESSARY_SAFE_CALL")
|
@Suppress("UNNECESSARY_SAFE_CALL")
|
||||||
val currentSuffix = gropify.github.ci.commit.id?.let { suffix ->
|
val currentSuffix = gropify.github.ci.commit.id?.let { suffix: String ->
|
||||||
if (suffix.isNotBlank()) "-$suffix" else ""
|
if (suffix.isNotBlank()) "-$suffix" else ""
|
||||||
}
|
}
|
||||||
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
|
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import java.util.Locale
|
|||||||
* @param targetSdk 目标 SDK 版本
|
* @param targetSdk 目标 SDK 版本
|
||||||
* @param minSdk 最低 SDK 版本
|
* @param minSdk 最低 SDK 版本
|
||||||
* @param isNativeCrash 是否为原生层异常
|
* @param isNativeCrash 是否为原生层异常
|
||||||
|
* @param isAnr 是否为 ANR (Application Not Responding)
|
||||||
* @param exceptionClassName 异常类名
|
* @param exceptionClassName 异常类名
|
||||||
* @param exceptionMessage 异常信息
|
* @param exceptionMessage 异常信息
|
||||||
* @param throwClassName 抛出异常的类名
|
* @param throwClassName 抛出异常的类名
|
||||||
@@ -78,6 +79,8 @@ data class AppErrorsInfoBean(
|
|||||||
var minSdk: Int = -1,
|
var minSdk: Int = -1,
|
||||||
@SerializedName("isNativeCrash")
|
@SerializedName("isNativeCrash")
|
||||||
var isNativeCrash: Boolean = false,
|
var isNativeCrash: Boolean = false,
|
||||||
|
@SerializedName("isAnr")
|
||||||
|
var isAnr: Boolean = false,
|
||||||
@SerializedName("exceptionClassName")
|
@SerializedName("exceptionClassName")
|
||||||
var exceptionClassName: String = "",
|
var exceptionClassName: String = "",
|
||||||
@SerializedName("exceptionMessage")
|
@SerializedName("exceptionMessage")
|
||||||
@@ -134,6 +137,37 @@ data class AppErrorsInfoBean(
|
|||||||
timestamp = System.currentTimeMillis()
|
timestamp = System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 [ApplicationErrorReport.AnrInfo] 克隆
|
||||||
|
* @param context 当前实例
|
||||||
|
* @param pid APP 进程 ID
|
||||||
|
* @param userId APP 用户 ID
|
||||||
|
* @param packageName APP 包名
|
||||||
|
* @param anrInfo [ApplicationErrorReport.AnrInfo]
|
||||||
|
* @return [AppErrorsInfoBean]
|
||||||
|
*/
|
||||||
|
fun cloneAnr(context: Context, pid: Int, userId: Int, packageName: String?, anrInfo: ApplicationErrorReport.AnrInfo?) =
|
||||||
|
AppErrorsInfoBean(
|
||||||
|
pid = pid,
|
||||||
|
userId = userId,
|
||||||
|
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
|
||||||
|
packageName = packageName ?: "unknown",
|
||||||
|
versionName = packageName?.let { context.appVersionNameOf(it).ifBlank { "unknown" } } ?: "",
|
||||||
|
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
|
||||||
|
targetSdk = packageName?.let { context.appTargetSdkOf(it) } ?: -1,
|
||||||
|
minSdk = packageName?.let { context.appMinSdkOf(it) } ?: -1,
|
||||||
|
isNativeCrash = false,
|
||||||
|
isAnr = true,
|
||||||
|
exceptionClassName = "ANR",
|
||||||
|
exceptionMessage = anrInfo?.cause ?: "Application Not Responding",
|
||||||
|
throwFileName = anrInfo?.activity?.toString() ?: "unknown",
|
||||||
|
throwClassName = packageName ?: "unknown",
|
||||||
|
throwMethodName = "unknown",
|
||||||
|
throwLineNumber = -1,
|
||||||
|
stackTrace = anrInfo?.info?.trim() ?: "unknown",
|
||||||
|
timestamp = System.currentTimeMillis()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,7 +282,11 @@ data class AppErrorsInfoBean(
|
|||||||
[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}
|
[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}
|
||||||
[Target SDK]: ${targetSdk.takeIf { it != -1 } ?: "unknown"}
|
[Target SDK]: ${targetSdk.takeIf { it != -1 } ?: "unknown"}
|
||||||
[Min SDK]: ${minSdk.takeIf { it != -1 } ?: "unknown"}
|
[Min SDK]: ${minSdk.takeIf { it != -1 } ?: "unknown"}
|
||||||
[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}
|
[Error Type]: ${when {
|
||||||
|
isAnr -> "ANR"
|
||||||
|
isNativeCrash -> "Native"
|
||||||
|
else -> "JVM"
|
||||||
|
}}
|
||||||
[Crash Time]: $utcTime
|
[Crash Time]: $utcTime
|
||||||
[Stack Trace]:
|
[Stack Trace]:
|
||||||
""".trimIndent() + "\n$stackTrace"
|
""".trimIndent() + "\n$stackTrace"
|
||||||
|
|||||||
@@ -98,8 +98,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
* @param errors [AppErrorsClass] 实例
|
* @param errors [AppErrorsClass] 实例
|
||||||
* @param proc [ProcessRecordClass] 实例
|
* @param proc [ProcessRecordClass] 实例
|
||||||
* @param resultData [AppErrorDialog_DataClass] 实例 - 默认空
|
* @param resultData [AppErrorDialog_DataClass] 实例 - 默认空
|
||||||
|
* @param isAnr 是否为 ANR - 默认 false
|
||||||
*/
|
*/
|
||||||
private class AppErrorsProcessData(errors: Any?, proc: Any?, resultData: Any? = null) {
|
private class AppErrorsProcessData(errors: Any?, proc: Any?, resultData: Any? = null, val isAnr: Boolean = false) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前包列表实例
|
* 获取当前包列表实例
|
||||||
@@ -330,7 +331,12 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
val appNameWithUserId = if (userId != 0) "$appName (${locale.userId(userId)})" else appName
|
val appNameWithUserId = if (userId != 0) "$appName (${locale.userId(userId)})" else appName
|
||||||
|
|
||||||
/** 崩溃标题 */
|
/** 崩溃标题 */
|
||||||
val errorTitle = if (isRepeatingCrash) locale.aerrRepeatedTitle(appNameWithUserId) else locale.aerrTitle(appNameWithUserId)
|
val errorTitle = when {
|
||||||
|
isAnr && isRepeatingCrash -> locale.anrRepeatedTitle(appNameWithUserId)
|
||||||
|
isAnr -> locale.anrTitle(appNameWithUserId)
|
||||||
|
isRepeatingCrash -> locale.aerrRepeatedTitle(appNameWithUserId)
|
||||||
|
else -> locale.aerrTitle(appNameWithUserId)
|
||||||
|
}
|
||||||
|
|
||||||
/** 使用通知推送异常信息 */
|
/** 使用通知推送异常信息 */
|
||||||
fun showAppErrorsWithNotify() =
|
fun showAppErrorsWithNotify() =
|
||||||
@@ -408,6 +414,16 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
YLog.info("Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
|
YLog.info("Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 APP 进程 ANR 数据
|
||||||
|
* @param context 当前实例
|
||||||
|
* @param info ANR 错误报告数据实例
|
||||||
|
*/
|
||||||
|
private fun AppErrorsProcessData.handleAppAnrInfo(context: Context, info: ApplicationErrorReport.AnrInfo?) {
|
||||||
|
AppErrorsRecordData.add(AppErrorsInfoBean.cloneAnr(context, pid, userId, appInfo?.packageName, info))
|
||||||
|
YLog.info("Received ANR application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
|
||||||
|
}
|
||||||
|
|
||||||
override fun onHook() {
|
override fun onHook() {
|
||||||
/** 注册生命周期 */
|
/** 注册生命周期 */
|
||||||
registerLifecycle()
|
registerLifecycle()
|
||||||
@@ -502,6 +518,31 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
/** 创建 APP 进程异常数据类 */
|
/** 创建 APP 进程异常数据类 */
|
||||||
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
|
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
|
||||||
}
|
}
|
||||||
|
/** Hook ANR handling methods */
|
||||||
|
firstMethodOrNull {
|
||||||
|
name = "appNotResponding"
|
||||||
|
}?.hook()?.after {
|
||||||
|
/** 当前实例 */
|
||||||
|
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
|
||||||
|
|
||||||
|
/** 当前进程信息 - 第一个参数是 ProcessRecord */
|
||||||
|
val proc = args().first().any() ?: return@after YLog.warn("Received ANR but got null ProcessRecord")
|
||||||
|
|
||||||
|
/** 创建 APP 进程异常数据类并展示 ANR UI */
|
||||||
|
AppErrorsProcessData(instance, proc, isAnr = true).handleShowAppErrorUi(context)
|
||||||
|
}
|
||||||
|
firstMethodOrNull {
|
||||||
|
name = "handleAnrInActivityController"
|
||||||
|
returnType = Boolean::class
|
||||||
|
}?.hook()?.after {
|
||||||
|
/** 当前实例 */
|
||||||
|
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after
|
||||||
|
|
||||||
|
/** 当前进程信息 */
|
||||||
|
val proc = args().first().any() ?: return@after YLog.warn("Received ANR but got null ProcessRecord")
|
||||||
|
/** 创建 ANR 数据 - args(1) 应该包含 AnrInfo */
|
||||||
|
AppErrorsProcessData(instance, proc, isAnr = true).handleAppAnrInfo(context, args(index = 1).cast())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
<string name="close_app">アプリを閉じる</string>
|
<string name="close_app">アプリを閉じる</string>
|
||||||
<string name="aerr_title">%1$s が停止しました</string>
|
<string name="aerr_title">%1$s が停止しました</string>
|
||||||
<string name="aerr_repeated_title">%1$s が繰り返し停止しています</string>
|
<string name="aerr_repeated_title">%1$s が繰り返し停止しています</string>
|
||||||
|
<string name="anr_title">%1$s は応答していません</string>
|
||||||
|
<string name="anr_repeated_title">%1$s は繰り返し応答していません</string>
|
||||||
<string name="mute_if_unlock_tip">デバイスのロックが解除されるまで「%1$s」のエラーをミュートにします</string>
|
<string name="mute_if_unlock_tip">デバイスのロックが解除されるまで「%1$s」のエラーをミュートにします</string>
|
||||||
<string name="mute_if_restart_tip">デバイスが再起動されるまで「%1$s」のエラーをミュートにします</string>
|
<string name="mute_if_restart_tip">デバイスが再起動されるまで「%1$s」のエラーをミュートにします</string>
|
||||||
<string name="back">戻る</string>
|
<string name="back">戻る</string>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<string name="close_app">关闭应用</string>
|
<string name="close_app">关闭应用</string>
|
||||||
<string name="aerr_title">%1$s 已停止运行</string>
|
<string name="aerr_title">%1$s 已停止运行</string>
|
||||||
<string name="aerr_repeated_title">%1$s 屡次停止运行</string>
|
<string name="aerr_repeated_title">%1$s 屡次停止运行</string>
|
||||||
|
<string name="anr_title">%1$s 无响应</string>
|
||||||
|
<string name="anr_repeated_title">%1$s 屡次无响应</string>
|
||||||
<string name="mute_if_unlock_tip">忽略“%1$s”的错误直到设备重新解锁</string>
|
<string name="mute_if_unlock_tip">忽略“%1$s”的错误直到设备重新解锁</string>
|
||||||
<string name="mute_if_restart_tip">忽略“%1$s”的错误直到设备重新启动</string>
|
<string name="mute_if_restart_tip">忽略“%1$s”的错误直到设备重新启动</string>
|
||||||
<string name="back">返回</string>
|
<string name="back">返回</string>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<string name="close_app">結束程式</string>
|
<string name="close_app">結束程式</string>
|
||||||
<string name="aerr_title">%1$s 已停止運作</string>
|
<string name="aerr_title">%1$s 已停止運作</string>
|
||||||
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
|
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
|
||||||
|
<string name="anr_title">%1$s 無回應</string>
|
||||||
|
<string name="anr_repeated_title">%1$s 屢次無回應</string>
|
||||||
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
||||||
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
||||||
<string name="back">回退</string>
|
<string name="back">回退</string>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<string name="close_app">結束程式</string>
|
<string name="close_app">結束程式</string>
|
||||||
<string name="aerr_title">%1$s 已停止運作</string>
|
<string name="aerr_title">%1$s 已停止運作</string>
|
||||||
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
|
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
|
||||||
|
<string name="anr_title">%1$s 無回應</string>
|
||||||
|
<string name="anr_repeated_title">%1$s 屢次無回應</string>
|
||||||
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
||||||
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
||||||
<string name="back">回退</string>
|
<string name="back">回退</string>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<string name="close_app">結束程式</string>
|
<string name="close_app">結束程式</string>
|
||||||
<string name="aerr_title">%1$s 已停止運作</string>
|
<string name="aerr_title">%1$s 已停止運作</string>
|
||||||
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
|
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
|
||||||
|
<string name="anr_title">%1$s 無回應</string>
|
||||||
|
<string name="anr_repeated_title">%1$s 屢次無回應</string>
|
||||||
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到裝置重新展示</string>
|
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到裝置重新展示</string>
|
||||||
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到裝置重新開機</string>
|
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到裝置重新開機</string>
|
||||||
<string name="back">回退</string>
|
<string name="back">回退</string>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<string name="close_app">Close App</string>
|
<string name="close_app">Close App</string>
|
||||||
<string name="aerr_title">%1$s has stopped</string>
|
<string name="aerr_title">%1$s has stopped</string>
|
||||||
<string name="aerr_repeated_title">%1$s keeps stopping</string>
|
<string name="aerr_repeated_title">%1$s keeps stopping</string>
|
||||||
|
<string name="anr_title">%1$s isn\'t responding</string>
|
||||||
|
<string name="anr_repeated_title">%1$s keeps not responding</string>
|
||||||
<string name="mute_if_unlock_tip">Muted errors for \'%1$s\' until device is re-unlocked</string>
|
<string name="mute_if_unlock_tip">Muted errors for \'%1$s\' until device is re-unlocked</string>
|
||||||
<string name="mute_if_restart_tip">Muted errors for \'%1$s\' until device reboots</string>
|
<string name="mute_if_restart_tip">Muted errors for \'%1$s\' until device reboots</string>
|
||||||
<string name="back">Back</string>
|
<string name="back">Back</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user