广播消息分开为,电量消息与应用配置更新消息。

This commit is contained in:
2025-12-20 15:44:12 +08:00
parent 1d9a03554c
commit e539922478
8 changed files with 341 additions and 232 deletions

View File

@@ -505,7 +505,7 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
// ======================== 消息发送方法 ========================
private void notifyServiceAppConfigChange() {
LogUtils.d(TAG, "notifyServiceAppConfigChange: 通知服务配置变更");
ControlCenterService.updateStatus(this);
ControlCenterService.sendAppConfigStatusUpdateMessage(this);
reloadAppConfig();
}

View File

@@ -26,12 +26,20 @@ public class ControlCenterServiceHandler extends Handler {
private static final int BATTERY_LEVEL_MIN = 0;
private static final int BATTERY_LEVEL_MAX = 100;
// 通知文案常量(抽离魔法值,便于统一修改)
private static final String CHARGE_REMIND_TITLE = "充电提醒";
private static final String USAGE_REMIND_TITLE = "耗电提醒";
private static final String CHARGE_REMIND_CONTENT_FORMAT = "(+) 当前电量%d%%%s已达标建议及时断电保护电池寿命";
private static final String USAGE_REMIND_CONTENT_FORMAT = "(-) 当前电量%d%%%s已偏低建议及时充电避免设备关机";
private static final String CHARGE_STATE_CHARGING = "充电中";
private static final String CHARGE_STATE_NOT_CHARGING = "未充电";
// ================================== 成员变量区弱引用防泄漏final保证不可变=================================
private final WeakReference<ControlCenterService> mwrControlCenterService;
// ================================== 构造方法(强制传入服务,初始化弱引用)=================================
public ControlCenterServiceHandler(ControlCenterService service) {
LogUtils.d(TAG, "ControlCenterServiceHandler: 构造方法执行 | service=" + (service != null ? service.getClass().getSimpleName() : "null"));
LogUtils.d(TAG, "构造方法执行 | service=" + (service != null ? service.getClass().getSimpleName() : "null"));
this.mwrControlCenterService = new WeakReference<>(service);
}
@@ -89,21 +97,21 @@ public class ControlCenterServiceHandler extends Handler {
return;
}
// 2. 构建通知模型,使用String.format统一格式
// 2. 构建通知模型,使用统一格式
NotificationMessage remindMsg = new NotificationMessage();
String chargeStateDesc = isCharging ? "充电中" : "未充电";
String chargeStateDesc = isCharging ? CHARGE_STATE_CHARGING : CHARGE_STATE_NOT_CHARGING;
if (REMIND_TYPE_CHARGE.equals(remindType)) {
remindMsg.setTitle("充电提醒");
remindMsg.setContent(String.format("(+) 当前电量%d%%%s已达标建议及时断电保护电池寿命", currentBattery, chargeStateDesc));
remindMsg.setTitle(CHARGE_REMIND_TITLE);
remindMsg.setContent(String.format(CHARGE_REMIND_CONTENT_FORMAT, currentBattery, chargeStateDesc));
remindMsg.setRemindMSG("charge_remind");
} else {
remindMsg.setTitle("耗电提醒");
remindMsg.setContent(String.format("(-) 当前电量%d%%%s已偏低建议及时充电避免设备关机", currentBattery, chargeStateDesc));
remindMsg.setTitle(USAGE_REMIND_TITLE);
remindMsg.setContent(String.format(USAGE_REMIND_CONTENT_FORMAT, currentBattery, chargeStateDesc));
remindMsg.setRemindMSG("usage_remind");
}
LogUtils.d(TAG, "handleRemindMessage: 通知模型构建完成 | title=" + remindMsg.getTitle() + " | content=" + remindMsg.getContent());
// 3. 直接调用通知工具类发送,不校验返回结果
// 3. 调用通知工具类发送提醒
LogUtils.d(TAG, "handleRemindMessage: 调用通知工具类发送提醒 | remindMSG=" + remindMsg.getRemindMSG());
service.getNotificationManager().showRemindNotification(service, remindMsg);
LogUtils.d(TAG, "handleRemindMessage: 提醒通知发送流程执行完毕");

View File

@@ -14,11 +14,11 @@ import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import java.lang.ref.WeakReference;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/19 20:23
* @Describe 控制中心广播接收器
* 控制中心广播接收器
* 功能:监听电池状态变化、前台通知更新、配置变更指令
* 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/19 20:23
*/
public class ControlCenterServiceReceiver extends BroadcastReceiver {
// ================================== 静态常量区(置顶归类,消除魔法值)=================================
@@ -34,11 +34,13 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
private static final int BATTERY_LEVEL_MIN = 0;
private static final int BATTERY_LEVEL_MAX = 100;
// ================================== 成员变量区(弱引用防泄漏,volatile保线程安全=================================
private WeakReference<ControlCenterService> mwrControlCenterService;
// ================================== 静态状态标记(volatile保证多线程可见性=================================
private static volatile int sLastBatteryLevel = -1; // 上次电量(多线程可见)
private static volatile boolean sIsCharging = false; // 上次充电状态(多线程可见)
// ================================== 成员变量区(弱引用防泄漏,按功能分层)=================================
private WeakReference<ControlCenterService> mwrControlCenterService;
// ================================== 构造方法(初始化弱引用,避免服务强引用泄漏)=================================
public ControlCenterServiceReceiver(ControlCenterService service) {
LogUtils.d(TAG, "构造接收器 | service=" + (service != null ? service.getClass().getSimpleName() : "null"));
@@ -48,18 +50,18 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
// ================================== 广播核心接收逻辑入口方法分Action分发处理=================================
@Override
public void onReceive(Context context, Intent intent) {
LogUtils.d(TAG, "onReceive: 接收广播 | context=" + context + " | intent=" + intent + " | action=" + (intent != null ? intent.getAction() : "null"));
LogUtils.d(TAG, "onReceive: 接收广播 | action=" + (intent != null ? intent.getAction() : "null"));
// 基础参数校验
if (context == null || intent == null || intent.getAction() == null) {
LogUtils.e(TAG, "onReceive: 参数无效,终止处理");
LogUtils.e(TAG, "onReceive: 参数无效context=" + context + " | intent=" + intent + ",终止处理");
return;
}
// 弱引用获取服务,双重校验服务有效性
ControlCenterService service = mwrControlCenterService != null ? mwrControlCenterService.get() : null;
if (service == null || service.isDestroyed()) {
LogUtils.e(TAG, "onReceive: 服务已销毁或为空,注销广播");
LogUtils.e(TAG, "onReceive: 服务已销毁或为空service=" + service + ",注销广播");
unregisterAction(context);
return;
}
@@ -90,7 +92,7 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
* @param intent 电池状态广播意图
*/
private void handleBatteryStateChanged(ControlCenterService service, Intent intent) {
LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态 | service=" + service);
LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态 | service=" + service + " | intent=" + intent);
try {
// 1. 解析并校验当前电池状态
boolean currentCharging = BatteryUtils.isCharging(intent);
@@ -104,19 +106,14 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
return;
}
// 3. 加载最新配置(三级兜底:本地配置→服务内存配置→默认配置)
AppConfigBean latestConfig = AppConfigUtils.getInstance(service).loadAppConfig();
// 3. 同步电池状态到服务,触发线程更新
service.notifyBatteryStateChanged(currentCharging, currentBatteryLevel);
// 4. 同步电池状态到配置,通知服务更新线程
latestConfig.setCurrentBatteryValue(currentBatteryLevel);
latestConfig.setIsCharging(currentCharging);
service.notifyAppConfigUpdate(latestConfig);
// 5. 更新静态缓存状态,保证多线程可见
// 4. 更新静态缓存状态,保证多线程可见
sIsCharging = currentCharging;
sLastBatteryLevel = currentBatteryLevel;
LogUtils.d(TAG, "handleBatteryStateChanged: 电池状态处理成功");
LogUtils.d(TAG, "handleBatteryStateChanged: 电池状态处理成功 | 缓存电量=" + sLastBatteryLevel + "% | 缓存充电状态=" + sIsCharging);
} catch (Exception e) {
LogUtils.e(TAG, "handleBatteryStateChanged: 处理失败", e);
}
@@ -131,7 +128,8 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
try {
// 加载最新配置
AppConfigBean latestConfig = AppConfigUtils.getInstance(service).loadAppConfig();
LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 加载最新配置 | 充电阈值=" + latestConfig.getChargeReminderValue() + " | 耗电阈值=" + latestConfig.getUsageReminderValue());
// 同步缓存的电池状态到配置
latestConfig.setCurrentBatteryValue(sLastBatteryLevel);
latestConfig.setIsCharging(sIsCharging);
@@ -155,18 +153,17 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
// 非空校验,避免空指针
if (notifyUtils == null || notifyMsg == null) {
LogUtils.e(TAG, "handleUpdateForegroundNotification: 通知工具类或消息为空");
LogUtils.e(TAG, "handleUpdateForegroundNotification: 通知工具类或消息为空notifyUtils=" + notifyUtils + " | notifyMsg=" + notifyMsg + "");
return;
}
notifyUtils.updateForegroundServiceNotify(notifyMsg);
LogUtils.d(TAG, "handleUpdateForegroundNotification: 前台通知更新成功");
LogUtils.d(TAG, "handleUpdateForegroundNotification: 前台通知更新成功 | 通知标题=" + notifyMsg.getTitle());
} catch (Exception e) {
LogUtils.e(TAG, "handleUpdateForegroundNotification: 处理失败", e);
}
}
// ================================== 广播注册/注销(强化容错,避免重复操作)=================================
/**
* 注册广播接收器
@@ -190,7 +187,7 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
filter.setPriority(BROADCAST_PRIORITY);
context.registerReceiver(this, filter);
LogUtils.d(TAG, "registerAction: 广播注册成功");
LogUtils.d(TAG, "registerAction: 广播注册成功 | 优先级=" + BROADCAST_PRIORITY);
} catch (Exception e) {
LogUtils.e(TAG, "registerAction: 注册失败", e);
}
@@ -227,11 +224,12 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
if (mwrControlCenterService != null) {
mwrControlCenterService.clear();
mwrControlCenterService = null;
LogUtils.d(TAG, "release: 弱引用已清空");
}
// 重置静态状态缓存
sLastBatteryLevel = -1;
sIsCharging = false;
LogUtils.d(TAG, "release: 资源释放完成");
LogUtils.d(TAG, "release: 静态状态缓存已重置");
}
/**

View File

@@ -22,11 +22,35 @@ public class AssistantService extends Service {
private static final String TAG = "AssistantService";
// 服务返回策略常量(统一定义,避免魔法值)
private static final int SERVICE_RETURN_STICKY = START_STICKY;
// 服务绑定标记常量
private static final int BIND_FLAG = Context.BIND_IMPORTANT;
// ================================== 成员变量区按功能分层volatile保证多线程可见性=================================
private AppConfigUtils mAppConfigUtils;
private MyServiceConnection mMyServiceConnection;
private volatile boolean mIsThreadAlive;
private AppConfigUtils mAppConfigUtils;
// ================================== 内部类(服务连接状态监听,前置定义便于引用)=================================
/**
* 服务连接状态监听器
* 主服务连接成功时记录状态,断开时自动重连
*/
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.d(TAG, "onServiceConnected: 主服务连接成功 | 组件名=" + name.getClassName() + " | Binder=" + service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.d(TAG, "onServiceDisconnected: 主服务连接断开 | 组件名=" + name.getClassName());
// 主服务断开且配置启用时,重新唤醒绑定
if (mAppConfigUtils != null && mAppConfigUtils.isServiceEnabled()) {
LogUtils.d(TAG, "onServiceDisconnected: 配置启用,尝试重新唤醒并绑定主服务");
wakeupAndBindMain();
}
}
}
// ================================== 服务生命周期方法按执行顺序排列onCreate→onStartCommand→onBind→onDestroy=================================
@Override
@@ -56,7 +80,7 @@ public class AssistantService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand: 守护服务触发重启 | intent=" + intent + " | flags=" + flags + " | startId=" + startId);
LogUtils.d(TAG, "onStartCommand: 守护服务触发重启 | flags=" + flags + " | startId=" + startId);
// 配置工具类为空时,直接返回非粘性策略
if (mAppConfigUtils == null) {
LogUtils.e(TAG, "onStartCommand: AppConfigUtils未初始化终止服务");
@@ -85,15 +109,7 @@ public class AssistantService extends Service {
mIsThreadAlive = false;
// 解绑主服务,添加异常捕获防止重复解绑崩溃
if (mMyServiceConnection != null) {
try {
unbindService(mMyServiceConnection);
LogUtils.d(TAG, "onDestroy: 已成功解绑ControlCenterService");
} catch (IllegalArgumentException e) {
LogUtils.w(TAG, "onDestroy: 解绑服务失败,服务未绑定 | " + e.getMessage());
}
mMyServiceConnection = null;
}
unbindMainService();
// 置空工具类引用帮助GC回收
mAppConfigUtils = null;
@@ -142,29 +158,23 @@ public class AssistantService extends Service {
// 绑定主服务,监听连接状态,添加结果日志
Intent bindIntent = new Intent(AssistantService.this, ControlCenterService.class);
boolean bindResult = bindService(bindIntent, mMyServiceConnection, Context.BIND_IMPORTANT);
boolean bindResult = bindService(bindIntent, mMyServiceConnection, BIND_FLAG);
LogUtils.d(TAG, "wakeupAndBindMain: 绑定主服务结果=" + bindResult + " | 绑定标记=BIND_IMPORTANT");
}
// ================================== 内部类(服务连接状态监听=================================
// ================================== 辅助工具方法(拆分独立逻辑,提高可维护性=================================
/**
* 服务连接状态监听器
* 主服务连接成功时记录状态,断开时自动重连
* 解绑主服务,包含异常捕获与状态日志
*/
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.d(TAG, "onServiceConnected: 主服务连接成功 | 组件名=" + name.getClassName() + " | Binder=" + service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.d(TAG, "onServiceDisconnected: 主服务连接断开 | 组件名=" + name.getClassName());
// 主服务断开且配置启用时,重新唤醒绑定
if (mAppConfigUtils != null && mAppConfigUtils.isServiceEnabled()) {
LogUtils.d(TAG, "onServiceDisconnected: 尝试重新唤醒并绑定主服务");
wakeupAndBindMain();
private void unbindMainService() {
if (mMyServiceConnection != null) {
try {
unbindService(mMyServiceConnection);
LogUtils.d(TAG, "unbindMainService: 已成功解绑ControlCenterService");
} catch (IllegalArgumentException e) {
LogUtils.w(TAG, "unbindMainService: 解绑服务失败,服务未绑定 | " + e.getMessage());
}
mMyServiceConnection = null;
}
}
}

View File

@@ -28,68 +28,65 @@ import java.util.List;
public class ControlCenterService extends Service {
// ================================== 静态常量区(置顶归类,消除魔法值)=================================
public static final String TAG = "ControlCenterService";
// 超时/阈值常量
private static final long THREAD_STOP_TIMEOUT = 1000L;
// 服务返回策略常量
private static final int SERVICE_RETURN_STICKY = START_STICKY;
private static final int DEFAULT_CHARGE_REMINDER_VALUE = 80;
private static final int DEFAULT_USAGE_REMINDER_VALUE = 20;
private static final int DEFAULT_BATTERY_DETECT_INTERVAL = 1000;
private static final int RUNNING_SERVICE_LIST_LIMIT = 100;
// ================================== 成员变量区(按功能分层,volatile保证多线程可见性=================================
// 服务控制配置
private ControlCenterServiceBean mServiceControlBean;
// 业务核心组件
private ControlCenterServiceHandler mServiceHandler;
private ControlCenterServiceReceiver mControlCenterServiceReceiver;
private NotificationManagerUtils mNotificationManager;
private AppConfigBean mCurrentConfigBean;
private NotificationMessage mForegroundNotifyMsg;
// 服务状态标记volatile保证多线程可见性
// ================================== 静态状态标记(volatile保证多线程可见性=================================
private static volatile boolean isServiceRunning = false;
private static volatile boolean mIsDestroyed = true;
// ================================== 服务生命周期方法按执行顺序排列onCreate→onStartCommand→onBind→onDestroy=================================
// ================================== 成员变量区(按功能分层:配置→核心组件→通知相关=================================
// 服务控制配置
private ControlCenterServiceBean mServiceControlBean;
private AppConfigBean mCurrentConfigBean;
// 业务核心组件
private ControlCenterServiceHandler mServiceHandler;
private ControlCenterServiceReceiver mControlCenterServiceReceiver;
// 通知相关
private NotificationManagerUtils mNotificationManager;
private NotificationMessage mForegroundNotifyMsg;
// ================================== 服务生命周期方法按执行顺序onCreate→onStartCommand→onBind→onDestroy=================================
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate: 服务启动 | 线程=" + Thread.currentThread().getName() + " | 进程ID=" + android.os.Process.myPid());
LogUtils.d(TAG, "onCreate执行 | 线程=" + Thread.currentThread().getName() + " | 进程ID=" + android.os.Process.myPid());
runCoreServiceLogic();
LogUtils.d(TAG, "onCreate: 服务初始化完成 | 前台状态=" + isServiceRunning + " | 服务启用=" + (mServiceControlBean != null && mServiceControlBean.isEnableService()));
LogUtils.d(TAG, "onCreate完成 | 前台状态=" + isServiceRunning + " | 服务启用=" + (mServiceControlBean != null && mServiceControlBean.isEnableService()));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand: 触发 | intent=" + intent + " | flags=" + flags + " | startId=" + startId);
LogUtils.d(TAG, "onStartCommand: action=" + (intent != null ? intent.getAction() : "null"));
LogUtils.d(TAG, "onStartCommand执行 | startId=" + startId + " | action=" + (intent != null ? intent.getAction() : "null"));
loadLatestServiceControlConfig();
runCoreServiceLogic();
int returnFlag = (mServiceControlBean != null && mServiceControlBean.isEnableService())
? SERVICE_RETURN_STICKY
: super.onStartCommand(intent, flags, startId);
LogUtils.d(TAG, "onStartCommand: 处理完成 | 返回策略=" + (returnFlag == SERVICE_RETURN_STICKY ? "START_STICKY" : "DEFAULT"));
LogUtils.d(TAG, "onStartCommand完成 | 返回策略=" + (returnFlag == SERVICE_RETURN_STICKY ? "START_STICKY" : "DEFAULT"));
return returnFlag;
}
@Override
public IBinder onBind(Intent intent) {
LogUtils.d(TAG, "onBind: 绑定请求 | intent=" + intent);
LogUtils.d(TAG, "onBind执行 | intent=" + intent);
return null;
}
@Override
public void onDestroy() {
LogUtils.d(TAG, "onDestroy: 服务销毁流程启动");
LogUtils.d(TAG, "onDestroy执行:服务销毁流程启动");
super.onDestroy();
// 资源释放顺序:前台服务 → 线程 → 广播接收器 → Handler → 通知 → 引用(避免内存泄漏)
stopForegroundService();
RemindThread.stopRemindThreadSafely();
if (mControlCenterServiceReceiver != null) {
mControlCenterServiceReceiver.release();
LogUtils.d(TAG, "onDestroy: 广播接收器已释放");
}
releaseBroadcastReceiver();
destroyHandler();
releaseNotificationResource();
clearAllReferences();
@@ -101,7 +98,7 @@ public class ControlCenterService extends Service {
isServiceRunning = false;
mIsDestroyed = true;
LogUtils.d(TAG, "onDestroy: 服务销毁完成");
LogUtils.d(TAG, "onDestroy完成:服务销毁完成");
}
// ================================== 核心业务逻辑(独立抽取,统一调用)=================================
@@ -110,11 +107,11 @@ public class ControlCenterService extends Service {
* 避免重复初始化,保证前台服务优先启动
*/
private synchronized void runCoreServiceLogic() {
LogUtils.d(TAG, "runCoreServiceLogic: 执行核心逻辑");
LogUtils.d(TAG, "runCoreServiceLogic执行");
loadLatestServiceControlConfig();
boolean serviceEnabled = mServiceControlBean != null && mServiceControlBean.isEnableService();
LogUtils.d(TAG, "runCoreServiceLogic: 服务启用=" + serviceEnabled + " | 已运行=" + isServiceRunning + " | 已销毁=" + mIsDestroyed);
LogUtils.d(TAG, "runCoreServiceLogic服务启用=" + serviceEnabled + " | 已运行=" + isServiceRunning + " | 已销毁=" + mIsDestroyed);
if (serviceEnabled && !isServiceRunning) {
isServiceRunning = true;
@@ -123,14 +120,14 @@ public class ControlCenterService extends Service {
if (initForegroundNotificationImmediately()) {
loadDefaultConfig();
initServiceBusinessLogic();
LogUtils.d(TAG, "runCoreServiceLogic: 核心组件初始化成功");
LogUtils.d(TAG, "runCoreServiceLogic核心组件初始化成功");
} else {
LogUtils.e(TAG, "runCoreServiceLogic: 前台通知初始化失败,终止业务");
LogUtils.e(TAG, "runCoreServiceLogic前台通知初始化失败,终止业务");
stopForegroundService();
isServiceRunning = false;
}
} else {
LogUtils.d(TAG, "runCoreServiceLogic: 无需执行核心逻辑");
LogUtils.d(TAG, "runCoreServiceLogic无需执行核心逻辑");
}
}
@@ -140,11 +137,11 @@ public class ControlCenterService extends Service {
* @return true=成功 false=失败
*/
private boolean initForegroundNotificationImmediately() {
LogUtils.d(TAG, "initForegroundNotificationImmediately: 前台通知初始化");
LogUtils.d(TAG, "initForegroundNotificationImmediately执行");
try {
if (mNotificationManager == null) {
mNotificationManager = new NotificationManagerUtils(this);
LogUtils.d(TAG, "initForegroundNotificationImmediately: 通知工具类初始化完成");
LogUtils.d(TAG, "initForegroundNotificationImmediately通知工具类初始化完成");
}
if (mForegroundNotifyMsg == null) {
@@ -152,15 +149,15 @@ public class ControlCenterService extends Service {
mForegroundNotifyMsg.setTitle("电池监测服务");
mForegroundNotifyMsg.setContent("后台运行中");
mForegroundNotifyMsg.setRemindMSG("service_running");
LogUtils.d(TAG, "initForegroundNotificationImmediately: 通知消息构建完成");
LogUtils.d(TAG, "initForegroundNotificationImmediately通知消息构建完成");
}
mNotificationManager.startForegroundServiceNotify(this, mForegroundNotifyMsg);
ToastUtils.show("电池监测服务已启动");
LogUtils.d(TAG, "initForegroundNotificationImmediately: 前台通知发送成功 | ID=" + NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE);
LogUtils.d(TAG, "initForegroundNotificationImmediately前台通知发送成功 | ID=" + NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE);
return true;
} catch (Exception e) {
LogUtils.e(TAG, "initForegroundNotificationImmediately: 通知初始化异常", e);
LogUtils.e(TAG, "initForegroundNotificationImmediately通知初始化异常", e);
return false;
}
}
@@ -169,12 +166,12 @@ public class ControlCenterService extends Service {
* 停止前台服务并取消通知
*/
private void stopForegroundService() {
LogUtils.d(TAG, "stopForegroundService: 停止前台服务");
LogUtils.d(TAG, "stopForegroundService执行");
try {
stopForeground(true);
LogUtils.d(TAG, "stopForegroundService: 前台服务已停止,通知已取消");
LogUtils.d(TAG, "stopForegroundService前台服务已停止,通知已取消");
} catch (Exception e) {
LogUtils.e(TAG, "stopForegroundService: 停止异常", e);
LogUtils.e(TAG, "stopForegroundService停止异常", e);
}
}
@@ -183,13 +180,13 @@ public class ControlCenterService extends Service {
* 加载本地最新服务控制配置
*/
private void loadLatestServiceControlConfig() {
LogUtils.d(TAG, "loadLatestServiceControlConfig: 读取服务配置");
LogUtils.d(TAG, "loadLatestServiceControlConfig执行");
ControlCenterServiceBean latestBean = ControlCenterServiceBean.loadBean(this, ControlCenterServiceBean.class);
if (latestBean != null) {
mServiceControlBean = latestBean;
LogUtils.d(TAG, "loadLatestServiceControlConfig: 配置读取成功 | 启用=" + mServiceControlBean.isEnableService());
LogUtils.d(TAG, "loadLatestServiceControlConfig配置读取成功 | 启用=" + mServiceControlBean.isEnableService());
} else {
LogUtils.w(TAG, "loadLatestServiceControlConfig: 本地无配置,沿用内存配置");
LogUtils.w(TAG, "loadLatestServiceControlConfig本地无配置,沿用内存配置");
}
}
@@ -197,40 +194,54 @@ public class ControlCenterService extends Service {
* 加载默认业务配置(首次启动兜底)
*/
private void loadDefaultConfig() {
LogUtils.d(TAG, "loadDefaultConfig: 加载默认配置");
LogUtils.d(TAG, "loadDefaultConfig执行");
if (mCurrentConfigBean == null) {
mCurrentConfigBean = new AppConfigBean();
mCurrentConfigBean.setEnableChargeReminder(true);
mCurrentConfigBean.setChargeReminderValue(80);
mCurrentConfigBean.setChargeReminderValue(DEFAULT_CHARGE_REMINDER_VALUE);
mCurrentConfigBean.setEnableUsageReminder(true);
mCurrentConfigBean.setUsageReminderValue(20);
mCurrentConfigBean.setBatteryDetectInterval(1000);
LogUtils.d(TAG, "loadDefaultConfig: 默认配置加载完成 | 充电阈值=80 | 耗电阈值=20 | 检测间隔=1000ms");
mCurrentConfigBean.setUsageReminderValue(DEFAULT_USAGE_REMINDER_VALUE);
mCurrentConfigBean.setBatteryDetectInterval(DEFAULT_BATTERY_DETECT_INTERVAL);
LogUtils.d(TAG, "loadDefaultConfig默认配置加载完成 | 充电阈值=" + DEFAULT_CHARGE_REMINDER_VALUE + " | 耗电阈值=" + DEFAULT_USAGE_REMINDER_VALUE + " | 检测间隔=" + DEFAULT_BATTERY_DETECT_INTERVAL + "ms");
} else {
LogUtils.d(TAG, "loadDefaultConfig: 内存已有配置,无需加载");
LogUtils.d(TAG, "loadDefaultConfig内存已有配置,无需加载");
}
}
// ================================== 业务组件初始化与销毁Handler/线程等)=================================
// ================================== 业务组件初始化与销毁Handler/广播/线程等)=================================
/**
* 初始化Handler等核心业务组件
*/
private void initServiceBusinessLogic() {
LogUtils.d(TAG, "initServiceBusinessLogic: 初始化业务组件");
LogUtils.d(TAG, "initServiceBusinessLogic执行");
// 初始化Handler
if (mServiceHandler == null) {
mServiceHandler = new ControlCenterServiceHandler(this);
LogUtils.d(TAG, "initServiceBusinessLogic: Handler初始化完成");
LogUtils.d(TAG, "initServiceBusinessLogicHandler初始化完成");
} else {
LogUtils.d(TAG, "initServiceBusinessLogic: Handler已存在");
LogUtils.d(TAG, "initServiceBusinessLogicHandler已存在");
}
// 初始化广播接收器
if (mControlCenterServiceReceiver == null) {
mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this);
mControlCenterServiceReceiver.registerAction(this);
LogUtils.d(TAG, "initServiceBusinessLogic: 广播接收器初始化并注册完成");
LogUtils.d(TAG, "initServiceBusinessLogic广播接收器初始化并注册完成 | 接收器=" + mControlCenterServiceReceiver);
} else {
LogUtils.d(TAG, "initServiceBusinessLogic: 广播接收器已存在");
LogUtils.d(TAG, "initServiceBusinessLogic广播接收器已存在");
}
}
/**
* 释放广播接收器资源
*/
private void releaseBroadcastReceiver() {
LogUtils.d(TAG, "releaseBroadcastReceiver执行");
if (mControlCenterServiceReceiver != null) {
mControlCenterServiceReceiver.release();
mControlCenterServiceReceiver = null;
LogUtils.d(TAG, "releaseBroadcastReceiver广播接收器已释放");
} else {
LogUtils.w(TAG, "releaseBroadcastReceiver广播接收器实例为空");
}
}
@@ -238,13 +249,13 @@ public class ControlCenterService extends Service {
* 销毁Handler移除所有消息和回调防止内存泄漏
*/
private void destroyHandler() {
LogUtils.d(TAG, "destroyHandler: 销毁Handler");
LogUtils.d(TAG, "destroyHandler执行");
if (mServiceHandler != null) {
mServiceHandler.removeCallbacksAndMessages(null);
mServiceHandler = null;
LogUtils.d(TAG, "destroyHandler: Handler已销毁");
LogUtils.d(TAG, "destroyHandlerHandler已销毁");
} else {
LogUtils.w(TAG, "destroyHandler: Handler实例为空");
LogUtils.w(TAG, "destroyHandlerHandler实例为空");
}
}
@@ -252,13 +263,13 @@ public class ControlCenterService extends Service {
* 释放通知工具类资源
*/
private void releaseNotificationResource() {
LogUtils.d(TAG, "releaseNotificationResource: 释放通知资源");
LogUtils.d(TAG, "releaseNotificationResource执行");
if (mNotificationManager != null) {
mNotificationManager.release();
mNotificationManager = null;
LogUtils.d(TAG, "releaseNotificationResource: 通知资源已释放");
LogUtils.d(TAG, "releaseNotificationResource通知资源已释放");
} else {
LogUtils.w(TAG, "releaseNotificationResource: 通知工具类实例为空");
LogUtils.w(TAG, "releaseNotificationResource通知工具类实例为空");
}
}
@@ -266,10 +277,10 @@ public class ControlCenterService extends Service {
* 置空所有引用,防止内存泄漏
*/
private void clearAllReferences() {
LogUtils.d(TAG, "clearAllReferences: 清理内存引用");
LogUtils.d(TAG, "clearAllReferences执行");
mForegroundNotifyMsg = null;
mServiceControlBean = null;
LogUtils.d(TAG, "clearAllReferences: 引用清理完成");
LogUtils.d(TAG, "clearAllReferences引用清理完成");
}
// ================================== 外部调用接口(静态方法,提供服务启停/配置更新入口)=================================
@@ -278,25 +289,25 @@ public class ControlCenterService extends Service {
* @param context 上下文
*/
public static void startControlCenterService(Context context) {
LogUtils.d(TAG, "startControlCenterService: 外部启动服务 | context=" + context);
LogUtils.d(TAG, "startControlCenterService执行 | context=" + context);
if (context == null) {
LogUtils.e(TAG, "startControlCenterService: Context为空启动失败");
LogUtils.e(TAG, "startControlCenterServiceContext为空启动失败");
return;
}
// 保存启用配置
ControlCenterServiceBean controlBean = new ControlCenterServiceBean(true);
ControlCenterServiceBean.saveBean(context, controlBean);
LogUtils.d(TAG, "startControlCenterService: 服务启用配置已保存");
LogUtils.d(TAG, "startControlCenterService服务启用配置已保存 | 配置=" + controlBean);
// 启动服务区分API版本
Intent intent = new Intent(context, ControlCenterService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
LogUtils.d(TAG, "startControlCenterService: 以前台服务方式启动API26+");
LogUtils.d(TAG, "startControlCenterService以前台服务方式启动API26+");
} else {
context.startService(intent);
LogUtils.d(TAG, "startControlCenterService: 以普通服务方式启动API26-");
LogUtils.d(TAG, "startControlCenterService以普通服务方式启动API26-");
}
}
@@ -305,39 +316,39 @@ public class ControlCenterService extends Service {
* @param context 上下文
*/
public static void stopControlCenterService(Context context) {
LogUtils.d(TAG, "stopControlCenterService: 外部停止服务 | context=" + context);
LogUtils.d(TAG, "stopControlCenterService执行 | context=" + context);
if (context == null) {
LogUtils.e(TAG, "stopControlCenterService: Context为空停止失败");
LogUtils.e(TAG, "stopControlCenterServiceContext为空停止失败");
return;
}
// 保存停用配置
ControlCenterServiceBean controlBean = new ControlCenterServiceBean(false);
ControlCenterServiceBean.saveBean(context, controlBean);
LogUtils.d(TAG, "stopControlCenterService: 服务停用配置已保存");
LogUtils.d(TAG, "stopControlCenterService服务停用配置已保存 | 配置=" + controlBean);
// 停止服务
Intent intent = new Intent(context, ControlCenterService.class);
context.stopService(intent);
LogUtils.d(TAG, "stopControlCenterService: 停止指令已发送");
LogUtils.d(TAG, "stopControlCenterService停止指令已发送");
}
/**
* 外部更新配置并触发线程重启
* @param context 上下文
*/
public static void updateStatus(Context context) {
LogUtils.d(TAG, "updateStatus: 外部更新配置 | context=" + context);
public static void sendAppConfigStatusUpdateMessage(Context context) {
LogUtils.d(TAG, "sendAppConfigStatusUpdateMessage执行 | context=" + context);
if (context == null) {
LogUtils.e(TAG, "updateStatus: 参数为空,更新失败");
LogUtils.e(TAG, "sendAppConfigStatusUpdateMessage参数为空,更新失败");
return;
}
Intent intent = new Intent(context, ControlCenterService.class);
Intent intent = new Intent(context, ControlCenterServiceReceiver.class);
intent.setAction(ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED);
intent.setPackage(context.getPackageName());
context.sendBroadcast(intent);
LogUtils.d(TAG, "updateStatus: 配置更新广播已发送 | action=" + ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED);
LogUtils.d(TAG, "sendAppConfigStatusUpdateMessage配置更新广播已发送 | action=" + ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED);
}
/**
@@ -345,28 +356,28 @@ public class ControlCenterService extends Service {
* @param context 上下文
*/
public static void checkIgnoreBatteryOptimization(Context context) {
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 检查电池优化 | context=" + context);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization执行 | context=" + context);
if (context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
LogUtils.w(TAG, "checkIgnoreBatteryOptimization: 无需检查Context为空或API<23");
LogUtils.w(TAG, "checkIgnoreBatteryOptimization无需检查Context为空或API<23");
return;
}
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (powerManager == null) {
LogUtils.e(TAG, "checkIgnoreBatteryOptimization: PowerManager获取失败");
LogUtils.e(TAG, "checkIgnoreBatteryOptimizationPowerManager获取失败");
return;
}
String packageName = context.getPackageName();
boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(packageName);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已忽略电池优化=" + isIgnored);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization已忽略电池优化=" + isIgnored);
if (!isIgnored) {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已跳转至系统设置页 | package=" + packageName);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization已跳转至系统设置页 | package=" + packageName);
}
}
@@ -377,15 +388,15 @@ public class ControlCenterService extends Service {
* @return true=运行中 false=未运行
*/
private static boolean isServiceRunning(Context context, Class<?> serviceClass) {
LogUtils.d(TAG, "isServiceRunning: 检查服务状态 | context=" + context + " | service=" + (serviceClass != null ? serviceClass.getName() : "null"));
LogUtils.d(TAG, "isServiceRunning执行 | context=" + context + " | service=" + (serviceClass != null ? serviceClass.getName() : "null"));
if (context == null || serviceClass == null) {
LogUtils.e(TAG, "isServiceRunning: 参数为空");
LogUtils.e(TAG, "isServiceRunning参数为空");
return false;
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
LogUtils.e(TAG, "isServiceRunning: ActivityManager获取失败");
LogUtils.e(TAG, "isServiceRunningActivityManager获取失败");
return false;
}
@@ -406,10 +417,10 @@ public class ControlCenterService extends Service {
}
}
}
LogUtils.d(TAG, "isServiceRunning: API30+ 判断结果=" + isRunning);
LogUtils.d(TAG, "isServiceRunningAPI30+ 判断结果=" + isRunning);
} else {
// API30- 通过服务列表判断
List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(100);
List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(RUNNING_SERVICE_LIST_LIMIT);
if (services != null) {
for (ActivityManager.RunningServiceInfo info : services) {
if (serviceClassName.equals(info.service.getClassName())) {
@@ -418,13 +429,13 @@ public class ControlCenterService extends Service {
}
}
}
LogUtils.d(TAG, "isServiceRunning: API30- 判断结果=" + isRunning);
LogUtils.d(TAG, "isServiceRunningAPI30- 判断结果=" + isRunning);
}
// 兜底判断:配置启用状态
if (!isRunning) {
isRunning = isServiceStarted(context, serviceClass);
LogUtils.d(TAG, "isServiceRunning: 兜底判断结果=" + isRunning);
LogUtils.d(TAG, "isServiceRunning兜底判断结果=" + isRunning);
}
return isRunning;
}
@@ -433,29 +444,44 @@ public class ControlCenterService extends Service {
* 兜底判断服务是否已启动(通过配置文件)
*/
private static boolean isServiceStarted(Context context, Class<?> serviceClass) {
LogUtils.d(TAG, "isServiceStarted: 兜底判断服务状态");
LogUtils.d(TAG, "isServiceStarted执行");
try {
ControlCenterServiceBean controlBean = ControlCenterServiceBean.loadBean(context, ControlCenterServiceBean.class);
return controlBean != null && controlBean.isEnableService();
} catch (Exception e) {
LogUtils.e(TAG, "isServiceStarted: 兜底判断异常", e);
LogUtils.e(TAG, "isServiceStarted兜底判断异常", e);
return false;
}
}
// ================================== 业务方法(配置更新)=================================
// ================================== 业务方法(配置更新/电池状态回调=================================
/**
* 接收外部配置更新,同步到提醒线程
* @param latestConfig 最新配置
*/
public void notifyAppConfigUpdate(AppConfigBean latestConfig) {
LogUtils.d(TAG, "notifyAppConfigUpdate: 配置更新 | config=" + latestConfig);
LogUtils.d(TAG, "notifyAppConfigUpdate执行 | 充电阈值=" + (latestConfig != null ? latestConfig.getChargeReminderValue() : null) + " | 耗电阈值=" + (latestConfig != null ? latestConfig.getUsageReminderValue() : null));
if (latestConfig != null && mServiceHandler != null) {
mCurrentConfigBean = latestConfig;
RemindThread.startRemindThread(this, mServiceHandler, latestConfig);
LogUtils.d(TAG, "notifyAppConfigUpdate: 配置已同步到提醒线程 | 充电阈值=" + latestConfig.getChargeReminderValue() + " | 耗电阈值=" + latestConfig.getUsageReminderValue());
RemindThread.startRemindThreadWithAppConfig(this, mServiceHandler, latestConfig);
LogUtils.d(TAG, "notifyAppConfigUpdate配置已同步到提醒线程");
} else {
LogUtils.e(TAG, "notifyAppConfigUpdate: 参数为空,同步失败 | latestConfig=" + latestConfig + " | mServiceHandler=" + mServiceHandler);
LogUtils.e(TAG, "notifyAppConfigUpdate参数为空,同步失败 | latestConfig=" + latestConfig + " | mServiceHandler=" + mServiceHandler);
}
}
/**
* 接收电池状态变化,同步到提醒线程
* @param isCharging 是否充电中
* @param lastBatteryLevel 最新电池电量
*/
public void notifyBatteryStateChanged(boolean isCharging, int lastBatteryLevel) {
LogUtils.d(TAG, "notifyBatteryStateChanged执行 | 充电中=" + isCharging + " | 电量=" + lastBatteryLevel);
if (mServiceHandler != null) {
RemindThread.startRemindThreadWithBatteryInfo(this, mServiceHandler, isCharging, lastBatteryLevel);
LogUtils.d(TAG, "notifyBatteryStateChanged电池状态已同步到提醒线程");
} else {
LogUtils.w(TAG, "notifyBatteryStateChangedHandler未初始化同步失败");
}
}

View File

@@ -8,12 +8,10 @@ import cc.winboll.studio.powerbell.models.AppConfigBean;
import java.lang.ref.WeakReference;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/19 20:28
* @Describe 提醒线程(线程安全单例)
* 提醒线程(线程安全单例)
* 功能:管理充电/耗电提醒逻辑触发条件时向Handler发送提醒消息
* 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步
* 对外接口:{@link #startRemindThread(Context, ControlCenterServiceHandler, AppConfigBean)}、{@link #stopRemindThreadSafely()}
* 对外接口:{@link #startRemindThreadWithAppConfig(Context, ControlCenterServiceHandler, AppConfigBean)}、{@link #startRemindThreadWithBatteryInfo(Context, ControlCenterServiceHandler, boolean, int)}、{@link #stopRemindThreadSafely()}
*/
public class RemindThread extends Thread {
// ================================== 静态常量区(置顶归类,消除魔法值)=================================
@@ -26,6 +24,12 @@ public class RemindThread extends Thread {
// 状态常量
private static final int INVALID_BATTERY_VALUE = -1;
private static final int BATTERY_LEVEL_MIN = 0;
private static final int BATTERY_LEVEL_MAX = 100;
// 提醒类型常量
private static final String REMIND_TYPE_CHARGE = "+";
private static final String REMIND_TYPE_USAGE = "-";
// ================================== 单例核心(双重校验锁,保证线程安全)=================================
private static volatile RemindThread sInstance;
@@ -53,10 +57,11 @@ public class RemindThread extends Thread {
// ================================== 私有构造器(单例专用,禁止外部实例化)=================================
private RemindThread(Context context, ControlCenterServiceHandler handler) {
LogUtils.d(TAG, "创建线程实例 | threadId=" + getId() + " | threadName=" + getName());
LogUtils.d(TAG, "构造方法执行 | threadId=" + getId() + " | threadName=" + getName());
this.mContext = context.getApplicationContext();
this.mwrControlCenterServiceHandler = new WeakReference<>(handler);
resetThreadStateInternal();
LogUtils.d(TAG, "构造完成 | 初始状态重置成功");
}
// ================================== 单例获取方法(核心,双重校验锁)=================================
@@ -86,11 +91,11 @@ public class RemindThread extends Thread {
* @param config 应用配置Bean非空
* @return true: 启动成功/已在运行false: 入参非法
*/
public static boolean startRemindThread(Context context, ControlCenterServiceHandler handler, AppConfigBean config) {
LogUtils.d(TAG, "请求启动线程 | context=" + context + " | handler=" + handler + " | config=" + config);
public static boolean startRemindThreadWithAppConfig(Context context, ControlCenterServiceHandler handler, AppConfigBean config) {
LogUtils.d(TAG, "startRemindThreadWithAppConfig执行 | context=" + context + " | handler=" + handler + " | config=" + config);
// 入参严格校验
if (context == null || handler == null || config == null) {
LogUtils.e(TAG, "启动失败:入参为空");
LogUtils.e(TAG, "启动失败:入参为空 | context=" + context + " | handler=" + handler + " | config=" + config);
return false;
}
@@ -111,15 +116,54 @@ public class RemindThread extends Thread {
return true;
} else {
LogUtils.d(TAG, "线程已在运行状态 | threadId=" + instance.getId());
return true;
}
}
/**
* 启动提醒线程,同步电池信息
* @param context 上下文(非空)
* @param handler 服务处理器(非空)
* @param isCharging 充电状态
* @param lastBatteryLevel 最新电量
* @return true: 启动成功/已在运行false: 入参非法
*/
public static boolean startRemindThreadWithBatteryInfo(Context context, ControlCenterServiceHandler handler, boolean isCharging, int lastBatteryLevel) {
LogUtils.d(TAG, "startRemindThreadWithBatteryInfo执行 | context=" + context + " | handler=" + handler + " | isCharging=" + isCharging + " | lastBatteryLevel=" + lastBatteryLevel);
// 入参严格校验
if (context == null || handler == null) {
LogUtils.e(TAG, "启动失败:入参为空 | context=" + context + " | handler=" + handler);
return false;
}
RemindThread instance = getInstance(context, handler);
// 已在提醒状态,仅同步电池信息
if (instance.isReminding) {
instance.isCharging = isCharging;
instance.quantityOfElectricity = lastBatteryLevel;
LogUtils.d(TAG, "线程已在运行,同步电池信息 | threadId=" + instance.getId() + " | isCharging=" + isCharging + " | lastBatteryLevel=" + lastBatteryLevel);
return true;
}
// 同步电池信息并启动线程
instance.isCharging = isCharging;
instance.quantityOfElectricity = lastBatteryLevel;
if (!instance.isRunning()) {
instance.isExist = false;
instance.start();
LogUtils.d(TAG, "线程启动成功 | threadId=" + instance.getId());
return true;
} else {
LogUtils.d(TAG, "线程已在运行状态 | threadId=" + instance.getId());
return true;
}
}
/**
* 安全停止线程,优雅销毁单例
*/
public static void stopRemindThreadSafely() {
LogUtils.d(TAG, "请求安全停止线程 | 单例存在=" + (sInstance != null));
LogUtils.d(TAG, "stopRemindThreadSafely执行 | 单例存在=" + (sInstance != null));
synchronized (RemindThread.class) {
if (sInstance == null) {
LogUtils.w(TAG, "停止失败:线程实例为空");
@@ -176,7 +220,7 @@ public class RemindThread extends Thread {
// ================================== 线程核心运行逻辑=================================
@Override
public void run() {
LogUtils.d(TAG, "线程进入运行 | threadId=" + getId() + " | 状态=" + getState());
LogUtils.d(TAG, "run执行 | threadId=" + getId() + " | 状态=" + getState());
// 初始化提醒状态(加锁保护,避免多线程竞争)
synchronized (mLock) {
@@ -198,18 +242,19 @@ public class RemindThread extends Thread {
if (isExist) break;
// 电量有效性校验非0-100视为无效
if (quantityOfElectricity < 0 || quantityOfElectricity > 100) {
LogUtils.w(TAG, "电量无效,退出循环 | 当前电量=" + quantityOfElectricity);
break;
if (quantityOfElectricity < BATTERY_LEVEL_MIN || quantityOfElectricity > BATTERY_LEVEL_MAX) {
LogUtils.w(TAG, "电量无效,跳过本次检测 | 当前电量=" + quantityOfElectricity);
safeSleepInternal(sleepTime);
continue;
}
// 充电提醒触发逻辑
if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) {
LogUtils.d(TAG, "触发充电提醒 | 当前电量=" + quantityOfElectricity + " ≥ 阈值=" + chargeReminderValue);
sendNotificationMessageInternal("+", quantityOfElectricity, isCharging);
sendNotificationMessageInternal(REMIND_TYPE_CHARGE, quantityOfElectricity, isCharging);
} else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) {
LogUtils.d(TAG, "触发耗电提醒 | 当前电量=" + quantityOfElectricity + " ≤ 阈值=" + usageReminderValue);
sendNotificationMessageInternal("-", quantityOfElectricity, isCharging);
sendNotificationMessageInternal(REMIND_TYPE_USAGE, quantityOfElectricity, isCharging);
}
// 安全休眠,保留中断标记
@@ -222,7 +267,7 @@ public class RemindThread extends Thread {
// 循环退出,清理状态
cleanThreadStateInternal();
LogUtils.d(TAG, "线程运行结束 | threadId=" + getId());
LogUtils.d(TAG, "run结束 | threadId=" + getId());
}
// ================================== 内部业务辅助方法=================================
@@ -233,6 +278,7 @@ public class RemindThread extends Thread {
* @param isCharging 充电状态
*/
private void sendNotificationMessageInternal(String type, int battery, boolean isCharging) {
LogUtils.d(TAG, "sendNotificationMessageInternal执行 | 类型=" + type + " | 电量=" + battery + " | isCharging=" + isCharging);
// 前置状态校验
if (isExist || !isReminding) {
LogUtils.d(TAG, "消息发送跳过:线程已退出或提醒关闭");
@@ -280,7 +326,7 @@ public class RemindThread extends Thread {
* 重置线程初始状态(构造器专用)
*/
private void resetThreadStateInternal() {
LogUtils.d(TAG, "重置线程初始状态");
LogUtils.d(TAG, "resetThreadStateInternal执行");
// 状态标记初始化
isExist = false;
isReminding = false;
@@ -298,7 +344,7 @@ public class RemindThread extends Thread {
* 清理线程运行状态(循环退出时调用)
*/
private void cleanThreadStateInternal() {
LogUtils.d(TAG, "清理线程运行状态");
LogUtils.d(TAG, "cleanThreadStateInternal执行");
isReminding = false;
isExist = true;
quantityOfElectricity = INVALID_BATTERY_VALUE;
@@ -313,7 +359,7 @@ public class RemindThread extends Thread {
* @param config 应用配置Bean
*/
public void setAppConfigBean(AppConfigBean config) {
LogUtils.d(TAG, "同步应用配置 | config=" + config);
LogUtils.d(TAG, "setAppConfigBean执行 | config=" + config);
if (config == null) {
LogUtils.e(TAG, "配置同步失败配置Bean为空");
quantityOfElectricity = INVALID_BATTERY_VALUE;
@@ -323,22 +369,22 @@ public class RemindThread extends Thread {
// 配置参数同步 + 范围校验(确保参数合法)
isEnableChargeReminder = config.isEnableChargeReminder();
isEnableUsageReminder = config.isEnableUsageReminder();
chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), 0), 100);
usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), 0), 100);
chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX);
usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX);
sleepTime = Math.max(config.getBatteryDetectInterval(), MIN_SLEEP_TIME);
quantityOfElectricity = (config.getCurrentBatteryValue() >= 0 && config.getCurrentBatteryValue() <= 100)
quantityOfElectricity = (config.getCurrentBatteryValue() >= BATTERY_LEVEL_MIN && config.getCurrentBatteryValue() <= BATTERY_LEVEL_MAX)
? config.getCurrentBatteryValue() : INVALID_BATTERY_VALUE;
isCharging = config.isCharging();
isReminding = isEnableChargeReminder || isEnableUsageReminder;
LogUtils.d(TAG, "配置同步完成 | 休眠时间=" + sleepTime + "ms | 提醒开启=" + isReminding + " | 当前电量=" + quantityOfElectricity);
LogUtils.d(TAG, "配置同步完成 | 休眠时间=" + sleepTime + "ms | 提醒开启=" + isReminding + " | 当前电量=" + quantityOfElectricity + " | 充电阈值=" + chargeReminderValue + " | 耗电阈值=" + usageReminderValue);
}
/**
* 释放线程内部资源,杜绝内存泄漏
*/
private void releaseResourcesInternal() {
LogUtils.d(TAG, "释放线程内部资源");
LogUtils.d(TAG, "releaseResourcesInternal执行");
// 释放上下文引用
mContext = null;
// 清空WeakReference
@@ -356,13 +402,13 @@ public class RemindThread extends Thread {
*/
private boolean isRunning() {
boolean running = !isExist && isAlive();
LogUtils.d(TAG, "线程运行状态判断 | 运行中=" + running + " | 退出标记=" + isExist + " | 存活=" + isAlive());
LogUtils.d(TAG, "isRunning执行 | 运行中=" + running + " | 退出标记=" + isExist + " | 存活=" + isAlive());
return running;
}
// ================================== Getter/Setter按需开放=================================
public void setIsExist(boolean isExist) {
LogUtils.d(TAG, "设置线程退出标记 | isExist=" + isExist);
LogUtils.d(TAG, "setIsExist执行 | isExist=" + isExist);
this.isExist = isExist;
}

View File

@@ -20,9 +20,9 @@ import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.models.NotificationMessage;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 13:42
* @Describe 通知工具类:统一管理前台服务/电池提醒通知适配API19-30Java7兼容前台服务无铃声提醒通知系统默认铃声
* 通知工具类:统一管理前台服务/电池提醒通知
* 适配API19-30 | Java7 | 小米手机
* 特性:前台服务无铃声提醒通知系统默认铃声、API分级适配、内存泄漏防护
*/
public class NotificationManagerUtils {
// ================================== 静态常量(置顶统一管理,杜绝魔法值)=================================
@@ -35,6 +35,14 @@ public class NotificationManagerUtils {
public static final int NOTIFY_ID_REMIND = 1002;
// 低版本兼容默认通知图标API<21 避免显示异常)
private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher;
// 通知内容兜底常量
private static final String FOREGROUND_NOTIFY_TITLE_DEFAULT = "电池服务运行中";
private static final String FOREGROUND_NOTIFY_CONTENT_DEFAULT = "后台监测电池状态";
private static final String REMIND_NOTIFY_TITLE_DEFAULT = "电池状态提醒";
private static final String REMIND_NOTIFY_CONTENT_DEFAULT = "电池状态异常,请及时处理";
// PendingIntent请求码
private static final int PENDING_INTENT_REQUEST_CODE_FOREGROUND = 0;
private static final int PENDING_INTENT_REQUEST_CODE_REMIND = 1;
// ================================== 成员变量(私有封装,按依赖优先级排序)=================================
// 核心上下文(应用级,避免内存泄漏)
@@ -46,10 +54,10 @@ public class NotificationManagerUtils {
// ================================== 构造方法(初始化核心资源,前置校验)=================================
public NotificationManagerUtils(Context context) {
LogUtils.d(TAG, "NotificationManagerUtils init start");
LogUtils.d(TAG, "构造方法执行");
// 前置校验Context非空
if (context == null) {
LogUtils.e(TAG, "init failed: context is null");
LogUtils.e(TAG, "构造失败:context is null");
return;
}
// 初始化核心资源
@@ -57,7 +65,7 @@ public class NotificationManagerUtils {
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
// 初始化通知渠道API26+ 必需)
initNotificationChannels();
LogUtils.d(TAG, "NotificationManagerUtils init success");
LogUtils.d(TAG, "构造完成:核心资源初始化成功");
}
// ================================== 核心初始化方法通知渠道API分级适配=================================
@@ -67,12 +75,12 @@ public class NotificationManagerUtils {
private void initNotificationChannels() {
// API<26 无渠道机制,直接返回
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
LogUtils.d(TAG, "initNotificationChannels: API<26, no need channel");
LogUtils.d(TAG, "initNotificationChannelsAPI<26,无需创建渠道");
return;
}
// 通知服务为空,避免空指针
if (mNotificationManager == null) {
LogUtils.e(TAG, "initNotificationChannels failed: NotificationManager is null");
LogUtils.e(TAG, "initNotificationChannels失败:NotificationManager is null");
return;
}
@@ -98,14 +106,14 @@ public class NotificationManagerUtils {
remindChannel.setDescription("电池满电/低电量提醒,系统默认铃声,无振动");
remindChannel.enableLights(true);
remindChannel.enableVibration(false);
remindChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT);
remindChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT);
remindChannel.setShowBadge(false);
remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
// 注册渠道到系统
mNotificationManager.createNotificationChannel(foregroundChannel);
mNotificationManager.createNotificationChannel(remindChannel);
LogUtils.d(TAG, "initNotificationChannels success: foreground+remind channel created");
LogUtils.d(TAG, "initNotificationChannels成功:创建前台服务+电池提醒渠道");
}
// ================================== 对外核心方法(前台服务通知:启动/更新/取消)=================================
@@ -113,26 +121,26 @@ public class NotificationManagerUtils {
* 启动前台服务通知API30适配无铃声
*/
public void startForegroundServiceNotify(Service service, NotificationMessage message) {
LogUtils.d(TAG, "startForegroundServiceNotify start, notifyId: " + NOTIFY_ID_FOREGROUND_SERVICE);
LogUtils.d(TAG, "startForegroundServiceNotify执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE);
// 前置校验:参数非空
if (service == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "startForegroundServiceNotify failed: param is null");
LogUtils.e(TAG, "startForegroundServiceNotify失败:param is null | service=" + service + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
// 构建前台通知
mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "startForegroundServiceNotify failed: build notify null");
LogUtils.e(TAG, "startForegroundServiceNotify失败:构建通知为空");
return;
}
// 启动前台服务API30无FOREGROUND_SERVICE_TYPE限制全版本通用
try {
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "startForegroundServiceNotify success");
LogUtils.d(TAG, "startForegroundServiceNotify成功");
} catch (Exception e) {
LogUtils.e(TAG, "startForegroundServiceNotify exception", e);
LogUtils.e(TAG, "startForegroundServiceNotify异常", e);
}
}
@@ -140,23 +148,23 @@ public class NotificationManagerUtils {
* 更新前台服务通知内容复用通知ID保持无铃声
*/
public void updateForegroundServiceNotify(NotificationMessage message) {
LogUtils.d(TAG, "updateForegroundServiceNotify start, notifyId: " + NOTIFY_ID_FOREGROUND_SERVICE);
LogUtils.d(TAG, "updateForegroundServiceNotify执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE);
if (message == null || mNotificationManager == null) {
LogUtils.e(TAG, "updateForegroundServiceNotify failed: param is null");
LogUtils.e(TAG, "updateForegroundServiceNotify失败:param is null | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "updateForegroundServiceNotify failed: build notify null");
LogUtils.e(TAG, "updateForegroundServiceNotify失败:构建通知为空");
return;
}
try {
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "updateForegroundServiceNotify success");
LogUtils.d(TAG, "updateForegroundServiceNotify成功");
} catch (Exception e) {
LogUtils.e(TAG, "updateForegroundServiceNotify exception", e);
LogUtils.e(TAG, "updateForegroundServiceNotify异常", e);
}
}
@@ -164,10 +172,10 @@ public class NotificationManagerUtils {
* 取消前台服务通知Service销毁时调用
*/
public void cancelForegroundServiceNotify() {
LogUtils.d(TAG, "cancelForegroundServiceNotify start, notifyId: " + NOTIFY_ID_FOREGROUND_SERVICE);
LogUtils.d(TAG, "cancelForegroundServiceNotify执行 | notifyId=" + NOTIFY_ID_FOREGROUND_SERVICE);
cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE);
mForegroundServiceNotify = null; // 置空释放
LogUtils.d(TAG, "cancelForegroundServiceNotify success");
LogUtils.d(TAG, "cancelForegroundServiceNotify成功");
}
// ================================== 对外核心方法(电池提醒通知:发送)=================================
@@ -175,23 +183,23 @@ public class NotificationManagerUtils {
* 发送电池提醒通知(系统默认铃声,无振动)
*/
public void showRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "showRemindNotification start, notifyId: " + NOTIFY_ID_REMIND);
LogUtils.d(TAG, "showRemindNotification执行 | notifyId=" + NOTIFY_ID_REMIND);
if (context == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "showRemindNotification failed: param is null");
LogUtils.e(TAG, "showRemindNotification失败:param is null | context=" + context + " | message=" + message + " | mNotificationManager=" + mNotificationManager);
return;
}
Notification remindNotify = buildRemindNotification(context, message);
if (remindNotify == null) {
LogUtils.e(TAG, "showRemindNotification failed: build notify null");
LogUtils.e(TAG, "showRemindNotification失败:构建通知为空");
return;
}
try {
mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify);
LogUtils.d(TAG, "showRemindNotification success");
LogUtils.d(TAG, "showRemindNotification成功");
} catch (Exception e) {
LogUtils.e(TAG, "showRemindNotification exception", e);
LogUtils.e(TAG, "showRemindNotification异常", e);
}
}
@@ -200,15 +208,16 @@ public class NotificationManagerUtils {
* 取消指定ID的通知
*/
public void cancelNotification(int notifyId) {
LogUtils.d(TAG, "cancelNotification执行 | notifyId=" + notifyId);
if (mNotificationManager == null) {
LogUtils.e(TAG, "cancelNotification failed: NotificationManager is null");
LogUtils.e(TAG, "cancelNotification失败:NotificationManager is null");
return;
}
try {
mNotificationManager.cancel(notifyId);
LogUtils.d(TAG, "cancelNotification success, notifyId: " + notifyId);
LogUtils.d(TAG, "cancelNotification成功 | notifyId=" + notifyId);
} catch (Exception e) {
LogUtils.e(TAG, "cancelNotification exception, notifyId: " + notifyId, e);
LogUtils.e(TAG, "cancelNotification异常 | notifyId=" + notifyId, e);
}
}
@@ -216,15 +225,16 @@ public class NotificationManagerUtils {
* 取消所有通知(兜底场景使用)
*/
public void cancelAllNotifications() {
LogUtils.d(TAG, "cancelAllNotifications执行");
if (mNotificationManager == null) {
LogUtils.e(TAG, "cancelAllNotifications failed: NotificationManager is null");
LogUtils.e(TAG, "cancelAllNotifications失败:NotificationManager is null");
return;
}
try {
mNotificationManager.cancelAll();
LogUtils.d(TAG, "cancelAllNotifications success");
LogUtils.d(TAG, "cancelAllNotifications成功");
} catch (Exception e) {
LogUtils.e(TAG, "cancelAllNotifications exception", e);
LogUtils.e(TAG, "cancelAllNotifications异常", e);
}
}
@@ -233,14 +243,16 @@ public class NotificationManagerUtils {
* 构建前台服务通知(全版本无铃声+无振动)
*/
private Notification buildForegroundNotification(NotificationMessage message) {
LogUtils.d(TAG, "buildForegroundNotification执行");
if (message == null || mContext == null) {
LogUtils.e(TAG, "buildForegroundNotification failed: param is null");
LogUtils.e(TAG, "buildForegroundNotification失败:param is null | message=" + message + " | mContext=" + mContext);
return null;
}
// 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中";
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台监测电池状态";
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : FOREGROUND_NOTIFY_TITLE_DEFAULT;
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : FOREGROUND_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildForegroundNotificationtitle=" + title + " | content=" + content);
Notification.Builder builder;
// API分级构建
@@ -262,7 +274,7 @@ public class NotificationManagerUtils {
.setAutoCancel(false)
.setOngoing(true) // 不可手动关闭
.setWhen(System.currentTimeMillis())
.setContentIntent(createJumpPendingIntent(mContext, 0));
.setContentIntent(createJumpPendingIntent(mContext, PENDING_INTENT_REQUEST_CODE_FOREGROUND));
// API21+ 新增大图标+主题色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -279,14 +291,16 @@ public class NotificationManagerUtils {
* 构建电池提醒通知(全版本系统默认铃声+无振动)
*/
private Notification buildRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "buildRemindNotification执行");
if (context == null || message == null) {
LogUtils.e(TAG, "buildRemindNotification failed: param is null");
LogUtils.e(TAG, "buildRemindNotification失败:param is null | context=" + context + " | message=" + message);
return null;
}
// 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒";
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理";
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : REMIND_NOTIFY_TITLE_DEFAULT;
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : REMIND_NOTIFY_CONTENT_DEFAULT;
LogUtils.d(TAG, "buildRemindNotificationtitle=" + title + " | content=" + content);
Notification.Builder builder;
// API分级构建
@@ -309,7 +323,7 @@ public class NotificationManagerUtils {
.setAutoCancel(true) // 点击关闭
.setOngoing(false)
.setWhen(System.currentTimeMillis())
.setContentIntent(createJumpPendingIntent(context, 1));
.setContentIntent(createJumpPendingIntent(context, PENDING_INTENT_REQUEST_CODE_REMIND));
// API21+ 新增大图标+主题色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -326,6 +340,7 @@ public class NotificationManagerUtils {
* 创建跳转MainActivity的PendingIntentAPI23+ 添加IMMUTABLE标记避免安全异常
*/
private PendingIntent createJumpPendingIntent(Context context, int requestCode) {
LogUtils.d(TAG, "createJumpPendingIntent执行 | requestCode=" + requestCode);
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -333,9 +348,12 @@ public class NotificationManagerUtils {
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags |= PendingIntent.FLAG_IMMUTABLE;
LogUtils.d(TAG, "createJumpPendingIntent添加FLAG_IMMUTABLE标记");
}
return PendingIntent.getActivity(context, requestCode, intent, flags);
PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, flags);
LogUtils.d(TAG, "createJumpPendingIntent成功 | requestCode=" + requestCode);
return pendingIntent;
}
// ================================== 内部辅助方法获取APP图标异常兜底=================================
@@ -343,11 +361,14 @@ public class NotificationManagerUtils {
* 获取APP图标失败返回默认图标
*/
private Bitmap getAppIcon(Context context) {
LogUtils.d(TAG, "getAppIcon执行");
try {
PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return BitmapFactory.decodeResource(context.getResources(), pkgInfo.applicationInfo.icon);
Bitmap appIcon = BitmapFactory.decodeResource(context.getResources(), pkgInfo.applicationInfo.icon);
LogUtils.d(TAG, "getAppIcon成功获取应用图标");
return appIcon;
} catch (PackageManager.NameNotFoundException e) {
LogUtils.e(TAG, "getAppIcon exception", e);
LogUtils.e(TAG, "getAppIcon异常:获取应用图标失败,使用默认图标", e);
return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON);
}
}
@@ -362,11 +383,11 @@ public class NotificationManagerUtils {
* 释放资源,销毁时调用
*/
public void release() {
LogUtils.d(TAG, "release start");
LogUtils.d(TAG, "release执行");
cancelForegroundServiceNotify();
mNotificationManager = null;
mContext = null;
LogUtils.d(TAG, "release success");
LogUtils.d(TAG, "release成功:所有资源已释放");
}
}