mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-04 02:05:16 +08:00
Modify make crashed apps info persistence and add more info to stackOutputShareContent, stackOutputFileContent in AppErrorsInfoBean, FrameworkHooker, AppErrorsDetailActivity
This commit is contained in:
@@ -22,10 +22,10 @@
|
|||||||
package com.fankes.apperrorstracking.bean
|
package com.fankes.apperrorstracking.bean
|
||||||
|
|
||||||
import android.app.ApplicationErrorReport
|
import android.app.ApplicationErrorReport
|
||||||
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import com.fankes.apperrorstracking.locale.LocaleString
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
import com.fankes.apperrorstracking.utils.factory.difference
|
import com.fankes.apperrorstracking.utils.factory.*
|
||||||
import com.fankes.apperrorstracking.utils.factory.toUtcTime
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@@ -35,7 +35,10 @@ import java.util.*
|
|||||||
* 应用异常信息 bean
|
* 应用异常信息 bean
|
||||||
* @param pid 进程 ID
|
* @param pid 进程 ID
|
||||||
* @param userId 用户 ID
|
* @param userId 用户 ID
|
||||||
|
* @param cpuAbi CPU 架构类型
|
||||||
* @param packageName 包名
|
* @param packageName 包名
|
||||||
|
* @param versionName 版本名称
|
||||||
|
* @param versionCode 版本号
|
||||||
* @param isNativeCrash 是否为原生层异常
|
* @param isNativeCrash 是否为原生层异常
|
||||||
* @param exceptionClassName 异常类名
|
* @param exceptionClassName 异常类名
|
||||||
* @param exceptionMessage 异常信息
|
* @param exceptionMessage 异常信息
|
||||||
@@ -51,8 +54,14 @@ data class AppErrorsInfoBean(
|
|||||||
var pid: Int = -1,
|
var pid: Int = -1,
|
||||||
@SerializedName("userId")
|
@SerializedName("userId")
|
||||||
var userId: Int = -1,
|
var userId: Int = -1,
|
||||||
|
@SerializedName("cpuAbi")
|
||||||
|
var cpuAbi: String = "",
|
||||||
@SerializedName("packageName")
|
@SerializedName("packageName")
|
||||||
var packageName: String = "",
|
var packageName: String = "",
|
||||||
|
@SerializedName("versionName")
|
||||||
|
var versionName: String = "",
|
||||||
|
@SerializedName("versionCode")
|
||||||
|
var versionCode: Long = -1L,
|
||||||
@SerializedName("isNativeCrash")
|
@SerializedName("isNativeCrash")
|
||||||
var isNativeCrash: Boolean = false,
|
var isNativeCrash: Boolean = false,
|
||||||
@SerializedName("exceptionClassName")
|
@SerializedName("exceptionClassName")
|
||||||
@@ -77,18 +86,22 @@ data class AppErrorsInfoBean(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 [ApplicationErrorReport.CrashInfo] 克隆
|
* 从 [ApplicationErrorReport.CrashInfo] 克隆
|
||||||
|
* @param context 当前实例
|
||||||
* @param pid APP 进程 ID
|
* @param pid APP 进程 ID
|
||||||
* @param userId APP 用户 ID
|
* @param userId APP 用户 ID
|
||||||
* @param packageName APP 包名
|
* @param packageName APP 包名
|
||||||
* @param crashInfo [ApplicationErrorReport.CrashInfo]
|
* @param crashInfo [ApplicationErrorReport.CrashInfo]
|
||||||
* @return [AppErrorsInfoBean]
|
* @return [AppErrorsInfoBean]
|
||||||
*/
|
*/
|
||||||
fun clone(pid: Int, userId: Int, packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
fun clone(context: Context, pid: Int, userId: Int, packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
||||||
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
|
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
|
||||||
AppErrorsInfoBean(
|
AppErrorsInfoBean(
|
||||||
pid = pid,
|
pid = pid,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
|
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
|
||||||
packageName = packageName ?: "unknown",
|
packageName = packageName ?: "unknown",
|
||||||
|
versionName = packageName?.let { context.appVersionNameOf(it) } ?: "",
|
||||||
|
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
|
||||||
isNativeCrash = isNativeCrash,
|
isNativeCrash = isNativeCrash,
|
||||||
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
|
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
|
||||||
exceptionMessage = if (isNativeCrash) crashInfo?.stackTrace.let {
|
exceptionMessage = if (isNativeCrash) crashInfo?.stackTrace.let {
|
||||||
@@ -119,6 +132,12 @@ data class AppErrorsInfoBean(
|
|||||||
*/
|
*/
|
||||||
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
|
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 APP 版本信息与版本号
|
||||||
|
* @return [String]
|
||||||
|
*/
|
||||||
|
val versionBrand get() = if (versionName.isBlank()) "unknown" else "$versionName($versionCode)"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取异常本地化 UTC 时间
|
* 获取异常本地化 UTC 时间
|
||||||
* @return [String]
|
* @return [String]
|
||||||
@@ -153,18 +172,7 @@ data class AppErrorsInfoBean(
|
|||||||
val stackOutputShareContent
|
val stackOutputShareContent
|
||||||
get() = "Generated by AppErrorsTracking\n" +
|
get() = "Generated by AppErrorsTracking\n" +
|
||||||
"Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
|
"Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
|
||||||
"===============\n" +
|
"===============\n$environmentInfo"
|
||||||
"[Device Brand]: ${Build.BRAND}\n" +
|
|
||||||
"[Device Model]: ${Build.MODEL}\n" +
|
|
||||||
"[Display]: ${Build.DISPLAY}\n" +
|
|
||||||
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
|
|
||||||
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
|
||||||
"[System Locale]: ${Locale.getDefault()}\n" +
|
|
||||||
"[Package Name]: $packageName\n" +
|
|
||||||
(if (userId > 0) "[User Id]: $userId\n" else "") +
|
|
||||||
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
|
||||||
"[Crash Time]: $utcTime\n" +
|
|
||||||
"[Stack Trace]:\n" + stackTrace
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取异常堆栈文件模板
|
* 获取异常堆栈文件模板
|
||||||
@@ -175,15 +183,26 @@ data class AppErrorsInfoBean(
|
|||||||
" Generated by AppErrorsTracking\n" +
|
" Generated by AppErrorsTracking\n" +
|
||||||
" Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
|
" Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
|
||||||
"================================================================\n" +
|
"================================================================\n" +
|
||||||
"[Device Brand]: ${Build.BRAND}\n" +
|
environmentInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取运行环境信息
|
||||||
|
* @return [String]
|
||||||
|
*/
|
||||||
|
private val environmentInfo
|
||||||
|
get() = "[Device Brand]: ${Build.BRAND}\n" +
|
||||||
"[Device Model]: ${Build.MODEL}\n" +
|
"[Device Model]: ${Build.MODEL}\n" +
|
||||||
"[Display]: ${Build.DISPLAY}\n" +
|
"[Display]: ${Build.DISPLAY}\n" +
|
||||||
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
|
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
|
||||||
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
|
||||||
"[System Locale]: ${Locale.getDefault()}\n" +
|
"[System Locale]: ${Locale.getDefault()}\n" +
|
||||||
"[Package Name]: $packageName\n" +
|
"[Process ID]: $pid\n" +
|
||||||
(if (userId > 0) "[User Id]: $userId\n" else "") +
|
(if (userId > 0) "[User Id]: $userId\n" else "") +
|
||||||
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
"[CPU ABI]: ${cpuAbi.ifBlank { "none" }}\n" +
|
||||||
|
"[Package Name]: $packageName\n" +
|
||||||
|
"[Version Name]: ${versionName.ifBlank { "unknown" }}\n" +
|
||||||
|
"[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}\n" +
|
||||||
|
"[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}\n" +
|
||||||
"[Crash Time]: $utcTime\n" +
|
"[Crash Time]: $utcTime\n" +
|
||||||
"[Stack Trace]:\n" + stackTrace
|
"[Stack Trace]:\n" + stackTrace
|
||||||
}
|
}
|
@@ -350,10 +350,11 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 APP 进程异常数据
|
* 处理 APP 进程异常数据
|
||||||
|
* @param context 当前实例
|
||||||
* @param info 系统错误报告数据实例
|
* @param info 系统错误报告数据实例
|
||||||
*/
|
*/
|
||||||
private fun AppErrorsProcessData.handleAppErrorsInfo(info: ApplicationErrorReport.CrashInfo?) {
|
private fun AppErrorsProcessData.handleAppErrorsInfo(context: Context, info: ApplicationErrorReport.CrashInfo?) {
|
||||||
AppErrorsRecordData.add(AppErrorsInfoBean.clone(pid, userId, appInfo?.packageName, info))
|
AppErrorsRecordData.add(AppErrorsInfoBean.clone(context, pid, userId, appInfo?.packageName, info))
|
||||||
loggerI(msg = "Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
|
loggerI(msg = "Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,10 +442,13 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
returnType = BooleanType
|
returnType = BooleanType
|
||||||
}
|
}
|
||||||
afterHook {
|
afterHook {
|
||||||
|
/** 当前实例 */
|
||||||
|
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@afterHook
|
||||||
|
|
||||||
/** 当前进程信息 */
|
/** 当前进程信息 */
|
||||||
val proc = args().first().any() ?: return@afterHook loggerW(msg = "Received but got null ProcessRecord")
|
val proc = args().first().any() ?: return@afterHook loggerW(msg = "Received but got null ProcessRecord")
|
||||||
/** 创建 APP 进程异常数据类 */
|
/** 创建 APP 进程异常数据类 */
|
||||||
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(args(index = 1).cast())
|
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -105,10 +105,10 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
}
|
}
|
||||||
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
|
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
|
||||||
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
|
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
|
||||||
binding.appVersionText.text = appVersionBrandOf(appErrorsInfo.packageName)
|
binding.appVersionText.text = appErrorsInfo.versionBrand
|
||||||
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
|
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
|
||||||
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
|
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
|
||||||
binding.appCpuAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi }
|
binding.appCpuAbiText.text = appErrorsInfo.cpuAbi.ifBlank { LocaleString.noCpuAbi }
|
||||||
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
|
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
|
||||||
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
||||||
binding.errorInfoText.text = appErrorsInfo.exceptionMessage
|
binding.errorInfoText.text = appErrorsInfo.exceptionMessage
|
||||||
|
Reference in New Issue
Block a user