LibAppBase: 添加崩溃通知分享日志功能

- 在崩溃通知中添加"分享日志"按钮,点击可分享崩溃日志
- 新增 ShareLogActivity 窗口类处理分享逻辑
- 崩溃日志先保存到缓存文件,再读取分享
- 移除广播接收器方案,简化实现
- 更新 AndroidManifest 注册新 Activity

修改文件:
- libappbase/src/main/AndroidManifest.xml
- libappbase/src/main/java/.../utils/CrashHandleNotifyUtils.java
- libappbase/src/main/java/.../utils/ShareLogActivity.java (新增)

影响范围: 崩溃通知功能
This commit is contained in:
2026-05-11 20:27:40 +08:00
parent f3114a8121
commit 42112eb677
5 changed files with 286 additions and 72 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Mon May 11 19:10:52 HKT 2026 #Mon May 11 20:19:17 CST 2026
stageCount=8 stageCount=8
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.20 baseVersion=15.20
publishVersion=15.20.7 publishVersion=15.20.7
buildCount=0 buildCount=8
baseBetaVersion=15.20.8 baseBetaVersion=15.20.8

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Mon May 11 19:10:34 HKT 2026 #Mon May 11 20:19:17 CST 2026
stageCount=8 stageCount=8
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.20 baseVersion=15.20
publishVersion=15.20.7 publishVersion=15.20.7
buildCount=0 buildCount=8
baseBetaVersion=15.20.8 baseBetaVersion=15.20.8

View File

@@ -47,6 +47,13 @@
<activity android:name="cc.winboll.studio.libappbase.activities.FTPBackupsActivity"/> <activity android:name="cc.winboll.studio.libappbase.activities.FTPBackupsActivity"/>
<activity
android:name=".utils.ShareLogActivity"
android:label="ShareLogActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@android:style/Theme.NoDisplay"/>
</application> </application>
</manifest> </manifest>

View File

