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