20260511_152720_122

This commit is contained in:
2026-05-11 15:27:25 +08:00
parent 1274bc7c05
commit 4b2b5acc99
8 changed files with 15 additions and 234 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Mon May 11 14:45:48 HKT 2026
#Mon May 11 07:23:43 GMT 2026
stageCount=6
libraryProject=libappbase
baseVersion=15.20
publishVersion=15.20.5
buildCount=0
buildCount=4
baseBetaVersion=15.20.6

View File

@@ -390,7 +390,7 @@ public final class CrashHandler {
* 检查当前保险丝是否有效(防护未熔断)
* @return true有效等级 1~2false已熔断
*/
boolean isAppCrashSafetyWireOK() {
public boolean isAppCrashSafetyWireOK() {
LogUtils.d(TAG, "isAppCrashSafetyWireOK()");
currentSafetyLevel = loadCurrentSafetyLevel();
return isSafetyWireWorking(currentSafetyLevel);

View File

@@ -1,219 +0,0 @@
package cc.winboll.studio.libappbase;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @CreateTime 2026-05-11 13:28:00
* @EditTime 2026-05-11 13:30:45
* @Describe 应用崩溃保险丝单例管理类
* 限制短时间内应用重复崩溃,通过分级熔断机制控制崩溃页面跳转策略;
* 防护等级区间 1~2每次崩溃自动降级等级溢出则判定为彻底熔断
* 支持本地文件持久化等级、延迟自动恢复、手动重置防护等级能力。
*/
public final class GlobalAppCrashSafetyWire {
/** 日志标识标签 */
public static final String TAG = "GlobalAppCrashSafetyWire";
/** 最低防护等级阈值 */
private static final int _MINI = 1;
/** 最高防护等级阈值 */
private static final int _MAX = 2;
/** 单例实例对象 */
private static volatile GlobalAppCrashSafetyWire _AppCrashSafetyWire;
/** 当前防护熔断等级 */
private volatile Integer currentSafetyLevel;
/**
* 私有构造方法
* 单例禁止外部实例化,初始化加载本地持久化防护等级
*/
private GlobalAppCrashSafetyWire() {
LogUtils.d(TAG, "构造方法执行:初始化崩溃保险丝实例");
currentSafetyLevel = loadCurrentSafetyLevel();
}
/**
* 获取单例对象
* @return GlobalAppCrashSafetyWire 单例
*/
public static synchronized GlobalAppCrashSafetyWire getInstance() {
if (_AppCrashSafetyWire == null) {
_AppCrashSafetyWire = new GlobalAppCrashSafetyWire();
}
return _AppCrashSafetyWire;
}
/**
* 设置当前防护等级
* @param currentSafetyLevel 待设置的防护等级
*/
public void setCurrentSafetyLevel(final int currentSafetyLevel) {
this.currentSafetyLevel = currentSafetyLevel;
LogUtils.d(TAG, "setCurrentSafetyLevel设置等级 = " + currentSafetyLevel);
}
/**
* 获取当前内存中防护等级
* @return 当前防护等级
*/
public int getCurrentSafetyLevel() {
return currentSafetyLevel;
}
/**
* 保存防护等级到本地文件持久化
* @param currentSafetyLevel 待保存防护等级
*/
public void saveCurrentSafetyLevel(final int currentSafetyLevel) {
LogUtils.d(TAG, "saveCurrentSafetyLevel开始持久化等级 = " + currentSafetyLevel);
this.currentSafetyLevel = currentSafetyLevel;
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(CrashHandler._CrashCountFilePath));
oos.writeInt(currentSafetyLevel);
oos.flush();
LogUtils.d(TAG, "saveCurrentSafetyLevel写入文件成功等级 = " + currentSafetyLevel);
} catch (IOException e) {
LogUtils.e(TAG, "saveCurrentSafetyLevel写入文件异常", e);
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
LogUtils.e(TAG, "saveCurrentSafetyLevel关闭流异常", e);
}
}
}
}
/**
* 从本地文件读取防护等级
* @return 加载后的防护等级
*/
public int loadCurrentSafetyLevel() {
LogUtils.d(TAG, "loadCurrentSafetyLevel加载本地防护等级");
File file = new File(CrashHandler._CrashCountFilePath);
if (!file.exists()) {
currentSafetyLevel = _MAX;
LogUtils.d(TAG, "loadCurrentSafetyLevel文件不存在初始化为最高等级 = " + _MAX);
saveCurrentSafetyLevel(currentSafetyLevel);
return currentSafetyLevel;
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(CrashHandler._CrashCountFilePath));
currentSafetyLevel = ois.readInt();
LogUtils.d(TAG, "loadCurrentSafetyLevel读取文件成功当前等级 = " + currentSafetyLevel);
} catch (IOException e) {
LogUtils.e(TAG, "loadCurrentSafetyLevel读取文件异常", e);
currentSafetyLevel = _MAX;
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
LogUtils.e(TAG, "loadCurrentSafetyLevel关闭流异常", e);
}
}
}
return currentSafetyLevel;
}
/**
* 执行一次保险丝熔断降级
* @return 降级后是否仍在有效防护区间
*/
boolean burnSafetyWire() {
LogUtils.d(TAG, "burnSafetyWire执行一次熔断降级");
final int safeLevel = loadCurrentSafetyLevel();
if (isSafetyWireWorking(safeLevel)) {
saveCurrentSafetyLevel(safeLevel - 1);
return isSafetyWireWorking(safeLevel - 1);
}
return false;
}
/**
* 校验指定等级是否在有效防护区间
* @param safetyLevel 待校验防护等级
* @return true=防护有效 false=已熔断
*/
boolean isSafetyWireWorking(final int safetyLevel) {
LogUtils.d(TAG, "isSafetyWireWorking校验等级 = " + safetyLevel);
if (safetyLevel >= _MINI && safetyLevel <= _MAX) {
LogUtils.d(TAG, "isSafetyWireWorking等级在合法区间内");
return true;
}
LogUtils.d(TAG, "isSafetyWireWorking等级超出合法区间已熔断");
return false;
}
/**
* 立即恢复防护等级为最高等级
*/
void resumeToMaximumImmediately() {
LogUtils.d(TAG, "resumeToMaximumImmediately重置为最高防护等级");
GlobalAppCrashSafetyWire.getInstance().saveCurrentSafetyLevel(_MAX);
}
/**
* 关闭防护,设置为临界最低等级
*/
void off() {
LogUtils.d(TAG, "off设置为临界最低防护等级");
saveCurrentSafetyLevel(_MINI);
}
/**
* 检查当前整体保险丝是否处于可用防护状态
* @return true=防护正常 false=已熔断
*/
boolean isAppCrashSafetyWireOK() {
LogUtils.d(TAG, "isAppCrashSafetyWireOK检测当前防护状态");
currentSafetyLevel = loadCurrentSafetyLevel();
return isSafetyWireWorking(currentSafetyLevel);
}
/**
* 延迟异步检测并自动恢复防护等级
* @param context 上下文对象
*/
void postResumeCrashSafetyWireHandler(final Context context) {
LogUtils.d(TAG, "postResumeCrashSafetyWireHandler开启延迟自动恢复任务");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
final int nextLevel = currentSafetyLevel - 1;
if (!GlobalAppCrashSafetyWire.getInstance().isSafetyWireWorking(nextLevel)) {
GlobalAppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
LogUtils.d(TAG, "postResumeCrashSafetyWireHandler临近熔断自动重置最高等级");
}
}
}, 500);
}
public static void testGlobalAppCrashSafetyWire(Context context) {
if (GlobalAppCrashSafetyWire.getInstance().isAppCrashSafetyWireOK()) {
GlobalAppCrashSafetyWire.getInstance().burnSafetyWire();
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
context.getString(i);
}
}
}
}