@@ -1,11 +1,11 @@
package cc.winboll.studio.libappbase.utils; package cc.winboll.studio.libappbase.utils;
import android.app.Application;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
@@ -13,13 +13,19 @@ import cc.winboll.studio.libappbase.CrashHandler;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.R; import cc.winboll.studio.libappbase.R;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
/** /**
* 应用崩溃处理通知实用工具集(类库兼容版) * 应用崩溃处理通知实用工具集(类库兼容版)
* 核心功能:作为独立类库使用,发送崩溃通知,点击跳转宿主应用的 GlobalCrashActivity 并传递日志 * 核心功能:作为独立类库使用,发送崩溃通知,点击跳转宿主应用的 GlobalCrashActivity 并传递日志
* 适配说明:移除固定包名依赖,通过外部传入宿主包名,支持任意应用集成使用 * 适配说明:移除固定包名依赖,通过外部传入宿主包名,支持任意应用集成使用
* @Author 豆包&ZhanGSKen<zhangsken@qq.com> * @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @CreateTime 2025/11/29 21:12:00 * @CreateTime 2025/11/29 21:12:00
* @EditTime 2026/05/11 15:38:21 * @EditTime 2026/05/11 21:55:00
*/ */
public class CrashHandleNotifyUtils { public class CrashHandleNotifyUtils {
@@ -35,17 +41,18 @@ public class CrashHandleNotifyUtils {
private static final int API_LEVEL_ANDROID_12 = 31; private static final int API_LEVEL_ANDROID_12 = 31;
/** PendingIntent.FLAG_IMMUTABLE 常量值API 31+ */ /** PendingIntent.FLAG_IMMUTABLE 常量值API 31+ */
private static final int FLAG_IMMUTABLE = 0x00000040; private static final int FLAG_IMMUTABLE = 0x00000040;
/** 通知内容最大行数控制在3行超出部分省略 */
private static final int NOTIFICATION_MAX_LINES = 3;
/** 通知摘要最大长度 */ /** 通知摘要最大长度 */
private static final int SUMMARY_MAX_LENGTH = 200; private static final int SUMMARY_MAX_LENGTH = 200;
/** 展开按钮广播Action */ /** 分享日志请求码 */
private static final String ACTION_EXPAND = "cc.winboll.studio.libappbase.ACTION_EXPAND_CRASH"; private static final int REQUEST_CODE_SHARE_LOG = 0x002;
/** 缓存崩溃日志子目录 */
private static final String CRASH_LOG_CACHE_SUBDIR = "crashnotify";
/** 缓存崩溃日志文件名 */
private static final String CRASH_LOG_CACHE_FILENAME = "crash_log.txt";
private static Context sHostContext = null; // ====================== 静态成员 ======================
private static String sHostPackageName = ""; private static String sHostPackageName = "";
private static String sHostAppName = ""; private static String sCrashLogCacheFilePath = "";
private static Class<?> sReportCrashActivity = null;
// ====================== 正则表达式定义 ====================== // ====================== 正则表达式定义 ======================
private static final String REGEX_EXCEPTION_TYPE = "([\\w.]+Exception|[\\w.]+Error)"; private static final String REGEX_EXCEPTION_TYPE = "([\\w.]+Exception|[\\w.]+Error)";
@@ -61,18 +68,29 @@ public class CrashHandleNotifyUtils {
* @param errorLog 崩溃日志内容 * @param errorLog 崩溃日志内容
* @param reportCrashActivity 崩溃详情跳转Activity类 * @param reportCrashActivity 崩溃详情跳转Activity类
*/ */
public static void handleUncaughtException(final Application hostApp, public static void handleUncaughtException(final android.app.Application hostApp,
final String hostPackageName, final String hostPackageName,
final String errorLog, final String errorLog,
final Class<?> reportCrashActivity) { final Class<?> reportCrashActivity) {
LogUtils.d(TAG, "handleUncaughtException 进入方法"); LogUtils.d(TAG, "handleUncaughtException 进入方法");
// 校验入参
if (hostApp == null || TextUtils.isEmpty(hostPackageName) || TextUtils.isEmpty(errorLog)) { if (hostApp == null || TextUtils.isEmpty(hostPackageName) || TextUtils.isEmpty(errorLog)) {
LogUtils.e(TAG, "handleUncaughtException 参数为空校验不通过"); LogUtils.e(TAG, "handleUncaughtException 参数为空校验不通过");
return; return;
} }
sHostPackageName = hostPackageName;
final String hostAppName = getHostAppName(hostApp, hostPackageName); final String hostAppName = getHostAppName(hostApp, hostPackageName);
sendCrashNotification(hostApp, hostPackageName, hostAppName, errorLog, reportCrashActivity); final String crashLogFilePath = saveCrashLogToCache(hostApp, errorLog);
if (TextUtils.isEmpty(crashLogFilePath)) {
LogUtils.e(TAG, "保存崩溃日志到缓存文件失败");
return;
}
sCrashLogCacheFilePath = crashLogFilePath;
final Intent shareIntent = new Intent(hostApp, ShareLogActivity.class);
shareIntent.putExtra(ShareLogActivity.EXTRA_CRASH_LOG_FILEPATH, crashLogFilePath);
shareIntent.putExtra(ShareLogActivity.EXTRA_CRASH_LOG_SUBJECT, "崩溃日志");
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final PendingIntent sharePendingIntent = createSharePendingIntent(hostApp, shareIntent);
sendCrashNotification(hostApp, hostPackageName, hostAppName, errorLog, reportCrashActivity, sharePendingIntent);
} }
/** /**
@@ -81,9 +99,9 @@ public class CrashHandleNotifyUtils {
* @param intent 携带崩溃信息Intent * @param intent 携带崩溃信息Intent
* @param reportCrashActivity 崩溃详情Activity * @param reportCrashActivity 崩溃详情Activity
*/ */
public static void handleUncaughtException(final Application hostApp, public static void handleUncaughtException(final android.app.Application hostApp,
final Intent intent, final Intent intent,
final Class<?> reportCrashActivity) { final Class<?> reportCrashActivity) {
LogUtils.d(TAG, "handleUncaughtException 重载方法进入"); LogUtils.d(TAG, "handleUncaughtException 重载方法进入");
String hostPackageName = intent.getStringExtra("EXTRA_HOST_PACKAGE_NAME"); String hostPackageName = intent.getStringExtra("EXTRA_HOST_PACKAGE_NAME");
if (TextUtils.isEmpty(hostPackageName)) { if (TextUtils.isEmpty(hostPackageName)) {
@@ -94,6 +112,16 @@ public class CrashHandleNotifyUtils {
handleUncaughtException(hostApp, hostPackageName, errorLog, reportCrashActivity); handleUncaughtException(hostApp, hostPackageName, errorLog, reportCrashActivity);
} }
/**
* 资源释放
* @param hostContext 宿主上下文
*/
public static void release(final Context hostContext) {
LogUtils.d(TAG, "release 执行资源释放");
sHostPackageName = "";
sCrashLogCacheFilePath = "";
}
// ====================== 内部工具方法 ====================== // ====================== 内部工具方法 ======================
/** /**
* 获取宿主应用名称 * 获取宿主应用名称
@@ -104,14 +132,87 @@ public class CrashHandleNotifyUtils {
private static String getHostAppName(final Context hostContext, final String hostPackageName) { private static String getHostAppName(final Context hostContext, final String hostPackageName) {
try { try {
return hostContext.getPackageManager() return hostContext.getPackageManager()
.getApplicationLabel(hostContext.getPackageManager() .getApplicationLabel(hostContext.getPackageManager()
.getApplicationInfo(hostPackageName, 0)).toString(); .getApplicationInfo(hostPackageName, 0)).toString();
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "获取宿主应用名称失败", e); LogUtils.e(TAG, "获取宿主应用名称失败", e);
return "未知应用"; return "未知应用";
} }
} }
/**
* 保存崩溃日志到缓存文件
* @param hostContext 宿主上下文
* @param crashLog 崩溃日志内容
* @return 缓存文件路径,失败返回空字符串
*/
private static String saveCrashLogToCache(final Context hostContext, final String crashLog) {
if (hostContext == null || TextUtils.isEmpty(crashLog)) {
return "";
}
BufferedReader reader = null;
FileOutputStream fos = null;
try {
final File cacheDir = new File(hostContext.getCacheDir(), CRASH_LOG_CACHE_SUBDIR);
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
final File cacheFile = new File(cacheDir, CRASH_LOG_CACHE_FILENAME);
if (cacheFile.exists()) {
cacheFile.delete();
}
cacheFile.createNewFile();
fos = new FileOutputStream(cacheFile);
fos.write(crashLog.getBytes("UTF-8"));
fos.flush();
LogUtils.d(TAG, "saveCrashLogToCache 保存崩溃日志到缓存: " + cacheFile.getAbsolutePath());
return cacheFile.getAbsolutePath();
} catch (Exception e) {
LogUtils.e(TAG, "saveCrashLogToCache 异常", e);
return "";
} finally {
if (reader != null) {
try { reader.close(); } catch (Exception e) {}
}
if (fos != null) {
try { fos.close(); } catch (Exception e) {}
}
}
}
/**
* 读取缓存的崩溃日志文件内容
* @param hostContext 宿主上下文
* @return 日志内容,失败返回空字符串
*/
private static String readCachedCrashLog(final Context hostContext) {
if (hostContext == null || TextUtils.isEmpty(sCrashLogCacheFilePath)) {
return "";
}
BufferedReader reader = null;
try {
final File cacheFile = new File(sCrashLogCacheFilePath);
if (!cacheFile.exists()) {
LogUtils.w(TAG, "readCachedCrashLog 缓存文件不存在");
return "";
}
reader = new BufferedReader(new InputStreamReader(new FileInputStream(cacheFile), "UTF-8"));
final StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
LogUtils.e(TAG, "readCachedCrashLog 异常", e);
return "";
} finally {
if (reader != null) {
try { reader.close(); } catch (Exception e) {}
}
}
}
/** /**
* 发送崩溃系统通知 * 发送崩溃系统通知
* @param hostContext 宿主上下文 * @param hostContext 宿主上下文
@@ -119,45 +220,65 @@ public class CrashHandleNotifyUtils {
* @param hostAppName 宿主应用名 * @param hostAppName 宿主应用名
* @param errorLog 崩溃日志 * @param errorLog 崩溃日志
* @param reportCrashActivity 跳转Activity * @param reportCrashActivity 跳转Activity
* @param sharePendingIntent 分享日志PendingIntent
*/ */
private static void sendCrashNotification(final Context hostContext, private static void sendCrashNotification(final Context hostContext,
final String hostPackageName, final String hostPackageName,
final String hostAppName, final String hostAppName,
final String errorLog, final String errorLog,
final Class<?> reportCrashActivity) { final Class<?> reportCrashActivity,
final PendingIntent sharePendingIntent) {
final NotificationManager notificationManager = final NotificationManager notificationManager =
(NotificationManager) hostContext.getSystemService(Context.NOTIFICATION_SERVICE); (NotificationManager) hostContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) { if (notificationManager == null) {
LogUtils.e(TAG, "获取NotificationManager失败"); LogUtils.e(TAG, "获取NotificationManager失败");
return; return;
} }
// 8.0以上创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createCrashNotifyChannel(hostContext, notificationManager); createCrashNotifyChannel(hostContext, notificationManager);
} }
final PendingIntent jumpIntent = getGlobalCrashPendingIntent(hostContext, final PendingIntent jumpIntent = getGlobalCrashPendingIntent(hostContext,
hostPackageName, errorLog, reportCrashActivity); hostPackageName, errorLog, reportCrashActivity);
if (jumpIntent == null) { if (jumpIntent == null) {
LogUtils.e(TAG, "构建跳转PendingIntent失败"); LogUtils.e(TAG, "构建跳转PendingIntent失败");
return; return;
} }
final Notification notification = buildNotification(hostContext, hostPackageName, hostAppName, errorLog, jumpIntent); final Notification notification = buildNotification(hostContext, hostPackageName, hostAppName, errorLog, jumpIntent, sharePendingIntent);
notificationManager.notify(CRASH_NOTIFY_ID, notification); notificationManager.notify(CRASH_NOTIFY_ID, notification);
LogUtils.d(TAG, "崩溃通知发送成功,宿主包名:" + hostPackageName); LogUtils.d(TAG, "崩溃通知发送成功,宿主包名:" + hostPackageName);
} }
/**
* 创建分享日志PendingIntent
* @param hostContext 宿主上下文
* @param shareIntent 分享意图
* @return PendingIntent实例
*/
private static PendingIntent createSharePendingIntent(final Context hostContext, final Intent shareIntent) {
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= API_LEVEL_ANDROID_12) {
flags |= FLAG_IMMUTABLE;
}
return PendingIntent.getActivity(
hostContext,
REQUEST_CODE_SHARE_LOG,
shareIntent,
flags
);
}
/** /**
* 创建通知渠道适配Android O及以上 * 创建通知渠道适配Android O及以上
* @param hostContext 宿主上下文 * @param hostContext 宿主上下文
* @param notificationManager 通知管理器 * @param notificationManager 通知管理器
*/ */
private static void createCrashNotifyChannel(final Context hostContext, private static void createCrashNotifyChannel(final Context hostContext,
final NotificationManager notificationManager) { final NotificationManager notificationManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
android.app.NotificationChannel channel = new android.app.NotificationChannel( android.app.NotificationChannel channel = new android.app.NotificationChannel(
CRASH_NOTIFY_CHANNEL_ID, CRASH_NOTIFY_CHANNEL_ID,
CRASH_NOTIFY_CHANNEL_NAME, CRASH_NOTIFY_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT NotificationManager.IMPORTANCE_DEFAULT
); );
channel.setDescription("应用崩溃通知(由 WinBoLL Studio 类库提供,点击查看详情)"); channel.setDescription("应用崩溃通知(由 WinBoLL Studio 类库提供,点击查看详情)");
notificationManager.createNotificationChannel(channel); notificationManager.createNotificationChannel(channel);
@@ -174,9 +295,9 @@ private static void sendCrashNotification(final Context hostContext,
* @return PendingIntent实例 * @return PendingIntent实例
*/ */
private static PendingIntent getGlobalCrashPendingIntent(final Context hostContext, private static PendingIntent getGlobalCrashPendingIntent(final Context hostContext,
final String hostPackageName, final String hostPackageName,
final String errorLog, final String errorLog,
final Class<?> reportCrashActivity) { final Class<?> reportCrashActivity) {
try { try {
final Intent crashIntent = new Intent(hostContext, reportCrashActivity); final Intent crashIntent = new Intent(hostContext, reportCrashActivity);
crashIntent.setPackage(hostPackageName); crashIntent.setPackage(hostPackageName);
@@ -187,11 +308,11 @@ private static void sendCrashNotification(final Context hostContext,
if (Build.VERSION.SDK_INT >= API_LEVEL_ANDROID_12) { if (Build.VERSION.SDK_INT >= API_LEVEL_ANDROID_12) {
flags |= FLAG_IMMUTABLE; flags |= FLAG_IMMUTABLE;
} }
return PendingIntent.getActivity( return PendingIntent.getActivity(
hostContext, hostContext,
CRASH_NOTIFY_ID, CRASH_NOTIFY_ID,
crashIntent, crashIntent,
flags flags
); );
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "构建跳转Intent异常", e); LogUtils.e(TAG, "构建跳转Intent异常", e);
@@ -206,32 +327,36 @@ return PendingIntent.getActivity(
* @param hostAppName 宿主应用名 * @param hostAppName 宿主应用名
* @param errorLog 崩溃日志 * @param errorLog 崩溃日志
* @param jumpIntent 点击跳转意图 * @param jumpIntent 点击跳转意图
* @param shareIntent 分享日志意图
* @return 构建好的Notification * @return 构建好的Notification
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private static Notification buildNotification(final Context hostContext, private static Notification buildNotification(final Context hostContext,
final String hostPackageName, final String hostPackageName,
final String hostAppName, final String hostAppName,
final String errorLog, final String errorLog,
final PendingIntent jumpIntent) { final PendingIntent jumpIntent,
final PendingIntent shareIntent) {
Notification.Builder builder = new Notification.Builder(hostContext); Notification.Builder builder = new Notification.Builder(hostContext);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CRASH_NOTIFY_CHANNEL_ID); builder.setChannelId(CRASH_NOTIFY_CHANNEL_ID);
} }
String briefInfo = extractBriefInfo(errorLog); final String briefInfo = extractBriefInfo(errorLog);
Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle(); final Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle();
bigTextStyle.setBigContentTitle(hostAppName + " 崩溃"); bigTextStyle.setBigContentTitle(hostAppName + " 崩溃");
bigTextStyle.bigText(briefInfo); bigTextStyle.bigText(briefInfo);
bigTextStyle.setSummaryText("点击查看详情"); bigTextStyle.setSummaryText("点击查看详情");
builder.setStyle(bigTextStyle); builder.setStyle(bigTextStyle);
builder.setSmallIcon(hostContext.getApplicationInfo().icon) builder.setSmallIcon(hostContext.getApplicationInfo().icon)
.setContentTitle(hostAppName + " 崩溃") .setContentTitle(hostAppName + " 崩溃")
.setContentText(briefInfo.split("\n")[0]) .setContentText(briefInfo.split("\n")[0])
.setContentIntent(jumpIntent) .setContentIntent(jumpIntent)
.setAutoCancel(true) .setAutoCancel(true)
.setWhen(System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_DEFAULT); .setPriority(Notification.PRIORITY_DEFAULT);
if (shareIntent != null) {
builder.addAction(android.R.drawable.ic_menu_send, "分享日志", shareIntent);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return builder.build(); return builder.build();
} else { } else {
@@ -248,10 +373,10 @@ return PendingIntent.getActivity(
if (errorLog == null || errorLog.isEmpty()) { if (errorLog == null || errorLog.isEmpty()) {
return "无崩溃日志"; return "无崩溃日志";
} }
String brief = extractBriefInfo(errorLog); final String brief = extractBriefInfo(errorLog);
String firstLine = brief.split("\n")[0]; final String firstLine = brief.split("\n")[0];
if (firstLine.length() > 80) { if (firstLine.length() > 80) {
firstLine = firstLine.substring(0, 80) + "..."; return firstLine.substring(0, 80) + "...";
} }
return firstLine; return firstLine;
} }
@@ -265,7 +390,7 @@ return PendingIntent.getActivity(
if (crashLog == null || crashLog.isEmpty()) { if (crashLog == null || crashLog.isEmpty()) {
return "无崩溃日志"; return "无崩溃日志";
} }
StringBuilder brief = new StringBuilder(); final StringBuilder brief = new StringBuilder();
try { try {
java.util.regex.Pattern exceptionPattern = java.util.regex.Pattern.compile(REGEX_EXCEPTION_TYPE); java.util.regex.Pattern exceptionPattern = java.util.regex.Pattern.compile(REGEX_EXCEPTION_TYPE);
java.util.regex.Matcher exceptionMatcher = exceptionPattern.matcher(crashLog); java.util.regex.Matcher exceptionMatcher = exceptionPattern.matcher(crashLog);
@@ -315,13 +440,4 @@ return PendingIntent.getActivity(
} }
return brief.toString(); return brief.toString();
} }
}
/**
* 资源释放预留方法
* @param hostContext 宿主上下文
*/
public static void release(final Context hostContext) {
LogUtils.d(TAG, "CrashHandleNotifyUtils 执行资源释放");
}
}

View File

@@ -0,0 +1,91 @@
package cc.winboll.studio.libappbase.utils;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
/**
* 分享崩溃日志窗口类
* @Author ZhanGSKen<zhangsken@qq.com>
* @CreateTime 2026/05/11 22:30:00
*/
public class ShareLogActivity extends Activity {
public static final String TAG = "ShareLogActivity";
public static final String EXTRA_CRASH_LOG_FILEPATH = "crash_log_filepath";
public static final String EXTRA_CRASH_LOG_SUBJECT = "crash_log_subject";
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate 进入方法");
final Intent intent = getIntent();
if (intent == null) {
Log.e(TAG, "onCreate intent 为空");
finish();
return;
}
final String crashLogFilePath = intent.getStringExtra(EXTRA_CRASH_LOG_FILEPATH);
if (crashLogFilePath == null || crashLogFilePath.isEmpty()) {
Log.e(TAG, "onCreate crashLogFilePath 为空");
Toast.makeText(this, "日志文件路径无效", Toast.LENGTH_SHORT).show();
finish();
return;
}
final String subject = intent.getStringExtra(EXTRA_CRASH_LOG_SUBJECT);
handleShareCrashLog(crashLogFilePath, subject);
}
private void handleShareCrashLog(final String crashLogFilePath, final String subject) {
Log.d(TAG, "handleShareCrashLog crashLogFilePath = " + crashLogFilePath);
final File crashLogFile = new File(crashLogFilePath);
if (!crashLogFile.exists()) {
Log.e(TAG, "handleShareCrashLog 文件不存在");
Toast.makeText(this, "日志文件不存在", Toast.LENGTH_SHORT).show();
finish();
return;
}
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(crashLogFile), "UTF-8"));
final StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
final String logContent = sb.toString();
final Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, logContent);
if (subject != null && !subject.isEmpty()) {
shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
} else {
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "崩溃日志");
}
startActivity(Intent.createChooser(shareIntent, "分享日志到"));
Log.d(TAG, "handleShareCrashLog 分享成功");
} catch (Exception e) {
Log.e(TAG, "handleShareCrashLog 异常", e);
Toast.makeText(this, "分享失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
} finally {
if (reader != null) {
try { reader.close(); } catch (Exception e) {}
}
finish();
}
}
}