mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-01 08:45:16 +08:00
Fix some custom system can't read application crash info on-time will get wrong errors data problem
This commit is contained in:
@@ -26,6 +26,7 @@ import java.io.Serializable
|
||||
|
||||
/**
|
||||
* 应用异常信息显示 bean
|
||||
* @param pid APP 进程 ID
|
||||
* @param userId APP 用户 ID
|
||||
* @param packageName APP 包名
|
||||
* @param processName APP 进程名
|
||||
@@ -36,6 +37,7 @@ import java.io.Serializable
|
||||
* @param isShowReopenButton 是否显示重新打开按钮
|
||||
*/
|
||||
data class AppErrorsDisplayBean(
|
||||
@Keep var pid: Int,
|
||||
@Keep var userId: Int,
|
||||
@Keep var packageName: String,
|
||||
@Keep var processName: String,
|
||||
|
@@ -33,6 +33,7 @@ import java.util.*
|
||||
|
||||
/**
|
||||
* 应用异常信息 bean
|
||||
* @param pid 进程 ID
|
||||
* @param userId 用户 ID
|
||||
* @param packageName 包名
|
||||
* @param isNativeCrash 是否为原生层异常
|
||||
@@ -46,31 +47,40 @@ import java.util.*
|
||||
* @param timestamp 记录时间戳
|
||||
*/
|
||||
data class AppErrorsInfoBean(
|
||||
@Keep var userId: Int,
|
||||
@Keep var packageName: String,
|
||||
@Keep var isNativeCrash: Boolean,
|
||||
@Keep var exceptionClassName: String,
|
||||
@Keep var exceptionMessage: String,
|
||||
@Keep var throwFileName: String,
|
||||
@Keep var throwClassName: String,
|
||||
@Keep var throwMethodName: String,
|
||||
@Keep var throwLineNumber: Int,
|
||||
@Keep var stackTrace: String,
|
||||
@Keep var timestamp: Long,
|
||||
@Keep var pid: Int = -1,
|
||||
@Keep var userId: Int = -1,
|
||||
@Keep var packageName: String = "",
|
||||
@Keep var isNativeCrash: Boolean = false,
|
||||
@Keep var exceptionClassName: String = "",
|
||||
@Keep var exceptionMessage: String = "",
|
||||
@Keep var throwFileName: String = "",
|
||||
@Keep var throwClassName: String = "",
|
||||
@Keep var throwMethodName: String = "",
|
||||
@Keep var throwLineNumber: Int = -1,
|
||||
@Keep var stackTrace: String = "",
|
||||
@Keep var timestamp: Long = -1L
|
||||
) : Serializable {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 创建一个空的 [AppErrorsInfoBean]
|
||||
* @return [AppErrorsInfoBean]
|
||||
*/
|
||||
fun createEmpty() = AppErrorsInfoBean().apply { isEmpty = true }
|
||||
|
||||
/**
|
||||
* 从 [ApplicationErrorReport.CrashInfo] 克隆
|
||||
* @param pid APP 进程 ID
|
||||
* @param packageName APP 包名
|
||||
* @param userId APP 用户 ID
|
||||
* @param crashInfo [ApplicationErrorReport.CrashInfo]
|
||||
* @return [AppErrorsInfoBean]
|
||||
*/
|
||||
fun clone(packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
||||
fun clone(pid: Int, packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
||||
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
|
||||
AppErrorsInfoBean(
|
||||
pid = pid,
|
||||
userId = userId ?: 0,
|
||||
packageName = packageName ?: "unknown",
|
||||
isNativeCrash = isNativeCrash,
|
||||
@@ -91,6 +101,9 @@ data class AppErrorsInfoBean(
|
||||
}
|
||||
}
|
||||
|
||||
/** 标识当前内容是否为空 */
|
||||
var isEmpty = false
|
||||
|
||||
/**
|
||||
* 获取异常本地化 UTC 时间
|
||||
* @return [String]
|
||||
|
@@ -76,7 +76,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
)
|
||||
|
||||
/** 已记录的 APP 用户 ID */
|
||||
private var appUserIdRecords = HashMap<String, Int>()
|
||||
private var appUserIdRecords = HashMap<Int, Int>()
|
||||
|
||||
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
|
||||
private var mutedErrorsIfUnlockApps = HashSet<String>()
|
||||
@@ -99,6 +99,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
}
|
||||
FrameworkTool.Host.with(instance = this) {
|
||||
onOpenAppUsedFramework { appContext?.openApp(it.first, it.second) }
|
||||
onPushAppErrorInfoData { appErrorsRecords.firstOrNull { e -> e.pid == it } ?: AppErrorsInfoBean.createEmpty() }
|
||||
onPushAppErrorsInfoData { appErrorsRecords }
|
||||
onRemoveAppErrorsInfoData {
|
||||
appErrorsRecords.remove(it)
|
||||
@@ -257,7 +258,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
/** 崩溃标题 */
|
||||
val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)
|
||||
/** 写入到用户 ID 记录 */
|
||||
appUserIdRecords[proc.toString()] = userId
|
||||
appUserIdRecords[pid] = userId
|
||||
/** 打印错误日志 */
|
||||
if (isApp) loggerE(
|
||||
msg = "App \"$packageName\"${if (packageName != processName) " --process \"$processName\"" else ""}" +
|
||||
@@ -298,6 +299,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
/** 启动错误对话框显示窗口 */
|
||||
AppErrorsDisplayActivity.start(
|
||||
context, AppErrorsDisplayBean(
|
||||
pid = pid,
|
||||
userId = userId,
|
||||
packageName = packageName,
|
||||
processName = processName,
|
||||
@@ -320,12 +322,20 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
/** 当前进程信息 */
|
||||
val proc = args().first().any()
|
||||
|
||||
/** 当前 pid 信息 */
|
||||
val pid = ProcessRecordClass.toClass().field { name { it == "mPid" || it == "pid" } }.get(proc).int()
|
||||
|
||||
/** 当前 APP 信息 */
|
||||
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
|
||||
/** 添加当前异常信息到第一位 */
|
||||
appErrorsRecords.add(
|
||||
0, AppErrorsInfoBean.clone(appInfo?.packageName, appUserIdRecords[proc.toString()], args().last().cast())
|
||||
)
|
||||
/** 启动新线程延迟防止方法执行顺序在前导致无法正确获取数据 */
|
||||
newThread {
|
||||
/** 延迟 50ms */
|
||||
Thread.sleep(50)
|
||||
/** 添加当前异常信息到第一位 */
|
||||
appErrorsRecords.add(
|
||||
0, AppErrorsInfoBean.clone(pid, appInfo?.packageName, appUserIdRecords[pid], args().last().cast())
|
||||
)
|
||||
}
|
||||
/** 保存异常记录到本地 */
|
||||
saveAllAppErrorsRecords()
|
||||
}
|
||||
|
@@ -84,13 +84,11 @@ class AppErrorsDisplayActivity : BaseActivity<ActivityAppErrorsDisplayBinding>()
|
||||
cancel()
|
||||
}
|
||||
binding.errorDetailItem.setOnClickListener {
|
||||
FrameworkTool.fetchAppErrorsInfoData(context) { appErrorsInfos ->
|
||||
appErrorsInfos.takeIf { it.isNotEmpty() }
|
||||
?.filter { it.packageName == appErrorsDisplay.packageName }
|
||||
?.takeIf { it.isNotEmpty() }?.get(0)?.let {
|
||||
AppErrorsDetailActivity.start(context, it)
|
||||
cancel()
|
||||
} ?: toast(msg = "No errors founded")
|
||||
FrameworkTool.fetchAppErrorInfoData(context, appErrorsDisplay.pid) { appErrorsInfo ->
|
||||
appErrorsInfo.takeIf { it.isEmpty.not() }?.also {
|
||||
AppErrorsDetailActivity.start(context, it)
|
||||
cancel()
|
||||
} ?: toast(LocaleString.unableGetAppErrorsRecordTip)
|
||||
}
|
||||
}
|
||||
binding.mutedIfUnlockItem.setOnClickListener {
|
||||
|
@@ -56,10 +56,12 @@ object FrameworkTool {
|
||||
private const val CALL_UNMUTE_ERRORS_APP_DATA_RESULT = "call_unmute_errors_app_data_result"
|
||||
private const val CALL_UNMUTE_ALL_ERRORS_APPS_DATA_RESULT = "call_unmute_all_errors_apps_data_result"
|
||||
|
||||
private val CALL_APP_ERROR_DATA_GET = ChannelData<Int>("call_app_error_data_get")
|
||||
private val CALL_OPEN_SPECIFY_APP = ChannelData<Pair<String, Int>>("call_open_specify_app")
|
||||
private val CALL_APP_LIST_DATA_GET = ChannelData<AppFiltersBean>("call_app_info_list_data_get")
|
||||
private val CALL_APP_ERRORS_DATA_REMOVE = ChannelData<AppErrorsInfoBean>("call_app_errors_data_remove")
|
||||
private val CALL_APP_LIST_DATA_GET_RESULT = ChannelData<ArrayList<AppInfoBean>>("call_app_info_list_data_get_result")
|
||||
private val CALL_APP_ERROR_DATA_GET_RESULT = ChannelData<AppErrorsInfoBean>("call_app_error_data_get_result")
|
||||
private val CALL_APP_ERRORS_DATA_GET_RESULT = ChannelData<ArrayList<AppErrorsInfoBean>>("call_app_errors_data_get_result")
|
||||
private val CALL_MUTED_ERRORS_APP_DATA_GET_RESULT = ChannelData<ArrayList<MutedErrorsAppBean>>("call_muted_app_errors_data_get_result")
|
||||
private val CALL_UNMUTE_ERRORS_APP_DATA = ChannelData<MutedErrorsAppBean>("call_unmute_errors_app_data")
|
||||
@@ -88,6 +90,14 @@ object FrameworkTool {
|
||||
*/
|
||||
fun onOpenAppUsedFramework(result: (Pair<String, Int>) -> Unit) = instance?.dataChannel?.wait(CALL_OPEN_SPECIFY_APP) { result(it) }
|
||||
|
||||
/**
|
||||
* 监听发送指定 APP 异常信息
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun onPushAppErrorInfoData(result: (Int) -> AppErrorsInfoBean) {
|
||||
instance?.dataChannel?.with { wait(CALL_APP_ERROR_DATA_GET) { put(CALL_APP_ERROR_DATA_GET_RESULT, result(it)) } }
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听发送 APP 异常信息数组
|
||||
* @param result 回调数据
|
||||
@@ -235,6 +245,19 @@ object FrameworkTool {
|
||||
fun openAppUsedFramework(context: Context, packageName: String, userId: Int) =
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).put(CALL_OPEN_SPECIFY_APP, Pair(packageName, userId))
|
||||
|
||||
/**
|
||||
* 获取指定 APP 异常信息
|
||||
* @param context 实例
|
||||
* @param pid 当前进程 ID
|
||||
* @param result 回调数据
|
||||
*/
|
||||
fun fetchAppErrorInfoData(context: Context, pid: Int, result: (AppErrorsInfoBean) -> Unit) {
|
||||
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||
wait(CALL_APP_ERROR_DATA_GET_RESULT) { result(it) }
|
||||
put(CALL_APP_ERROR_DATA_GET, pid)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 APP 异常信息数组
|
||||
* @param context 实例
|
||||
|
Reference in New Issue
Block a user