@@ -0,0 +1,195 @@
package cc.winboll.studio.libappbase.utils ;
import android.app.Application ;
import android.app.Notification ;
import android.app.NotificationManager ;
import android.app.PendingIntent ;
import android.content.Context ;
import android.content.Intent ;
import android.os.Build ;
import cc.winboll.studio.libappbase.CrashHandler ;
import cc.winboll.studio.libappbase.LogUtils ;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/29 21:12
* @Describe 应用崩溃处理通知实用工具集
* 核心功能:应用崩溃时捕获错误日志,发送通知到系统通知栏,方便用户查看崩溃信息
*/
public class CrashHandleNotifyUtils {
public static final String TAG = " CrashHandleNotifyUtils " ;
/** 通知渠道ID( Android 8.0+ 必须,用于归类通知) */
private static final String CRASH_NOTIFY_CHANNEL_ID = " crash_notify_channel " ;
/** 通知渠道名称(用户可见,描述渠道用途) */
private static final String CRASH_NOTIFY_CHANNEL_NAME = " 应用崩溃通知 " ;
/** 通知ID( 唯一标识一条通知, 避免重复创建) */
private static final int CRASH_NOTIFY_ID = 0x001 ;
/** Android 12 对应 API 版本号( 31) , 替代 Build.VERSION_CODES.S */
private static final int API_LEVEL_ANDROID_12 = 31 ;
/** PendingIntent.FLAG_IMMUTABLE 常量值( API 31+),避免依赖高版本 SDK */
private static final int FLAG_IMMUTABLE = 0x00000040 ;
/**
* 处理未捕获异常(核心方法)
* 1. 提取应用名称和崩溃日志;
* 2. 创建并发送系统通知(标题:应用名称,内容:崩溃日志);
* 3. 兼容 Android 8.0+ 通知渠道机制,适配低版本系统。
* @param app 应用全局 Application 实例(用于获取上下文、应用信息)
* @param intent 存储崩溃信息的意图( extra 中携带崩溃日志)
*/
public static void handleUncaughtException ( Application app , Intent intent ) {
// 1. 提取应用名称(优化:从 Application 中获取真实应用名,替代原类名)
String appName = getAppName ( app ) ;
// 2. 提取崩溃日志(从 Intent Extra 中获取,对应 CrashHandler 存储的崩溃信息)
String errorLog = intent . getStringExtra ( CrashHandler . EXTRA_CRASH_INFO ) ;
// 校验参数(避免空指针,确保通知正常发送)
if ( app = = null | | appName = = null | | errorLog = = null ) {
LogUtils . e ( TAG , " 发送崩溃通知失败: 参数为空( app= " + app + " , appName= " + appName + " , errorLog= " + errorLog + " ) " ) ;
return ;
}
// 3. 发送崩溃通知到通知栏
sendCrashNotification ( app , appName , errorLog ) ;
}
/**
* 获取应用真实名称(从 AndroidManifest 中读取 android:label)
* 替代原 app.getClass().toString()(原方式获取的是类名,用户不可读)
* @param context 上下文( Application 实例)
* @return 应用名称(读取失败返回 "未知应用")
*/
private static String getAppName ( Context context ) {
try {
// 从包管理器中获取应用信息(包含应用名称)
return context . getPackageManager ( ) . getApplicationLabel (
context . getApplicationInfo ( )
) . toString ( ) ;
} catch ( Exception e ) {
LogUtils . e ( TAG , " 获取应用名称失败 " , e ) ;
return " 未知应用 " ;
}
}
/**
* 发送崩溃通知到系统通知栏
* @param context 上下文( Application 实例,确保后台也能发送)
* @param title 通知标题(应用名称)
* @param content 通知内容(崩溃日志)
*/
private static void sendCrashNotification ( Context context , String title , String content ) {
// 1. 获取通知管理器(系统服务,用于发送/管理通知)
NotificationManager notificationManager = ( NotificationManager ) context . getSystemService ( Context . NOTIFICATION_SERVICE ) ;
if ( notificationManager = = null ) {
LogUtils . e ( TAG , " 发送崩溃通知失败:获取 NotificationManager 为空 " ) ;
return ;
}
// 2. 适配 Android 8.0+( API 26+):创建通知渠道(必须,否则通知不显示)
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . O ) {
createCrashNotifyChannel ( notificationManager ) ;
}
// 3. 构建通知意图(点击通知时可跳转,此处默认跳转应用主界面,可自定义)
PendingIntent pendingIntent = getNotificationPendingIntent ( context ) ;
// 4. 构建通知实例(兼容低版本,使用 Notification.Builder 构建)
Notification notification = buildNotification ( context , title , content , pendingIntent ) ;
// 5. 发送通知( 指定通知ID, 重复发送同ID会覆盖原通知)
notificationManager . notify ( CRASH_NOTIFY_ID , notification ) ;
LogUtils . d ( TAG , " 崩溃通知发送成功:标题= " + title + " ,内容长度= " + content . length ( ) + " 字符 " ) ;
}
/**
* 创建崩溃通知渠道( Android 8.0+ 必需)
* 通知渠道用于归类通知,用户可在系统设置中管理(开启/关闭/静音)
* @param notificationManager 通知管理器
*/
private static void createCrashNotifyChannel ( NotificationManager notificationManager ) {
// 仅 Android 8.0+ 执行(避免低版本报错)
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . O ) {
// 构建通知渠道( 指定ID、名称、重要性)
android . app . NotificationChannel channel = new android . app . NotificationChannel (
CRASH_NOTIFY_CHANNEL_ID ,
CRASH_NOTIFY_CHANNEL_NAME ,
NotificationManager . IMPORTANCE_DEFAULT // 重要性:默认(不会弹窗,有声音提示)
) ;
// 可选:设置渠道描述(用户在设置中可见)
channel . setDescription ( " 用于显示应用崩溃信息,帮助定位问题 " ) ;
// 注册通知渠道到系统
notificationManager . createNotificationChannel ( channel ) ;
LogUtils . d ( TAG , " 崩溃通知渠道创建成功: " + CRASH_NOTIFY_CHANNEL_ID ) ;
}
}
/**
* 构建通知点击意图( PendingIntent)
* 点击通知后跳转应用主界面(可根据需求修改为跳转崩溃日志详情页)
* @param context 上下文
* @return 封装好的 PendingIntent( 用于通知点击跳转)
*/
private static PendingIntent getNotificationPendingIntent ( Context context ) {
// 1. 获取应用主界面 Intent( 从包名启动默认 launcher Activity)
Intent launchIntent = context . getPackageManager ( ) . getLaunchIntentForPackage (
context . getPackageName ( )
) ;
if ( launchIntent = = null ) {
// 异常处理:若主界面 Intent 为空,创建空意图(避免崩溃)
launchIntent = new Intent ( ) ;
}
// 2. 构建 PendingIntent( 延迟执行的意图, FLAG_UPDATE_CURRENT 表示更新已存在的意图)
int flags = PendingIntent . FLAG_UPDATE_CURRENT ;
// 适配 Android 12+( API 31+):添加 FLAG_IMMUTABLE 避免安全警告(用常量值替代高版本 API)
if ( Build . VERSION . SDK_INT > = API_LEVEL_ANDROID_12 ) {
flags | = FLAG_IMMUTABLE ;
}
return PendingIntent . getActivity (
context ,
0 , // 请求码(可忽略,用于区分多个 PendingIntent)
launchIntent ,
flags
) ;
}
/**
* 构建通知实例(兼容 Android 4.0+ 所有版本)
* @param context 上下文
* @param title 通知标题
* @param content 通知内容
* @param pendingIntent 通知点击意图
* @return 构建完成的 Notification 对象
*/
private static Notification buildNotification ( Context context , String title , String content , PendingIntent pendingIntent ) {
// 兼容 Android 8.0+: 指定通知渠道ID
Notification . Builder builder = new Notification . Builder ( context ) ;
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . O ) {
builder . setChannelId ( CRASH_NOTIFY_CHANNEL_ID ) ;
}
// 配置通知核心参数
builder
. setSmallIcon ( context . getApplicationInfo ( ) . icon ) // 通知小图标(必需,从应用图标获取)
. setContentTitle ( title ) // 通知标题(应用名称)
. setContentText ( content ) // 通知内容(崩溃日志)
. setContentIntent ( pendingIntent ) // 通知点击意图
. setAutoCancel ( true ) // 点击通知后自动取消
. setWhen ( System . currentTimeMillis ( ) ) // 通知创建时间(当前时间)
. setPriority ( Notification . PRIORITY_DEFAULT ) ; // 通知优先级(默认)
// 可选:长文本适配(当崩溃日志过长时,显示完整文本)
if ( content . length ( ) > 100 ) { // 超过100字符时, 设置长文本样式
Notification . BigTextStyle bigTextStyle = new Notification . BigTextStyle ( ) ;
bigTextStyle . bigText ( content ) ; // 显示完整崩溃日志
builder . setStyle ( bigTextStyle ) ;
}
// 构建通知并返回( getNotification() 兼容低版本, build() 是 Android 4.1+ 方法)
return Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN ? builder . build ( ) : builder . getNotification ( ) ;
}
}