From 01b0a7736dc90dfb783a54deb87a3a436ceb4e19 Mon Sep 17 00:00:00 2001 From: LaizyBoy Date: Mon, 11 May 2026 19:02:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(CrashHandleNotifyUtils):=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E5=B4=A9=E6=BA=83=E9=80=9A=E7=9F=A5=E6=91=98=E8=A6=81?= =?UTF-8?q?=E6=8F=90=E5=8F=96=E4=B8=8E=E5=B1=95=E7=A4=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增正则表达式解析崩溃日志,提取异常类型、消息、原因及堆栈信息 - 实现 extractBriefInfo() 方法,生成结构化的崩溃摘要 - 优化通知内容展示:异常类型 + 错误信息 + 关键堆栈(最多3行) - 添加摘要最大长度控制(200字符),防止内容过长 - 新增展开通知布局文件支持 --- appbase/build.properties | 4 +- libappbase/build.properties | 4 +- .../utils/CrashHandleNotifyUtils.java | 112 +++++++++++++++--- .../main/res/layout/notification_crash.xml | 27 +++++ .../layout/notification_crash_expanded.xml | 27 +++++ 5 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 libappbase/src/main/res/layout/notification_crash.xml create mode 100644 libappbase/src/main/res/layout/notification_crash_expanded.xml diff --git a/appbase/build.properties b/appbase/build.properties index c6b702b..1abad11 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon May 11 16:56:31 HKT 2026 +#Mon May 11 18:51:30 CST 2026 stageCount=7 libraryProject=libappbase baseVersion=15.20 publishVersion=15.20.6 -buildCount=0 +buildCount=12 baseBetaVersion=15.20.7 diff --git a/libappbase/build.properties b/libappbase/build.properties index ba0efde..1abad11 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon May 11 16:56:19 HKT 2026 +#Mon May 11 18:51:30 CST 2026 stageCount=7 libraryProject=libappbase baseVersion=15.20 publishVersion=15.20.6 -buildCount=0 +buildCount=12 baseBetaVersion=15.20.7 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/CrashHandleNotifyUtils.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/CrashHandleNotifyUtils.java index 578e8c0..2b8832b 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/CrashHandleNotifyUtils.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/utils/CrashHandleNotifyUtils.java @@ -11,6 +11,7 @@ import android.text.TextUtils; import cc.winboll.studio.libappbase.CrashHandler; import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.libappbase.R; /** * 应用崩溃处理通知实用工具集(类库兼容版) @@ -36,6 +37,21 @@ public class CrashHandleNotifyUtils { private static final int FLAG_IMMUTABLE = 0x00000040; /** 通知内容最大行数(控制在3行,超出部分省略) */ private static final int NOTIFICATION_MAX_LINES = 3; + /** 通知摘要最大长度 */ + private static final int SUMMARY_MAX_LENGTH = 200; + /** 展开按钮广播Action */ + private static final String ACTION_EXPAND = "cc.winboll.studio.libappbase.ACTION_EXPAND_CRASH"; + + private static Context sHostContext = null; + private static String sHostPackageName = ""; + private static String sHostAppName = ""; + private static Class sReportCrashActivity = null; + + // ====================== 正则表达式定义 ====================== + private static final String REGEX_EXCEPTION_TYPE = "([\\w.]+Exception|[\\w.]+Error)"; + private static final String REGEX_EXCEPTION_MESSAGE = "(?<=:\\s)(.+?)(?=\\n|\\r|$)"; + private static final String REGEX_STACK_TRACE = "\\s+at\\s+([\\w.$]+)\\.([\\w<>]+)\\(([^:]+\\.java):(\\d+)\\)"; + private static final String REGEX_CAUSE = "(?<=Caused by:\\s)" + REGEX_EXCEPTION_TYPE + "\\s*:"; // ====================== 对外核心方法 ====================== /** @@ -104,11 +120,11 @@ public class CrashHandleNotifyUtils { * @param errorLog 崩溃日志 * @param reportCrashActivity 跳转Activity */ - private static void sendCrashNotification(final Context hostContext, - final String hostPackageName, - final String hostAppName, - final String errorLog, - final Class reportCrashActivity) { +private static void sendCrashNotification(final Context hostContext, + final String hostPackageName, + final String hostAppName, + final String errorLog, + final Class reportCrashActivity) { final NotificationManager notificationManager = (NotificationManager) hostContext.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) { @@ -125,7 +141,7 @@ public class CrashHandleNotifyUtils { LogUtils.e(TAG, "构建跳转PendingIntent失败"); return; } - final Notification notification = buildNotification(hostContext, hostAppName, errorLog, jumpIntent); + final Notification notification = buildNotification(hostContext, hostPackageName, hostAppName, errorLog, jumpIntent); notificationManager.notify(CRASH_NOTIFY_ID, notification); LogUtils.d(TAG, "崩溃通知发送成功,宿主包名:" + hostPackageName); } @@ -171,7 +187,7 @@ public class CrashHandleNotifyUtils { if (Build.VERSION.SDK_INT >= API_LEVEL_ANDROID_12) { flags |= FLAG_IMMUTABLE; } - return PendingIntent.getActivity( +return PendingIntent.getActivity( hostContext, CRASH_NOTIFY_ID, crashIntent, @@ -186,6 +202,7 @@ public class CrashHandleNotifyUtils { /** * 构建Notification通知实例 * @param hostContext 宿主上下文 + * @param hostPackageName 宿主包名 * @param hostAppName 宿主应用名 * @param errorLog 崩溃日志 * @param jumpIntent 点击跳转意图 @@ -193,6 +210,7 @@ public class CrashHandleNotifyUtils { */ @SuppressWarnings("deprecation") private static Notification buildNotification(final Context hostContext, + final String hostPackageName, final String hostAppName, final String errorLog, final PendingIntent jumpIntent) { @@ -200,15 +218,15 @@ public class CrashHandleNotifyUtils { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(CRASH_NOTIFY_CHANNEL_ID); } + String briefInfo = extractBriefInfo(errorLog); Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle(); - bigTextStyle.setSummaryText("日志已省略,下拉查看完整内容"); - bigTextStyle.bigText(errorLog); bigTextStyle.setBigContentTitle(hostAppName + " 崩溃"); + bigTextStyle.bigText(briefInfo); + bigTextStyle.setSummaryText("点击查看详情"); builder.setStyle(bigTextStyle); - builder.setSmallIcon(hostContext.getApplicationInfo().icon) .setContentTitle(hostAppName + " 崩溃") - .setContentText(getShortContent(errorLog)) + .setContentText(briefInfo.split("\n")[0]) .setContentIntent(jumpIntent) .setAutoCancel(true) .setWhen(System.currentTimeMillis()) @@ -226,12 +244,76 @@ public class CrashHandleNotifyUtils { * @param content 原始日志 * @return 缩略文案 */ - private static String getShortContent(final String content) { - if (content == null || content.isEmpty()) { + private static String getShortContent(final String errorLog) { + if (errorLog == null || errorLog.isEmpty()) { return "无崩溃日志"; } - final int maxLength = 80; - return content.length() <= maxLength ? content : content.substring(0, maxLength) + "..."; + String brief = extractBriefInfo(errorLog); + String firstLine = brief.split("\n")[0]; + if (firstLine.length() > 80) { + firstLine = firstLine.substring(0, 80) + "..."; + } + return firstLine; + } + + /** + * 使用正则表达式从崩溃日志中提取简要信息 + * @param crashLog 完整崩溃日志 + * @return 简要崩溃信息 + */ + private static String extractBriefInfo(final String crashLog) { + if (crashLog == null || crashLog.isEmpty()) { + return "无崩溃日志"; + } + StringBuilder brief = new StringBuilder(); + try { + java.util.regex.Pattern exceptionPattern = java.util.regex.Pattern.compile(REGEX_EXCEPTION_TYPE); + java.util.regex.Matcher exceptionMatcher = exceptionPattern.matcher(crashLog); + if (exceptionMatcher.find()) { + brief.append(exceptionMatcher.group(1)); + } + java.util.regex.Pattern messagePattern = java.util.regex.Pattern.compile(REGEX_EXCEPTION_MESSAGE); + java.util.regex.Matcher messageMatcher = messagePattern.matcher(crashLog); + if (messageMatcher.find()) { + String message = messageMatcher.group(1).trim(); + if (message.length() > 100) { + message = message.substring(0, 100) + "..."; + } + if (brief.length() > 0) { + brief.append(" - "); + } + brief.append(message); + } + java.util.regex.Pattern causePattern = java.util.regex.Pattern.compile(REGEX_CAUSE); + java.util.regex.Matcher causeMatcher = causePattern.matcher(crashLog); + if (causeMatcher.find()) { + if (brief.length() > 0) { + brief.append("\n"); + } + brief.append("原因: ").append(causeMatcher.group(1)); + } + java.util.regex.Pattern stackPattern = java.util.regex.Pattern.compile(REGEX_STACK_TRACE); + java.util.regex.Matcher stackMatcher = stackPattern.matcher(crashLog); + int lineCount = 0; + while (stackMatcher.find() && lineCount < 3) { + if (brief.length() > 0) { + brief.append("\n"); + } + brief.append(" at ").append(stackMatcher.group(1)).append(".") + .append(stackMatcher.group(2)).append("(") + .append(stackMatcher.group(3)).append(":") + .append(stackMatcher.group(4)).append(")"); + lineCount++; + } + if (brief.length() == 0) { + brief.append(crashLog.length() > SUMMARY_MAX_LENGTH ? crashLog.substring(0, SUMMARY_MAX_LENGTH) + "..." : crashLog); + } + } catch (Exception e) { + LogUtils.e(TAG, "提取崩溃简要信息失败", e); + brief.setLength(0); + brief.append(crashLog.length() > SUMMARY_MAX_LENGTH ? crashLog.substring(0, SUMMARY_MAX_LENGTH) + "..." : crashLog); + } + return brief.toString(); } /** diff --git a/libappbase/src/main/res/layout/notification_crash.xml b/libappbase/src/main/res/layout/notification_crash.xml new file mode 100644 index 0000000..6fbe3e3 --- /dev/null +++ b/libappbase/src/main/res/layout/notification_crash.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/libappbase/src/main/res/layout/notification_crash_expanded.xml b/libappbase/src/main/res/layout/notification_crash_expanded.xml new file mode 100644 index 0000000..0a1a118 --- /dev/null +++ b/libappbase/src/main/res/layout/notification_crash_expanded.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file