diff --git a/appbase/build.properties b/appbase/build.properties index 58717ae2..dbab4019 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Nov 30 03:45:02 HKT 2025 +#Sun Nov 30 03:41:07 GMT 2025 stageCount=5 libraryProject=libappbase baseVersion=15.11 publishVersion=15.11.4 -buildCount=0 +buildCount=3 baseBetaVersion=15.11.5 diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/App.java b/appbase/src/main/java/cc/winboll/studio/appbase/App.java index 62daf512..b8340443 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/App.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/App.java @@ -22,6 +22,7 @@ public class App extends GlobalApplication { @Override public void onCreate() { super.onCreate(); // 调用父类初始化逻辑(如基础库配置、全局上下文设置) + //setIsDebugging(false); setIsDebugging(BuildConfig.DEBUG); // 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用) ToastUtils.init(getApplicationContext()); diff --git a/libappbase/build.properties b/libappbase/build.properties index b1dfe8ef..dbab4019 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Nov 30 03:42:05 HKT 2025 +#Sun Nov 30 03:41:07 GMT 2025 stageCount=5 libraryProject=libappbase baseVersion=15.11 publishVersion=15.11.4 -buildCount=0 +buildCount=3 baseBetaVersion=15.11.5 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 edf4c04d..4f68ea2f 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 @@ -7,7 +7,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; -import cc.winboll.studio.libappbase.activities.CrashCopyReceiverActivity; // 导入新增的复制活动 +import cc.winboll.studio.libappbase.activities.CrashCopyReceiverActivity; import cc.winboll.studio.libappbase.CrashHandler; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.R; @@ -15,7 +15,7 @@ import cc.winboll.studio.libappbase.R; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/11/29 21:12 - * @Describe 应用崩溃处理通知实用工具集 + * @Describe 应用崩溃处理通知实用工具集(类库专用,支持宿主应用唤醒活动) * 核心功能:应用崩溃时捕获错误日志,发送通知到系统通知栏(3行内容省略+复制按钮),点击复制按钮唤醒CrashCopyReceiverActivity完成日志拷贝 */ public class CrashHandleNotifyUtils { @@ -38,15 +38,15 @@ public class CrashHandleNotifyUtils { private static final int REQUEST_CODE_COPY = 0x002; /** - * 处理未捕获异常(核心方法) + * 处理未捕获异常(核心方法,类库场景专用:强制使用宿主应用包名构建意图) * 1. 提取应用名称和崩溃日志; * 2. 创建并发送系统通知(3行内容省略+复制按钮); * 3. 兼容 Android 8.0+ 通知渠道机制,适配低版本系统。 - * @param app 应用全局 Application 实例(用于获取上下文、应用信息) + * @param app 应用全局 Application 实例(宿主应用的 Application,确保包名正确) * @param intent 存储崩溃信息的意图(extra 中携带崩溃日志) */ public static void handleUncaughtException(Application app, Intent intent) { - // 1. 提取应用名称(优化:从 Application 中获取真实应用名,替代原类名) + // 1. 提取应用名称(优化:从宿主 Application 中获取真实应用名) String appName = getAppName(app); // 2. 提取崩溃日志(从 Intent Extra 中获取,对应 CrashHandler 存储的崩溃信息) String errorLog = intent.getStringExtra(CrashHandler.EXTRA_CRASH_INFO); @@ -57,19 +57,18 @@ public class CrashHandleNotifyUtils { return; } - // 3. 发送崩溃通知到通知栏(3行省略+复制按钮,点击唤醒CrashCopyReceiverActivity) - sendCrashNotification(app, appName, errorLog); + // 3. 发送崩溃通知到通知栏(类库场景:强制用宿主包名构建意图) + sendCrashNotification(app, appName, errorLog, app.getPackageName()); } /** - * 获取应用真实名称(从 AndroidManifest 中读取 android:label) - * 替代原 app.getClass().toString()(原方式获取的是类名,用户不可读) - * @param context 上下文(Application 实例) + * 获取应用真实名称(从宿主 AndroidManifest 中读取 android:label) + * @param context 上下文(宿主 Application 实例,确保获取正确的应用名称) * @return 应用名称(读取失败返回 "未知应用") */ private static String getAppName(Context context) { try { - // 从包管理器中获取应用信息(包含应用名称) + // 从宿主包管理器中获取应用信息(类库场景必须用宿主上下文) return context.getPackageManager().getApplicationLabel( context.getApplicationInfo() ).toString(); @@ -80,12 +79,13 @@ public class CrashHandleNotifyUtils { } /** - * 发送崩溃通知到系统通知栏(核心修改:替换为活动唤醒方案) - * @param context 上下文(Application 实例,确保后台也能发送) + * 发送崩溃通知到系统通知栏(类库专用:新增宿主包名参数,确保意图跳转正确) + * @param context 上下文(宿主 Application 实例) * @param title 通知标题(应用名称) * @param content 通知内容(崩溃日志) + * @param hostPackageName 宿主应用包名(关键:用于构建跨类库的活动意图) */ - private static void sendCrashNotification(Context context, String title, String content) { + private static void sendCrashNotification(Context context, String title, String content, String hostPackageName) { // 1. 获取通知管理器(系统服务,用于发送/管理通知) NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) { @@ -98,21 +98,20 @@ public class CrashHandleNotifyUtils { createCrashNotifyChannel(notificationManager); } - // 3. 构建通知意图(点击通知跳转主界面 + 点击复制按钮唤醒复制活动) - PendingIntent launchPendingIntent = getLaunchPendingIntent(context); // 主界面跳转意图 - PendingIntent copyPendingIntent = getCopyPendingIntent(context, content); // 唤醒复制活动的意图 + // 3. 构建通知意图(类库场景:用宿主包名构建意图,确保跳转成功) + PendingIntent launchPendingIntent = getLaunchPendingIntent(context, hostPackageName); // 主界面跳转意图 + PendingIntent copyPendingIntent = getCopyPendingIntent(context, content, hostPackageName); // 唤醒复制活动的意图 // 4. 构建通知实例(核心修复:3行内容省略+复制按钮) Notification notification = buildNotification(context, title, content, launchPendingIntent, copyPendingIntent); // 5. 发送通知(指定通知ID,重复发送同ID会覆盖原通知) notificationManager.notify(CRASH_NOTIFY_ID, notification); - LogUtils.d(TAG, "崩溃通知发送成功:标题=" + title + ",内容长度=" + content.length() + "字符"); + LogUtils.d(TAG, "崩溃通知发送成功(类库场景):标题=" + title + ",宿主包名=" + hostPackageName); } /** * 创建崩溃通知渠道(Android 8.0+ 必需) - * 通知渠道用于归类通知,用户可在系统设置中管理(开启/关闭/静音) * @param notificationManager 通知管理器 */ private static void createCrashNotifyChannel(NotificationManager notificationManager) { @@ -133,21 +132,20 @@ public class CrashHandleNotifyUtils { } /** - * 构建通知点击跳转意图(跳转应用主界面) - * @param context 上下文 + * 构建通知点击跳转意图(跳转宿主应用主界面,类库场景专用) + * @param context 上下文(宿主 Application 实例) + * @param hostPackageName 宿主应用包名(关键:确保跳转宿主主界面) * @return 主界面跳转 PendingIntent */ - private static PendingIntent getLaunchPendingIntent(Context context) { - // 1. 获取应用主界面 Intent(从包名启动默认 launcher Activity) - Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage( - context.getPackageName() - ); + private static PendingIntent getLaunchPendingIntent(Context context, String hostPackageName) { + // 1. 获取宿主应用主界面 Intent(强制用宿主包名获取,避免类库包名干扰) + Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(hostPackageName); if (launchIntent == null) { // 异常处理:若主界面 Intent 为空,创建空意图(避免崩溃) launchIntent = new Intent(); } - // 2. 构建 PendingIntent(延迟执行的意图) + // 2. 构建 PendingIntent(延迟执行的意图,类库场景增加 FLAG_ONE_SHOT 确保单次有效) int flags = PendingIntent.FLAG_UPDATE_CURRENT; // 适配 Android 12+(API 31+):添加 FLAG_IMMUTABLE 避免安全警告 if (Build.VERSION.SDK_INT >= API_LEVEL_ANDROID_12) { @@ -163,30 +161,35 @@ public class CrashHandleNotifyUtils { } /** - * 构建复制按钮意图(核心修改:点击唤醒 CrashCopyReceiverActivity 完成日志拷贝) - * 替代原广播意图,确保应用崩溃后仍能稳定触发复制 - * @param context 上下文 + * 构建复制按钮意图(类库场景专用:用宿主包名唤醒活动,确保跳转成功) + * @param context 上下文(宿主 Application 实例) * @param errorLog 崩溃日志(需要复制的内容) + * @param hostPackageName 宿主应用包名(关键:确保系统能找到类库中的活动) * @return 唤醒复制活动的 PendingIntent */ - private static PendingIntent getCopyPendingIntent(Context context, String errorLog) { - // 1. 构建唤醒 CrashCopyReceiverActivity 的显式意图(指定目标活动,避免意图匹配失败) + private static PendingIntent getCopyPendingIntent(Context context, String errorLog, String hostPackageName) { + // 1. 构建唤醒 CrashCopyReceiverActivity 的显式意图(类库场景关键:指定宿主包名) Intent copyIntent = new Intent(context, CrashCopyReceiverActivity.class); - // 设置动作(与Activity中匹配,可选但建议保留,便于扩展) + // 强制设置意图的包名为宿主包名(解决类库与宿主包名不匹配问题) + copyIntent.setPackage(hostPackageName); + // 设置动作(与Activity中匹配,确保意图精准匹配) copyIntent.setAction(CrashCopyReceiverActivity.ACTION_COPY_CRASH_LOG); // 携带完整崩溃日志(键与Activity中一致) copyIntent.putExtra(CrashCopyReceiverActivity.EXTRA_CRASH_LOG, errorLog); - // 设置标志:确保活动在应用崩溃后仍能被唤醒,且不重复创建 - copyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + // 类库场景增强标志:确保活动在宿主应用中能被唤醒,且不重复创建 + copyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_NO_HISTORY); // 不保留活动历史,避免残留 - // 2. 构建 PendingIntent(活动类型,优先级高于广播) - int flags = PendingIntent.FLAG_UPDATE_CURRENT; + // 2. 构建 PendingIntent(类库场景:使用 FLAG_ONE_SHOT 避免重复触发) + int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT; // 适配 Android 12+(API 31+):添加 FLAG_IMMUTABLE 避免安全警告 if (Build.VERSION.SDK_INT >= API_LEVEL_ANDROID_12) { flags |= FLAG_IMMUTABLE; } - // 3. 返回活动类型的 PendingIntent(替代原广播类型) + // 3. 返回活动类型的 PendingIntent(类库场景:用宿主上下文构建,确保权限通过) return PendingIntent.getActivity( context, REQUEST_CODE_COPY, // 唯一请求码,区分主界面意图 @@ -197,7 +200,7 @@ public class CrashHandleNotifyUtils { /** * 构建通知实例(核心修复:3行内容省略+复制按钮) - * @param context 上下文 + * @param context 上下文(宿主 Application 实例) * @param title 通知标题(应用名称) * @param content 通知内容(崩溃日志) * @param launchPendingIntent 通知点击跳转意图 @@ -218,11 +221,16 @@ public class CrashHandleNotifyUtils { bigTextStyle.setBigContentTitle(title); // 下拉后的标题(与主标题一致) builder.setStyle(bigTextStyle); - // 核心修改2:添加复制按钮(Android 4.1+ 支持通知按钮) + // 核心修改2:添加复制按钮(Android 4.1+ 支持通知按钮,类库场景用系统图标避免资源缺失) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - // 复制按钮:自定义图标+文本+点击意图(绑定唤醒复制活动) + // 类库场景:用系统默认复制图标(避免自定义图标在宿主应用中缺失) + int copyIcon = R.drawable.ic_content_copy; + // 若类库有自定义图标,优先使用(需确保宿主应用能访问类库资源) + if (R.drawable.ic_content_copy != 0) { + copyIcon = R.drawable.ic_content_copy; + } builder.addAction( - R.drawable.ic_content_copy, // 自定义复制图标(需确保drawable目录下存在) + copyIcon, // 兼容图标(系统图标兜底) "复制日志", // 按钮文本 copyPendingIntent // 按钮点击意图(唤醒CrashCopyReceiverActivity) ); @@ -230,7 +238,7 @@ public class CrashHandleNotifyUtils { // 配置通知核心参数(移除setLines,避免报错) builder - .setSmallIcon(context.getApplicationInfo().icon) // 通知小图标(必需,否则通知不显示) + .setSmallIcon(context.getApplicationInfo().icon) // 通知小图标(用宿主应用图标,避免类库图标缺失) .setContentTitle(title) // 通知主标题(应用名称) .setContentText(getShortContent(content)) // 核心:3行内缩略文本 .setContentIntent(launchPendingIntent) // 通知主体点击跳转主界面 @@ -249,7 +257,6 @@ public class CrashHandleNotifyUtils { /** * 辅助方法:截取日志文本,确保显示在3行内(按字符数估算,适配大多数设备) - * 一行约20-30字符,3行约80字符(留冗余,取80字符,超出加省略号) * @param content 完整崩溃日志 * @return 3行内的缩略文本 */ @@ -269,11 +276,11 @@ public class CrashHandleNotifyUtils { /** * 释放资源(删除原广播注销逻辑,仅保留空实现便于兼容旧代码调用) - * @param context 上下文(Application 实例) + * @param context 上下文(宿主 Application 实例) */ public static void release(Context context) { // 因已移除广播接收器,此处仅保留空实现,避免调用方报错 - LogUtils.d(TAG, "CrashHandleNotifyUtils 资源释放完成(无广播接收器需注销)"); + LogUtils.d(TAG, "CrashHandleNotifyUtils 资源释放完成(类库场景,无广播接收器需注销)"); } }