View File

@@ -43,9 +43,7 @@ implements MenuItem.OnMenuItemClickListener {
LogUtils.d(TAG, "onCreate: 初始化崩溃展示页面");
final Context appContext = getApplicationContext();
GlobalAppCrashSafetyWire.getInstance()
.postResumeCrashSafetyWireHandler(appContext);
mCrashLog = getIntent().getStringExtra(CrashHandler.EXTRA_CRASH_LOG);
setContentView(R.layout.activity_globalcrash);
@@ -96,7 +94,7 @@ implements MenuItem.OnMenuItemClickListener {
copyCrashLogToClipboard();
break;
case MENU_ITEM_RESTART:
GlobalAppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
CrashHandler.AppCrashSafetyWire.getInstance().resumeToMaximumImmediately();
restartApp();
break;
default:

View File

@@ -8,15 +8,10 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.text.TextUtils;
import cc.winboll.studio.libappbase.CrashHandler;
import cc.winboll.studio.libappbase.GlobalCrashActivity;
import cc.winboll.studio.libappbase.LogUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/29 21:12
@@ -173,7 +168,7 @@ public class CrashHandleNotifyUtils {
private static PendingIntent getGlobalCrashPendingIntent(Context hostContext, String hostPackageName, String errorLog) {
try {
// 1. 构建跳转宿主 GlobalCrashActivity 的显式意图(类库场景必须显式指定宿主包名)
Intent crashIntent = new Intent(hostContext, GlobalCrashActivity.class);
Intent crashIntent = new Intent(hostContext, CrashHandler.AppCrashSafetyWire.getInstance().isAppCrashSafetyWireOK()?GlobalCrashActivity.class : CrashHandler.CrashActivity.class);
// 关键:绑定宿主包名,确保意图能正确匹配宿主的 Activity避免类库包名干扰
crashIntent.setPackage(hostPackageName);
// 传递崩溃日志EXTRA_CRASH_INFO与宿主 GlobalCrashActivity 完全匹配)

View File

@@ -12,5 +12,10 @@
android:layout_height="match_parent"
android:id="@+id/activityglobalcrashGlobalCrashReportView1"/>
<Button2
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"/>
</LinearLayout>