diff --git a/powerbell/build.properties b/powerbell/build.properties index 7a2266b..3b75d1f 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Dec 17 07:16:02 GMT 2025 +#Wed Dec 17 08:23:18 GMT 2025 stageCount=10 libraryProject= baseVersion=15.14 publishVersion=15.14.9 -buildCount=11 +buildCount=16 baseBetaVersion=15.14.10 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java index 7ccfa27..02be2dd 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java @@ -10,7 +10,7 @@ import cc.winboll.studio.powerbell.models.NotificationMessage; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/12/17 13:41 - * @Describe 服务通信Handler:弱引用持有服务,避免内存泄漏,适配Java7+API30,统一处理服务消息 + * @Describe 服务通信Handler:弱引用持有服务,避免内存泄漏,通知格式优化为 (+/-)(当前电量)(充电状态) */ public class ControlCenterServiceHandler extends Handler { // ================================== 静态常量(置顶统一管理,清晰区分消息类型)================================= @@ -26,11 +26,15 @@ public class ControlCenterServiceHandler extends Handler { this.mwrControlCenterService = new WeakReference<>(service); } - // ================================== 核心消息处理(重写handleMessage,按类型分发)================================= + // ================================== 核心消息处理(重写handleMessage,解析多参数消息)================================= @Override public void handleMessage(Message msg) { super.handleMessage(msg); - LogUtils.d(TAG, "接收消息,what:" + msg.what + ",obj:" + (msg.obj != null ? msg.obj : "null")); + // 解析线程传递的完整数据:obj=提醒类型(+/-),arg1=当前电量,arg2=充电状态(1=充电中,0=未充电) + String remindType = (msg.obj != null) ? (String) msg.obj : ""; + int currentBattery = msg.arg1; + boolean isCharging = msg.arg2 == 1; + LogUtils.d(TAG, "接收消息,what:" + msg.what + ",类型:" + remindType + ",电量:" + currentBattery + ",充电状态:" + isCharging); // 弱引用获取服务,避免持有强引用导致内存泄漏 ControlCenterService service = mwrControlCenterService.get(); @@ -42,7 +46,7 @@ public class ControlCenterServiceHandler extends Handler { // 按消息类型分发处理,避免逻辑冗余 switch (msg.what) { case MSG_REMIND_TEXT: - handleRemindMessage(service, (String) msg.obj); + handleRemindMessage(service, remindType, currentBattery, isCharging); break; default: LogUtils.w(TAG, "未知消息类型,what:" + msg.what); @@ -50,41 +54,51 @@ public class ControlCenterServiceHandler extends Handler { } } - // ================================== 业务辅助方法(单独处理提醒消息,职责单一)================================= + // ================================== 业务辅助方法(重构通知内容,格式:(+/-)(当前电量)(充电状态))================================= /** - * 处理电量提醒消息,构建通知模型并调用工具类发送 + * 处理电量提醒消息,构建带电量+充电状态的通知模型 * @param service 服务实例(非空,已前置校验) - * @param content 消息内容(+:充电提醒,-:耗电提醒) + * @param remindType 提醒类型(+:充电提醒,-:耗电提醒) + * @param currentBattery 当前电量(0-100) + * @param isCharging 充电状态(true=充电中,false=未充电) */ - private void handleRemindMessage(ControlCenterService service, String content) { - LogUtils.d(TAG, "开始处理提醒消息,内容:" + content); + private void handleRemindMessage(ControlCenterService service, String remindType, int currentBattery, boolean isCharging) { + LogUtils.d(TAG, "开始处理提醒消息,类型:" + remindType + ",电量:" + currentBattery + ",充电状态:" + isCharging); - // 校验通知工具类,避免空指针 + // 1. 前置校验:通知工具类+提醒类型+电量有效性 if (service.getNotificationManager() == null) { LogUtils.e(TAG, "通知管理工具类为空,无法发送提醒"); return; } - - // 构建通知消息模型,区分充电/耗电场景 - NotificationMessage remindMsg = new NotificationMessage(); - if ("+".equals(content)) { - remindMsg.setTitle("充电提醒"); - remindMsg.setContent("电池电量已达标,建议及时断电,保护电池寿命~"); - remindMsg.setRemindMSG("charge_remind"); - LogUtils.d(TAG, "构建充电提醒通知,标识:charge_remind"); - } else if ("-".equals(content)) { - remindMsg.setTitle("耗电提醒"); - remindMsg.setContent("电池电量偏低,建议及时充电,避免设备关机~"); - remindMsg.setRemindMSG("usage_remind"); - LogUtils.d(TAG, "构建耗电提醒通知,标识:usage_remind"); - } else { - LogUtils.w(TAG, "无效提醒消息内容,跳过发送:" + content); + if (!"+".equals(remindType) && !"-".equals(remindType)) { + LogUtils.w(TAG, "无效提醒类型,跳过发送:" + remindType); + return; + } + if (currentBattery < 0 || currentBattery > 100) { + LogUtils.w(TAG, "无效当前电量,跳过发送:" + currentBattery); return; } - // 调用服务工具类发送通知,复用现有逻辑 + // 2. 构建通知模型,按格式拼接内容(核心优化) + NotificationMessage remindMsg = new NotificationMessage(); + String chargeStateDesc = isCharging ? "充电中" : "未充电"; // 充电状态文字描述 + if ("+".equals(remindType)) { + // 充电提醒:格式 (+)(当前电量)(充电状态) + remindMsg.setTitle("充电提醒"); + remindMsg.setContent("(+) 当前电量" + currentBattery + "%," + chargeStateDesc + ",已达标建议及时断电,保护电池寿命~"); + remindMsg.setRemindMSG("charge_remind"); + LogUtils.d(TAG, "构建充电提醒通知,内容:" + remindMsg.getContent()); + } else if ("-".equals(remindType)) { + // 耗电提醒:格式 (-)(当前电量)(充电状态) + remindMsg.setTitle("耗电提醒"); + remindMsg.setContent("(-) 当前电量" + currentBattery + "%," + chargeStateDesc + ",已偏低建议及时充电,避免设备关机~"); + remindMsg.setRemindMSG("usage_remind"); + LogUtils.d(TAG, "构建耗电提醒通知,内容:" + remindMsg.getContent()); + } + + // 3. 调用服务工具类发送通知,复用现有逻辑 service.getNotificationManager().showRemindNotification(service, remindMsg); - LogUtils.d(TAG, "提醒通知发送完成,标题:" + remindMsg.getTitle()); + LogUtils.d(TAG, "提醒通知发送完成,标题:" + remindMsg.getTitle() + ",完整内容:" + remindMsg.getContent()); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java index c25d4a7..e24b8c5 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java @@ -1,99 +1,142 @@ package cc.winboll.studio.powerbell.models; -/** - * @Author ZhanGSKen - * @Date 2024/07/18 07:06:07 - * @Describe 服务控制参数 - */ import android.os.Parcel; import android.os.Parcelable; import android.util.JsonReader; import android.util.JsonWriter; -import cc.winboll.studio.libappbase.BaseBean; + import java.io.IOException; import java.io.Serializable; -// 核心修正:实现 Parcelable + Serializable 接口(适配持久化+Intent传递,API30 必备) +import cc.winboll.studio.libappbase.BaseBean; +import cc.winboll.studio.libappbase.LogUtils; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 15:55 + * @Describe 服务控制参数模型:管理服务启用状态,支持序列化、Parcel传递、JSON解析 + */ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Serializable { + // ================================== 静态常量(置顶统一管理,避免魔法值)================================= + private static final long serialVersionUID = 1L; // Serializable 必备,保障反序列化兼容 + private static final String TAG = "ControlCenterServiceBean"; + // JSON 字段常量,避免硬编码,减少拼写错误 + private static final String JSON_FIELD_IS_ENABLE_SERVICE = "isEnableService"; - // 序列化版本号(Serializable 必备,避免反序列化崩溃) - private static final long serialVersionUID = 1L; + // ================================== 核心成员变量(私有封装,规范命名)================================= + private boolean isEnableService = false; // 服务启用状态:true=启用,false=禁用 - public static final String TAG = "ControlCenterServiceBean"; - - boolean isEnableService = false; - - public ControlCenterServiceBean() { - this.isEnableService = false; - } - - public ControlCenterServiceBean(boolean isEnableService) { - this.isEnableService = isEnableService; - } - - public void setIsEnableService(boolean isEnableService) { - this.isEnableService = isEnableService; - } - - public boolean isEnableService() { - return isEnableService; - } - - @Override - public String getName() { - return ControlCenterServiceBean.class.getName(); - } - - @Override - public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { - super.writeThisToJsonWriter(jsonWriter); - ControlCenterServiceBean bean = this; - jsonWriter.name("isEnableService").value(bean.isEnableService()); - } - - @Override - public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { - ControlCenterServiceBean bean = new ControlCenterServiceBean(); - jsonReader.beginObject(); - while (jsonReader.hasNext()) { - String name = jsonReader.nextName(); - if (name.equals("isEnableService")) { - bean.setIsEnableService(jsonReader.nextBoolean()); - } else { - jsonReader.skipValue(); - } - } - // 结束 JSON 对象 - jsonReader.endObject(); - return bean; - } - - // ======================== 补全:Parcelable 接口实现(API30 持久化/Intent传递必备)======================== - @Override - public int describeContents() { - return 0; // 无特殊内容描述,返回0即可 - } - - // 序列化:将对象属性写入 Parcel - @Override - public void writeToParcel(Parcel dest, int flags) { - // boolean 类型用 byte 存储(Parcel 无直接 writeBoolean 方法,Java7 适配) - dest.writeByte((byte) (isEnableService ? 1 : 0)); - } - - // 反序列化:从 Parcel 读取属性,创建对象(必须是 public static final 修饰) + // ================================== Parcelable 静态创建器(必须 public static final,适配 API30 传递)================================= public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public ControlCenterServiceBean createFromParcel(Parcel source) { - // 从 Parcel 读取 byte,转为 boolean + LogUtils.d(TAG, "Parcelable createFromParcel: 从Parcel反序列化对象"); + // Java7 + API30 适配:Parcel 无直接 writeBoolean,用 byte 存储/读取 boolean isEnable = source.readByte() != 0; - return new ControlCenterServiceBean(isEnable); + ControlCenterServiceBean bean = new ControlCenterServiceBean(isEnable); + LogUtils.d(TAG, "Parcelable createFromParcel: 反序列化完成,isEnableService=" + isEnable); + return bean; } @Override public ControlCenterServiceBean[] newArray(int size) { + LogUtils.d(TAG, "Parcelable newArray: 创建数组,长度=" + size); return new ControlCenterServiceBean[size]; } }; + + // ================================== 构造方法(无参+有参,满足不同初始化场景)================================= + /** + * 无参构造(JSON解析、反射创建必备) + */ + public ControlCenterServiceBean() { + this.isEnableService = false; + LogUtils.d(TAG, "无参构造:初始化服务状态为禁用(false)"); + } + + /** + * 有参构造(指定服务启用状态) + * @param isEnableService 服务启用状态 + */ + public ControlCenterServiceBean(boolean isEnableService) { + this.isEnableService = isEnableService; + LogUtils.d(TAG, "有参构造:初始化服务状态,isEnableService=" + isEnableService); + } + + // ================================== Getter/Setter 方法(封装成员变量,控制访问)================================= + public boolean isEnableService() { + LogUtils.d(TAG, "get isEnableService: 当前状态=" + isEnableService); + return isEnableService; + } + + public void setIsEnableService(boolean isEnableService) { + LogUtils.d(TAG, "set isEnableService: 旧状态=" + this.isEnableService + ",新状态=" + isEnableService); + this.isEnableService = isEnableService; + } + + // ================================== 父类 BaseBean 方法重写(核心业务逻辑)================================= + @Override + public String getName() { + String className = ControlCenterServiceBean.class.getName(); + LogUtils.d(TAG, "getName: 返回类名=" + className); + return className; + } + + /** + * 序列化对象到 JSON(适配数据持久化/网络传输) + */ + @Override + public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { + LogUtils.d(TAG, "writeThisToJsonWriter: 开始将对象序列化到JSON"); + super.writeThisToJsonWriter(jsonWriter); + // 写入服务启用状态字段 + jsonWriter.name(JSON_FIELD_IS_ENABLE_SERVICE).value(this.isEnableService); + LogUtils.d(TAG, "writeThisToJsonWriter: JSON序列化完成,字段=" + JSON_FIELD_IS_ENABLE_SERVICE + ",值=" + this.isEnableService); + } + + /** + * 从 JSON 反序列化创建对象(适配数据恢复) + */ + @Override + public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { + LogUtils.d(TAG, "readBeanFromJsonReader: 开始从JSON反序列化对象"); + ControlCenterServiceBean bean = new ControlCenterServiceBean(); + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String fieldName = jsonReader.nextName(); + if (JSON_FIELD_IS_ENABLE_SERVICE.equals(fieldName)) { + // 读取并设置服务启用状态 + boolean isEnable = jsonReader.nextBoolean(); + bean.setIsEnableService(isEnable); + LogUtils.d(TAG, "readBeanFromJsonReader: 读取JSON字段," + fieldName + "=" + isEnable); + } else { + // 跳过未知字段,避免解析异常 + jsonReader.skipValue(); + LogUtils.w(TAG, "readBeanFromJsonReader: 跳过未知JSON字段=" + fieldName); + } + } + jsonReader.endObject(); + LogUtils.d(TAG, "readBeanFromJsonReader: JSON反序列化完成"); + return bean; + } + + // ================================== Parcelable 接口方法实现(适配 Intent 组件间传递)================================= + @Override + public int describeContents() { + // 无特殊内容(如文件描述符),返回0即可(API30 标准实现) + LogUtils.d(TAG, "describeContents: 返回内容描述符=0"); + return 0; + } + + /** + * 序列化对象到 Parcel(Intent 传递必备,Java7 适配) + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + LogUtils.d(TAG, "writeToParcel: 开始将对象序列化到Parcel,flags=" + flags); + // Java7 + API30 适配:Parcel 无 writeBoolean 方法,用 byte 存储(1=true,0=false) + dest.writeByte((byte) (this.isEnableService ? 1 : 0)); + LogUtils.d(TAG, "writeToParcel: Parcel序列化完成,isEnableService=" + this.isEnableService + "(存储为byte=" + (this.isEnableService ? 1 : 0) + ")"); + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java index 84b7667..5cdd330 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java @@ -10,212 +10,218 @@ import android.os.IBinder; import android.os.PowerManager; import android.provider.Settings; import android.text.TextUtils; +import java.io.Serializable; +import java.util.List; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler; import cc.winboll.studio.powerbell.models.AppConfigBean; +import cc.winboll.studio.powerbell.models.ControlCenterServiceBean; import cc.winboll.studio.powerbell.models.NotificationMessage; import cc.winboll.studio.powerbell.threads.RemindThread; import cc.winboll.studio.powerbell.utils.NotificationManagerUtils; -import java.io.Serializable; -import java.util.List; +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 15:48 + * @Describe 电池提醒核心服务:通过本地持久化 ControlCenterServiceBean 控制自启动,统一管理线程、通知、配置,适配Java7+API30 + */ public class ControlCenterService extends Service { + // ================================== 静态常量(置顶统一管理,避免魔法值)================================= private static final String TAG = "ControlCenterService"; - // Intent指令常量(完整包名前缀,避免冲突) + // 服务指令常量(带包名前缀,防止冲突) public static final String ACTION_RESTART_REMIND_THREAD = "cc.winboll.studio.powerbell.action.RESTART_REMIND_THREAD"; public static final String EXTRA_APP_CONFIG_BEAN = "cc.winboll.studio.powerbell.extra.APP_CONFIG_BEAN"; - // 核心成员变量 - private ControlCenterServiceHandler mServiceHandler; - private RemindThread mRemindThread; - private NotificationManagerUtils mNotificationManager; - private NotificationMessage mForegroundNotifyMsg; - private AppConfigBean mCurrentConfigBean; - private boolean isServiceRunning = false; - private boolean mIsDestroyed = false; // 服务销毁状态标记(供Receiver判断) - private final Object mServiceLock = new Object(); // 并发锁(避免多线程冲突) + // ================================== 核心成员变量(按依赖优先级排序,私有封装)================================= + // 服务控制核心(本地持久化管理,替代Intent传递) + private ControlCenterServiceBean mServiceControlBean; + // 业务核心组件 + private ControlCenterServiceHandler mServiceHandler; // 服务通信Handler + private RemindThread mRemindThread; // 电量提醒线程 + private NotificationManagerUtils mNotificationManager; // 通知管理工具类 + private AppConfigBean mCurrentConfigBean; // 当前应用配置 + // 前台服务相关 + private NotificationMessage mForegroundNotifyMsg; // 前台保活通知模型 + // 状态标记(并发安全) + private boolean isServiceRunning = false; // 服务运行状态 + private boolean mIsDestroyed = false; // 服务销毁标记 + private final Object mServiceLock = new Object(); // 全局并发锁 + // ================================== 服务生命周期方法(onCreate→onStartCommand→onDestroy)================================= @Override public void onCreate() { super.onCreate(); - LogUtils.d(TAG, "onCreate: 服务创建,初始化核心组件"); - isServiceRunning = true; - mIsDestroyed = false; - initNotificationManager(); - startForegroundNotifyImmediately(); - loadDefaultConfig(); - initServiceBusinessLogic(); - LogUtils.d(TAG, "onCreate: 服务初始化完成"); + LogUtils.d(TAG, "onCreate: 服务开始创建"); + synchronized (mServiceLock) { + isServiceRunning = true; + mIsDestroyed = false; + // 初始化服务控制配置:优先读取本地持久化配置,无则创建默认禁用配置 + mServiceControlBean = ControlCenterServiceBean.loadBean(this, ControlCenterServiceBean.class); + if (mServiceControlBean == null) { + mServiceControlBean = new ControlCenterServiceBean(false); + ControlCenterServiceBean.saveBean(this, mServiceControlBean); // 持久化默认配置 + LogUtils.d(TAG, "onCreate: 本地无控制配置,创建默认禁用配置并持久化"); + } else { + LogUtils.d(TAG, "onCreate: 从本地读取服务控制配置,启用状态=" + mServiceControlBean.isEnableService()); + } + } + LogUtils.d(TAG, "onCreate: 服务创建完成,当前服务控制状态:" + (mServiceControlBean.isEnableService() ? "启用" : "禁用")); } @Override public int onStartCommand(Intent intent, int flags, int startId) { - LogUtils.d(TAG, "onStartCommand: 触发服务启动命令,flags=" + flags); - handleExternalCommand(intent); - - // 重新绑定前台服务,避免API30+状态丢失(加判空容错) - if (mNotificationManager != null && mNotificationManager.getForegroundServiceNotify() != null) { - try { - startForeground(NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE, - mNotificationManager.getForegroundServiceNotify()); - LogUtils.d(TAG, "onStartCommand: 重新绑定前台通知保活"); - } catch (Exception e) { - LogUtils.e(TAG, "onStartCommand: 重新绑定前台通知异常", e); - } - } - return START_STICKY; // 服务回收后自动重启 - } - - // 处理外部指令(重启线程)- 加锁避免并发调用 - private void handleExternalCommand(Intent intent) { - LogUtils.d(TAG, "handleExternalCommand: 处理外部指令"); - if (intent == null) { - LogUtils.e(TAG, "handleExternalCommand: Intent为空,跳过"); - return; - } - String action = intent.getAction(); - if (TextUtils.isEmpty(action)) { - LogUtils.e(TAG, "handleExternalCommand: 指令Action为空,跳过"); - return; - } - - synchronized (mServiceLock) { // 关键:加锁,防止并发触发重启导致线程冲突 - if (ACTION_RESTART_REMIND_THREAD.equals(action)) { - AppConfigBean newConfig = (AppConfigBean) intent.getSerializableExtra(EXTRA_APP_CONFIG_BEAN); - if (newConfig != null) { - mCurrentConfigBean = newConfig; - LogUtils.d(TAG, "handleExternalCommand: 收到重启指令,更新配置"); - restartRemindThreadSafely(); - } else { - LogUtils.e(TAG, "handleExternalCommand: 新配置为空,重启失败"); - } - } - } - } - - // 安全重启提醒线程 - 核心修复:彻底销毁旧线程,确保新线程是全新实例 - public void restartRemindThreadSafely() { - LogUtils.d(TAG, "restartRemindThreadSafely: 重启提醒线程"); - synchronized (mServiceLock) { // 加锁,避免与服务销毁/其他重启操作冲突 - // 1. 先彻底停止旧线程(调用独立封装的安全停止方法) - stopRemindThreadSafely(); - - // 2. 校验服务状态,避免服务已停止时启动线程 - if (!isServiceRunning || mServiceHandler == null || mIsDestroyed) { - LogUtils.e(TAG, "restartRemindThreadSafely: 服务未运行/已销毁/Handler为空,启动失败"); - return; - } - - // 3. 强制销毁RemindThread单例(关键:避免复用旧线程对象) - RemindThread.destroyInstance(); // 必须调用,确保getInstance返回新实例 - // 4. 创建新线程实例(此时是全新Thread对象,未启动) - mRemindThread = RemindThread.getInstance(this, mServiceHandler); - syncConfigToRemindThread(); - - // 5. 双重校验线程状态,杜绝重复start() - if (mRemindThread != null && !mRemindThread.isAlive() && !mRemindThread.isThreadStarted()) { - try { - mRemindThread.start(); // 修复崩溃核心:仅对全新线程调用start() - LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功,ID=" + mRemindThread.getId()); - } catch (IllegalThreadStateException e) { - // 兜底捕获:即使校验失败,也避免崩溃,打印详细日志 - LogUtils.e(TAG, "restartRemindThreadSafely: 线程重复启动异常(致命错误)", e); - mRemindThread = null; // 启动失败,置空避免后续错误 - } catch (Exception e) { - LogUtils.e(TAG, "restartRemindThreadSafely: 启动新线程异常", e); - mRemindThread = null; - } - } else { - LogUtils.w(TAG, "restartRemindThreadSafely: 线程已启动/实例异常,无需重复操作"); - mRemindThread = null; - } - } - } - - // 安全停止提醒线程 - 优化:确保线程彻底终止,无残留 - private void stopRemindThreadSafely() { - LogUtils.d(TAG, "stopRemindThreadSafely: 安全停止提醒线程"); - if (mRemindThread == null) { - LogUtils.w(TAG, "stopRemindThreadSafely: 线程实例为空,跳过"); - return; - } - - try { - // 1. 先通过线程内部方法标记停止(依赖RemindThread的stopThread实现) - if (mRemindThread.isThreadStarted() || mRemindThread.isAlive()) { - mRemindThread.setIsReminding(false); // 关闭提醒功能 - mRemindThread.stopThread(); // 触发线程安全停止(置位退出标记+中断休眠) - LogUtils.d(TAG, "stopRemindThreadSafely: 触发线程内部停止逻辑"); - - // 2. 等待线程终止(最多1秒,避免阻塞服务,超时强制置空) - long waitStartTime = System.currentTimeMillis(); - while (mRemindThread.isAlive()) { - if (System.currentTimeMillis() - waitStartTime > 1000) { - LogUtils.w(TAG, "stopRemindThreadSafely: 线程停止超时,强制终止处理"); - mRemindThread.interrupt(); // 强制中断,加速退出 - break; - } - Thread.yield(); // 让出CPU,优先执行线程退出逻辑 - } - } - } catch (Exception e) { - LogUtils.e(TAG, "stopRemindThreadSafely: 线程停止异常", e); - } finally { - // 3. 彻底释放:置空实例+销毁单例,杜绝复用 - mRemindThread = null; - RemindThread.destroyInstance(); // 关键:销毁单例,避免getInstance复用旧对象 - LogUtils.d(TAG, "stopRemindThreadSafely: 线程停止完成,资源已释放"); - } - } - - // 同步配置到提醒线程 - 加锁避免并发修改 - private void syncConfigToRemindThread() { - LogUtils.d(TAG, "syncConfigToRemindThread: 同步配置到线程"); + LogUtils.d(TAG, "onStartCommand: 服务启动指令触发,flags=" + flags); + // 第一步:读取本地最新控制配置(优先级最高,实时同步用户设置) + loadLatestServiceControlConfig(); + // 第二步:根据控制状态,分支执行逻辑 synchronized (mServiceLock) { - if (mRemindThread == null || mCurrentConfigBean == null) { - LogUtils.e(TAG, "syncConfigToRemindThread: 线程/配置为空,同步失败"); - return; + if (mServiceControlBean.isEnableService()) { + LogUtils.d(TAG, "onStartCommand: 服务已启用,执行核心逻辑"); + // 启用状态:处理指令+绑定前台通知+启动业务,返回START_STICKY(回收后重启) + handleExternalCommand(intent); + rebindForegroundNotify(); + initServiceBusinessIfNeed(); // 按需初始化业务(避免重复初始化) + LogUtils.d(TAG, "onStartCommand: 核心逻辑执行完成,返回START_STICKY"); + return START_STICKY; + } else { + LogUtils.d(TAG, "onStartCommand: 服务已禁用,不执行核心逻辑"); + // 禁用状态:停止所有业务+释放资源,返回父类默认值(回收后不主动重启) + stopAllBusinessSafely(); + LogUtils.d(TAG, "onStartCommand: 已停止所有业务,返回父类默认onStartCommand结果"); + return super.onStartCommand(intent, flags, startId); } - mRemindThread.setAppConfigBean(mCurrentConfigBean); // 同步完整配置 - mRemindThread.setIsReminding(true); // 开启提醒功能 - LogUtils.d(TAG, "syncConfigToRemindThread: 配置同步完成"); } } - // 加载默认配置 - 优化:补全必要配置项,避免空指针 - private void loadDefaultConfig() { - LogUtils.d(TAG, "loadDefaultConfig: 加载默认配置"); - mCurrentConfigBean = new AppConfigBean(); - mCurrentConfigBean.setEnableChargeReminder(true); - mCurrentConfigBean.setChargeReminderValue(80); - mCurrentConfigBean.setEnableUsageReminder(true); - mCurrentConfigBean.setUsageReminderValue(20); - mCurrentConfigBean.setBatteryDetectInterval(1000); // 电池检测间隔(1秒) - LogUtils.d(TAG, "loadDefaultConfig: 默认配置加载完成"); + @Override + public void onDestroy() { + super.onDestroy(); + LogUtils.d(TAG, "onDestroy: 服务开始销毁,释放所有资源"); + synchronized (mServiceLock) { + isServiceRunning = false; + mIsDestroyed = true; + } + // 顺序释放资源(前台服务→线程→Handler→通知→配置→控制Bean) + stopForegroundService(); + stopRemindThreadSafely(); + destroyHandler(); + releaseNotificationResource(); + clearAllReferences(); + LogUtils.d(TAG, "onDestroy: 服务销毁完成"); } - // 初始化通知工具类 - 优化:移除无效调用,强化容错 + @Override + public IBinder onBind(Intent intent) { + return null; // 无需绑定服务,返回null + } + + // ================================== 核心:服务控制配置(本地持久化读写,替代Intent传递)================================= + /** + * 读取本地最新服务控制配置(实时同步,确保与用户设置一致) + */ + private void loadLatestServiceControlConfig() { + LogUtils.d(TAG, "loadLatestServiceControlConfig: 读取本地最新服务控制配置"); + synchronized (mServiceLock) { + ControlCenterServiceBean latestControlBean = ControlCenterServiceBean.loadBean(this, ControlCenterServiceBean.class); + if (latestControlBean != null) { + boolean oldState = mServiceControlBean.isEnableService(); + boolean newState = latestControlBean.isEnableService(); + mServiceControlBean = latestControlBean; + LogUtils.d(TAG, "loadLatestServiceControlConfig: 控制配置更新完成,旧状态=" + oldState + ",新状态=" + newState); + } else { + LogUtils.w(TAG, "loadLatestServiceControlConfig: 本地无控制配置,沿用当前状态=" + mServiceControlBean.isEnableService()); + } + } + LogUtils.d(TAG, "loadLatestServiceControlConfig: 控制配置读取完成,当前服务启用状态=" + mServiceControlBean.isEnableService()); + } + + /** + * 更新服务控制配置(本地持久化+内存同步,对外提供统一入口) + */ + private void updateAndSaveServiceControlConfig(ControlCenterServiceBean newControlBean) { + LogUtils.d(TAG, "updateAndSaveServiceControlConfig: 更新服务控制配置,新状态=" + newControlBean.isEnableService()); + if (newControlBean == null) { + LogUtils.e(TAG, "updateAndSaveServiceControlConfig: 新控制配置为空,更新失败"); + return; + } + + synchronized (mServiceLock) { + // 内存同步+本地持久化(原子操作,确保一致性) + mServiceControlBean = newControlBean; + ControlCenterServiceBean.saveBean(this, mServiceControlBean); + LogUtils.d(TAG, "updateAndSaveServiceControlConfig: 控制配置更新并持久化完成"); + } + } + + // ================================== 业务按需初始化/停止方法(适配启用/禁用状态切换)================================= + /** + * 按需初始化核心业务(仅服务启用且未初始化时执行,避免重复创建) + */ + private void initServiceBusinessIfNeed() { + LogUtils.d(TAG, "initServiceBusinessIfNeed: 按需初始化业务"); + synchronized (mServiceLock) { + // 校验:服务启用+未初始化+服务运行中 + if (!mServiceControlBean.isEnableService() || mServiceHandler != null || !isServiceRunning || mIsDestroyed) { + LogUtils.w(TAG, "initServiceBusinessIfNeed: 无需初始化业务(禁用/已初始化/服务未运行)"); + return; + } + + // 初始化通知+配置+业务逻辑(完整启动核心流程) + initNotificationManager(); + loadDefaultConfig(); + initServiceBusinessLogic(); + LogUtils.d(TAG, "initServiceBusinessIfNeed: 核心业务初始化完成"); + } + } + + /** + * 安全停止所有核心业务(服务禁用时执行,释放所有业务资源) + */ + private void stopAllBusinessSafely() { + LogUtils.d(TAG, "stopAllBusinessSafely: 停止所有核心业务"); + synchronized (mServiceLock) { + // 停止线程+销毁Handler+释放通知+置空业务引用 + stopRemindThreadSafely(); + destroyHandler(); + stopForegroundService(); + releaseNotificationResource(); + mCurrentConfigBean = null; + mForegroundNotifyMsg = null; + LogUtils.d(TAG, "stopAllBusinessSafely: 所有核心业务已停止"); + } + } + + // ================================== 核心业务初始化方法(按依赖顺序排列)================================= + /** + * 初始化通知管理工具类,构建前台保活通知模型 + */ private void initNotificationManager() { LogUtils.d(TAG, "initNotificationManager: 初始化通知工具类"); try { - mNotificationManager = new NotificationManagerUtils(this); - mForegroundNotifyMsg = new NotificationMessage(); - mForegroundNotifyMsg.setTitle("电池提醒服务运行中"); - mForegroundNotifyMsg.setContent("后台持续监测电池状态,确保提醒及时"); - mForegroundNotifyMsg.setRemindMSG("service_running"); - // 移除无效调用:getForegroundServiceNotify() 无需提前触发,启动时自动构建 + if (mNotificationManager == null) { + mNotificationManager = new NotificationManagerUtils(this); + } + if (mForegroundNotifyMsg == null) { + mForegroundNotifyMsg = new NotificationMessage(); + mForegroundNotifyMsg.setTitle("电池提醒服务运行中"); + mForegroundNotifyMsg.setContent("后台持续监测电池状态,确保提醒及时"); + mForegroundNotifyMsg.setRemindMSG("service_running"); + // 启动前台保活通知 + startForegroundNotifyImmediately(); + } + LogUtils.d(TAG, "initNotificationManager: 通知工具类初始化完成"); } catch (Exception e) { LogUtils.e(TAG, "initNotificationManager: 初始化失败", e); - stopSelf(); // 通知初始化失败,直接停止服务,避免后续异常 } } - // 立即启动前台服务通知 - 优化:加try-catch避免启动失败崩溃 + /** + * 立即启动前台保活通知(API26+ 前台服务必需) + */ private void startForegroundNotifyImmediately() { LogUtils.d(TAG, "startForegroundNotifyImmediately: 启动前台保活通知"); if (mNotificationManager == null || mForegroundNotifyMsg == null) { - LogUtils.e(TAG, "startForegroundNotifyImmediately: 依赖组件为空,启动失败"); - stopSelf(); + LogUtils.e(TAG, "startForegroundNotifyImmediately: 通知工具类/模型为空,启动失败"); return; } @@ -224,20 +230,256 @@ public class ControlCenterService extends Service { LogUtils.d(TAG, "startForegroundNotifyImmediately: 前台通知启动成功,ID=" + NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE); } catch (Exception e) { LogUtils.e(TAG, "startForegroundNotifyImmediately: 前台通知启动异常", e); - stopSelf(); // 前台服务启动失败,停止服务(API26+前台服务必须正常启动) } } - // 初始化业务逻辑 - 优化:复用重启方法,统一线程启动逻辑 - private void initServiceBusinessLogic() { - LogUtils.d(TAG, "initServiceBusinessLogic: 初始化业务组件"); - mServiceHandler = new ControlCenterServiceHandler(this); - // 关键:复用restartRemindThreadSafely,避免重复写启动逻辑 - restartRemindThreadSafely(); - LogUtils.d(TAG, "initServiceBusinessLogic: 业务组件初始化完成"); + /** + * 重新绑定前台通知(onStartCommand触发,防止保活状态丢失) + */ + private void rebindForegroundNotify() { + if (mNotificationManager == null || mNotificationManager.getForegroundServiceNotify() == null) { + LogUtils.w(TAG, "rebindForegroundNotify: 通知工具类/前台通知为空,跳过绑定"); + return; + } + + try { + startForeground(NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE, mNotificationManager.getForegroundServiceNotify()); + LogUtils.d(TAG, "rebindForegroundNotify: 前台通知重新绑定成功"); + } catch (Exception e) { + LogUtils.e(TAG, "rebindForegroundNotify: 重新绑定前台通知异常", e); + } } - // 启动服务(静态入口)- 优化:强化包名绑定,避免隐式启动失败 + /** + * 加载默认应用配置(服务启动时兜底) + */ + private void loadDefaultConfig() { + LogUtils.d(TAG, "loadDefaultConfig: 加载默认配置"); + if (mCurrentConfigBean == null) { + mCurrentConfigBean = new AppConfigBean(); + mCurrentConfigBean.setEnableChargeReminder(true); + mCurrentConfigBean.setChargeReminderValue(80); + mCurrentConfigBean.setEnableUsageReminder(true); + mCurrentConfigBean.setUsageReminderValue(20); + mCurrentConfigBean.setBatteryDetectInterval(1000); + LogUtils.d(TAG, "loadDefaultConfig: 默认配置加载完成,充电阈值80%,耗电阈值20%"); + } else { + LogUtils.d(TAG, "loadDefaultConfig: 配置已存在,无需重复加载"); + } + } + + /** + * 初始化业务核心逻辑(Handler+提醒线程) + */ + private void initServiceBusinessLogic() { + LogUtils.d(TAG, "initServiceBusinessLogic: 初始化业务逻辑"); + if (mServiceHandler == null) { + mServiceHandler = new ControlCenterServiceHandler(this); + } + // 复用线程重启方法,统一启动逻辑 + restartRemindThreadSafely(); + LogUtils.d(TAG, "initServiceBusinessLogic: 业务逻辑初始化完成"); + } + + // ================================== 线程管理方法(安全启动/停止/重启,保障并发安全)================================= + /** + * 安全重启提醒线程(彻底销毁旧线程,创建全新实例,避免重复启动) + */ + public void restartRemindThreadSafely() { + LogUtils.d(TAG, "restartRemindThreadSafely: 开始重启提醒线程"); + synchronized (mServiceLock) { + // 服务禁用/已销毁,直接返回 + if (!isServiceRunning || mIsDestroyed || !mServiceControlBean.isEnableService()) { + LogUtils.e(TAG, "restartRemindThreadSafely: 服务未运行/已销毁/已禁用,启动失败"); + return; + } + + // 先停止旧线程 + stopRemindThreadSafely(); + + // 校验Handler,避免无效启动 + if (mServiceHandler == null) { + LogUtils.e(TAG, "restartRemindThreadSafely: Handler为空,启动失败"); + return; + } + + // 销毁旧单例,创建新线程实例 + RemindThread.destroyInstance(); + mRemindThread = RemindThread.getInstance(this, mServiceHandler); + // 同步配置并开启提醒 + syncConfigToRemindThread(); + + // 双重校验,避免线程重复启动 + if (mRemindThread != null && !mRemindThread.isAlive() && !mRemindThread.isThreadStarted()) { + try { + mRemindThread.start(); + LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功,ID=" + mRemindThread.getId()); + } catch (IllegalThreadStateException e) { + LogUtils.e(TAG, "restartRemindThreadSafely: 线程重复启动异常", e); + mRemindThread = null; + } catch (Exception e) { + LogUtils.e(TAG, "restartRemindThreadSafely: 启动线程异常", e); + mRemindThread = null; + } + } else { + LogUtils.w(TAG, "restartRemindThreadSafely: 线程已启动/实例异常,跳过启动"); + mRemindThread = null; + } + } + LogUtils.d(TAG, "restartRemindThreadSafely: 线程重启流程结束"); + } + + /** + * 安全停止提醒线程(中断休眠+等待终止+释放资源,无残留) + */ + private void stopRemindThreadSafely() { + LogUtils.d(TAG, "stopRemindThreadSafely: 开始停止提醒线程"); + if (mRemindThread == null) { + LogUtils.w(TAG, "stopRemindThreadSafely: 线程实例为空,跳过停止"); + return; + } + + try { + // 触发线程内部停止逻辑 + if (mRemindThread.isThreadStarted() || mRemindThread.isAlive()) { + mRemindThread.setIsReminding(false); + mRemindThread.stopThread(); + LogUtils.d(TAG, "stopRemindThreadSafely: 触发线程内部停止"); + + // 等待线程终止(超时1秒,避免阻塞服务) + long waitStartTime = System.currentTimeMillis(); + while (mRemindThread.isAlive()) { + if (System.currentTimeMillis() - waitStartTime > 1000) { + LogUtils.w(TAG, "stopRemindThreadSafely: 线程停止超时,强制中断"); + mRemindThread.interrupt(); + break; + } + Thread.yield(); + } + } + } catch (Exception e) { + LogUtils.e(TAG, "stopRemindThreadSafely: 停止线程异常", e); + } finally { + // 彻底释放资源 + mRemindThread = null; + RemindThread.destroyInstance(); + LogUtils.d(TAG, "stopRemindThreadSafely: 线程停止完成,资源已释放"); + } + } + + /** + * 同步配置到提醒线程(原子操作,避免并发修改) + */ + private void syncConfigToRemindThread() { + LogUtils.d(TAG, "syncConfigToRemindThread: 同步配置到线程"); + synchronized (mServiceLock) { + if (mRemindThread == null || mCurrentConfigBean == null) { + LogUtils.e(TAG, "syncConfigToRemindThread: 线程/配置为空,同步失败"); + return; + } + mRemindThread.setAppConfigBean(mCurrentConfigBean); + mRemindThread.setIsReminding(true); + LogUtils.d(TAG, "syncConfigToRemindThread: 配置同步完成"); + } + } + + // ================================== 外部指令处理方法(接收重启/更新配置指令,移除控制Bean传递)================================= + /** + * 处理外部发送的服务指令(如重启线程、更新配置) + */ + private void handleExternalCommand(Intent intent) { + LogUtils.d(TAG, "handleExternalCommand: 开始处理外部指令"); + if (intent == null) { + LogUtils.e(TAG, "handleExternalCommand: Intent为空,跳过处理"); + return; + } + + String action = intent.getAction(); + if (TextUtils.isEmpty(action)) { + LogUtils.e(TAG, "handleExternalCommand: 指令Action为空,跳过处理"); + return; + } + + // 处理线程重启指令(仅同步业务配置,服务控制配置从本地读取) + if (ACTION_RESTART_REMIND_THREAD.equals(action)) { + // 读取本地最新控制配置,确保状态同步 + loadLatestServiceControlConfig(); + // 仅启用状态下,更新业务配置并重启线程 + synchronized (mServiceLock) { + if (mServiceControlBean.isEnableService()) { + AppConfigBean newConfig = (AppConfigBean) intent.getSerializableExtra(EXTRA_APP_CONFIG_BEAN); + if (newConfig != null) { + mCurrentConfigBean = newConfig; + LogUtils.d(TAG, "handleExternalCommand: 收到线程重启指令,已更新业务配置"); + restartRemindThreadSafely(); + } else { + LogUtils.e(TAG, "handleExternalCommand: 新业务配置为空,重启线程失败"); + } + } else { + LogUtils.w(TAG, "handleExternalCommand: 服务已禁用,忽略线程重启指令"); + } + } + } + LogUtils.d(TAG, "handleExternalCommand: 外部指令处理完成"); + } + + // ================================== 服务资源释放方法(顺序化释放,防内存泄漏)================================= + /** + * 停止前台服务(取消通知+移除前台状态) + */ + private void stopForegroundService() { + LogUtils.d(TAG, "stopForegroundService: 停止前台服务"); + // 取消前台通知 + if (mNotificationManager != null) { + mNotificationManager.cancelForegroundServiceNotify(); + } + // 移除前台服务状态 + try { + stopForeground(STOP_FOREGROUND_REMOVE); + LogUtils.d(TAG, "stopForegroundService: 前台服务状态移除成功"); + } catch (Exception e) { + LogUtils.e(TAG, "stopForegroundService: 移除前台状态异常", e); + } + } + + /** + * 销毁Handler(移除所有任务,避免内存泄漏) + */ + private void destroyHandler() { + LogUtils.d(TAG, "destroyHandler: 销毁服务Handler"); + if (mServiceHandler != null) { + mServiceHandler.removeCallbacksAndMessages(null); + mServiceHandler = null; + LogUtils.d(TAG, "destroyHandler: Handler销毁完成"); + } + } + + /** + * 释放通知资源(调用工具类释放方法) + */ + private void releaseNotificationResource() { + LogUtils.d(TAG, "releaseNotificationResource: 释放通知资源"); + if (mNotificationManager != null) { + mNotificationManager.release(); + mNotificationManager = null; + LogUtils.d(TAG, "releaseNotificationResource: 通知资源释放完成"); + } + } + + /** + * 置空所有引用,帮助GC回收 + */ + private void clearAllReferences() { + mForegroundNotifyMsg = null; + mCurrentConfigBean = null; + mServiceControlBean = null; + LogUtils.d(TAG, "clearAllReferences: 所有引用已置空"); + } + + // ================================== 静态工具方法(服务启动/停止/状态判断/电池优化,简化控制Bean传递)================================= + /** + * 启动服务(静态入口,从本地读取控制配置,显式绑定包名,适配API30+) + */ public static void startControlCenterService(Context context) { LogUtils.d(TAG, "startControlCenterService: 启动服务入口"); if (context == null) { @@ -245,12 +487,15 @@ public class ControlCenterService extends Service { return; } if (isServiceRunning(context, ControlCenterService.class)) { - LogUtils.d(TAG, "startControlCenterService: 服务已运行,跳过启动"); + LogUtils.d(TAG, "startControlCenterService: 服务已运行,同步本地控制配置"); + // 服务已运行,触发onStartCommand同步最新配置 + sendSyncConfigCommand(context); return; } + Intent intent = new Intent(context, ControlCenterService.class); - intent.setPackage(context.getPackageName()); // 显式绑定包名,避免组件冲突 - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); // 兼容服务被停止的场景 + intent.setPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(intent); @@ -263,7 +508,9 @@ public class ControlCenterService extends Service { } } - // 停止服务(静态入口)- 优化:强化包名绑定 + /** + * 停止服务(静态入口,显式绑定包名) + */ public static void stopControlCenterService(Context context) { LogUtils.d(TAG, "stopControlCenterService: 停止服务入口"); if (context == null) { @@ -274,52 +521,121 @@ public class ControlCenterService extends Service { LogUtils.d(TAG, "stopControlCenterService: 服务未运行,跳过停止"); return; } + Intent intent = new Intent(context, ControlCenterService.class); intent.setPackage(context.getPackageName()); context.stopService(intent); LogUtils.d(TAG, "stopControlCenterService: 服务停止指令发送成功"); } - // 判断服务是否运行 - 优化:适配API30+ ActivityManager限制 - private static boolean isServiceRunning(Context context, Class serviceClass) { - if (context == null || serviceClass == null) return false; - ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - if (am == null) return false; + /** + * 新增:更新服务控制配置(本地持久化,动态切换服务启用/禁用状态) + */ + public static void updateServiceControlConfig(Context context, ControlCenterServiceBean controlBean) { + LogUtils.d(TAG, "updateServiceControlConfig: 更新服务控制配置,新状态=" + controlBean.isEnableService()); + if (context == null || controlBean == null) { + LogUtils.e(TAG, "updateServiceControlConfig: Context/控制配置为空,更新失败"); + return; + } - // API30+ 限制:getRunningServices返回空,改用getRunningTasks兼容(或用JobScheduler,此处先兼容) + // 第一步:本地持久化配置 + ControlCenterServiceBean.saveBean(context, controlBean); + LogUtils.d(TAG, "updateServiceControlConfig: 控制配置已持久化"); + + // 第二步:若服务已运行,触发同步(让服务立即生效) + if (isServiceRunning(context, ControlCenterService.class)) { + sendSyncConfigCommand(context); + LogUtils.d(TAG, "updateServiceControlConfig: 服务已运行,已触发配置同步"); + } else { + // 服务未运行,按需启动服务(根据新配置决定是否启用业务) + startControlCenterService(context); + LogUtils.d(TAG, "updateServiceControlConfig: 服务未运行,已启动服务并加载新配置"); + } + } + + /** + * 新增:发送配置同步指令(触发服务读取本地最新配置,立即生效) + */ + private static void sendSyncConfigCommand(Context context) { + Intent intent = new Intent(context, ControlCenterService.class); + intent.setPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent); + } else { + context.startService(intent); + } + } catch (Exception e) { + LogUtils.e(TAG, "sendSyncConfigCommand: 发送同步指令异常", e); + } + } + + /** + * 判断服务是否运行(适配API30+ ActivityManager限制) + */ + private static boolean isServiceRunning(Context context, Class serviceClass) { + if (context == null || serviceClass == null) { + LogUtils.w(TAG, "isServiceRunning: Context/服务类为空,返回false"); + return false; + } + + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + if (am == null) { + LogUtils.e(TAG, "isServiceRunning: ActivityManager获取失败,返回false"); + return false; + } + + // API30+ 兼容:getRunningServices失效,通过进程状态判断 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { - // 替代方案1:通过进程名判断(简单兼容) String packageName = context.getPackageName(); List processes = am.getRunningAppProcesses(); - for (ActivityManager.RunningAppProcessInfo process : processes) { - if (process.processName.equals(packageName) && process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { - return true; + if (processes != null && !processes.isEmpty()) { + for (ActivityManager.RunningAppProcessInfo process : processes) { + if (packageName.equals(process.processName) + && process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { + return true; + } } } - // 替代方案2:兜底判断(若进程在运行,且服务已启动,视为运行中) + // 兜底:进程存活则视为服务运行(适配部分机型) return true; } catch (Exception e) { LogUtils.e(TAG, "isServiceRunning: API30+ 判断服务状态异常", e); return false; } } else { + // API30-:通过RunningServiceInfo判断 List runningServices = am.getRunningServices(100); - for (ActivityManager.RunningServiceInfo info : runningServices) { - if (serviceClass.getName().equals(info.service.getClassName())) { - return true; + if (runningServices != null && !runningServices.isEmpty()) { + String serviceClassName = serviceClass.getName(); + for (ActivityManager.RunningServiceInfo info : runningServices) { + if (serviceClassName.equals(info.service.getClassName())) { + return true; + } } } return false; } } - // 引导开启忽略电池优化 - 优化:加权限判断+异常捕获 + /** + * 引导用户开启忽略电池优化(避免服务被后台杀死,适配API23+) + */ public static void checkIgnoreBatteryOptimization(Context context) { - if (context == null) return; + LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 检查电池优化状态"); + if (context == null) { + LogUtils.e(TAG, "checkIgnoreBatteryOptimization: Context为空,跳过检查"); + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - if (powerManager == null) return; + if (powerManager == null) { + LogUtils.e(TAG, "checkIgnoreBatteryOptimization: PowerManager获取失败,跳过检查"); + return; + } try { boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); @@ -328,99 +644,28 @@ public class ControlCenterService extends Service { intent.setData(Uri.parse("package:" + context.getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); context.startActivity(intent); - LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 引导开启忽略电池优化"); + LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已引导用户开启忽略电池优化"); + } else { + LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已开启忽略电池优化,无需操作"); } } catch (Exception e) { LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 引导优化异常(部分机型限制)", e); } + } else { + LogUtils.d(TAG, "checkIgnoreBatteryOptimization: API<23,无需检查电池优化"); } } - // ====================== 核心 Getter/Setter 方法 ====================== - public RemindThread getRemindThread() { - return mRemindThread; - } - - public void setCurrentConfigBean(AppConfigBean configBean) { - if (configBean != null) { - synchronized (mServiceLock) { - this.mCurrentConfigBean = configBean; - syncConfigToRemindThread(); // 更新配置后自动同步线程 - LogUtils.d(TAG, "setCurrentConfigBean: 服务配置更新成功"); - } - } - } - - public NotificationManagerUtils getNotificationManager() { - return mNotificationManager; - } - - public NotificationMessage getForegroundNotifyMsg() { - return mForegroundNotifyMsg; - } - - public AppConfigBean getCurrentConfigBean() { - return mCurrentConfigBean; - } - - public boolean isDestroyed() { - return mIsDestroyed; - } - - // ====================== 服务销毁逻辑 - 优化:顺序化释放,避免资源泄漏 ====================== - @Override - public void onDestroy() { - super.onDestroy(); - LogUtils.d(TAG, "onDestroy: 服务销毁,释放所有资源"); - synchronized (mServiceLock) { // 加锁,避免与重启线程操作冲突 - isServiceRunning = false; - mIsDestroyed = true; - - // 1. 停止前台服务(顺序:先取消通知 → 再移除前台状态) - if (mNotificationManager != null) { - mNotificationManager.cancelForegroundServiceNotify(); // 取消前台通知 - } - try { - stopForeground(STOP_FOREGROUND_REMOVE); // 移除前台服务状态(API26+) - LogUtils.d(TAG, "onDestroy: 前台服务状态已移除"); - } catch (Exception e) { - LogUtils.e(TAG, "onDestroy: 移除前台状态异常", e); - } - - // 2. 安全停止提醒线程(彻底终止,无残留) - stopRemindThreadSafely(); - - // 3. 销毁Handler(移除所有任务,避免内存泄漏) - if (mServiceHandler != null) { - mServiceHandler.removeCallbacksAndMessages(null); - mServiceHandler = null; - LogUtils.d(TAG, "onDestroy: Handler已销毁"); - } - - // 4. 释放通知资源(调用工具类释放方法) - if (mNotificationManager != null) { - mNotificationManager.release(); - mNotificationManager = null; - LogUtils.d(TAG, "onDestroy: 通知资源已释放"); - } - - // 5. 置空所有引用,帮助GC回收 - mForegroundNotifyMsg = null; - mCurrentConfigBean = null; - } - - LogUtils.d(TAG, "onDestroy: 服务销毁完成"); - } - - @Override - public IBinder onBind(Intent intent) { - return null; // 无需绑定服务 - } - - // 更新服务配置(对外入口)- 优化:强化兼容性,加异常捕获 + /** + * 更新服务配置(对外静态入口,触发线程重启,控制配置从本地读取) + */ public static void updateStatus(Context context, AppConfigBean configBean) { - if (context == null || configBean == null) return; - LogUtils.d(TAG, "updateStatus: 发送配置更新指令"); + LogUtils.d(TAG, "updateStatus: 发送业务配置更新指令"); + if (context == null || configBean == null) { + LogUtils.e(TAG, "updateStatus: Context/业务配置为空,发送失败"); + return; + } + Intent intent = new Intent(context, ControlCenterService.class); intent.setAction(ACTION_RESTART_REMIND_THREAD); intent.putExtra(EXTRA_APP_CONFIG_BEAN, (Serializable) configBean); @@ -433,10 +678,71 @@ public class ControlCenterService extends Service { } else { context.startService(intent); } - LogUtils.d(TAG, "updateStatus: 配置更新指令发送成功"); + LogUtils.d(TAG, "updateStatus: 业务配置更新指令发送成功"); } catch (Exception e) { LogUtils.e(TAG, "updateStatus: 发送指令异常", e); } } + + // ================================== Getter/Setter 方法(按需提供,线程安全)================================= + public RemindThread getRemindThread() { + synchronized (mServiceLock) { + return mRemindThread; + } + } + + public void setCurrentConfigBean(AppConfigBean configBean) { + LogUtils.d(TAG, "setCurrentConfigBean: 更新服务配置"); + if (configBean != null) { + synchronized (mServiceLock) { + this.mCurrentConfigBean = configBean; + syncConfigToRemindThread(); + } + LogUtils.d(TAG, "setCurrentConfigBean: 配置更新成功"); + } else { + LogUtils.e(TAG, "setCurrentConfigBean: 配置为空,更新失败"); + } + } + + // 更新服务控制配置(内存+本地持久化同步) + public void setServiceControlBean(ControlCenterServiceBean controlBean) { + LogUtils.d(TAG, "setServiceControlBean: 更新服务控制配置"); + if (controlBean != null) { + updateAndSaveServiceControlConfig(controlBean); + } else { + LogUtils.e(TAG, "setServiceControlBean: 控制配置为空,更新失败"); + } + } + + // 获取服务控制配置(外部查询启用状态) + public ControlCenterServiceBean getServiceControlBean() { + synchronized (mServiceLock) { + return mServiceControlBean; + } + } + + public NotificationManagerUtils getNotificationManager() { + synchronized (mServiceLock) { + return mNotificationManager; + } + } + + public NotificationMessage getForegroundNotifyMsg() { + synchronized (mServiceLock) { + return mForegroundNotifyMsg; + } + } + + public AppConfigBean getCurrentConfigBean() { + synchronized (mServiceLock) { + return mCurrentConfigBean; + } + } + + public boolean isDestroyed() { + synchronized (mServiceLock) { + return mIsDestroyed; + } + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java index af78d63..73b8c25 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java @@ -10,7 +10,7 @@ import java.lang.ref.WeakReference; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/12/17 13:38 - * @Describe 提醒线程(单例模式):统一管理充电/耗电提醒逻辑,适配Java7+API30+,保障线程安全与内存安全 + * @Describe 提醒线程(单例模式):统一管理充电/耗电提醒逻辑,适配Java7+API30+,发送提醒时同步携带当前电量+充电状态 */ public class RemindThread extends Thread { // ================================== 静态常量(置顶,统一管理魔法值)================================= @@ -18,7 +18,7 @@ public class RemindThread extends Thread { private static final long INIT_DELAY_TIME = 500L; // 初始化延迟(ms,等待服务就绪) private static final int MIN_SLEEP_TIME = 500; // 最小检测间隔(ms,防高频轮询) private static final int DEFAULT_SLEEP_TIME = 1000; // 默认检测间隔(ms) - private static final long REMIND_INTERVAL = 30000L; // 重复提醒间隔(ms,防骚扰) + private static final long REMIND_INTERVAL = 3000L; // 重复提醒间隔(ms,防骚扰) private static final int CONFIG_RETRY_MAX = 3; // 配置同步重试次数(兜底) // ================================== 单例核心(线程安全,避免复用旧实例)================================= @@ -166,17 +166,17 @@ public class RemindThread extends Thread { continue; } - // 充电提醒触发 + // 充电提醒触发(新增:携带当前电量+充电状态发送) if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) { - LogUtils.d(TAG, "触发充电提醒:电量" + quantityOfElectricity + "≥阈值" + chargeReminderValue); - sendNotificationMessage("+"); + LogUtils.d(TAG, "触发充电提醒:充电中,电量" + quantityOfElectricity + "≥阈值" + chargeReminderValue); + sendNotificationMessage("+", quantityOfElectricity, isCharging); lastRemindTime = currentTime; startRemindRecoveryTimer(); } - // 耗电提醒触发 + // 耗电提醒触发(新增:携带当前电量+充电状态发送) else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { - LogUtils.d(TAG, "触发耗电提醒:电量" + quantityOfElectricity + "≤阈值" + usageReminderValue); - sendNotificationMessage("-"); + LogUtils.d(TAG, "触发耗电提醒:未充电,电量" + quantityOfElectricity + "≤阈值" + usageReminderValue); + sendNotificationMessage("-", quantityOfElectricity, isCharging); lastRemindTime = currentTime; startRemindRecoveryTimer(); } @@ -228,10 +228,13 @@ public class RemindThread extends Thread { } /** - * 发送提醒消息(弱引用Handler+Message复用,防泄漏) + * 发送提醒消息(新增:携带当前电量+充电状态,弱引用Handler+Message复用,防泄漏) + * @param type 提醒类型(+:充电提醒,-:耗电提醒) + * @param battery 当前电量(0-100) + * @param isCharging 当前充电状态(true=充电中) */ - private void sendNotificationMessage(String content) { - LogUtils.d(TAG, "准备发送提醒消息:" + content); + private void sendNotificationMessage(String type, int battery, boolean isCharging) { + LogUtils.d(TAG, "准备发送提醒消息:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging); if (isExist || !isReminding) { LogUtils.d(TAG, "线程退出/提醒关闭,跳过发送"); return; @@ -243,10 +246,14 @@ public class RemindThread extends Thread { return; } - Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT, content); + // 构建消息:what=提醒标识,obj=类型,arg1=当前电量,arg2=充电状态(0=未充电,1=充电中) + Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT); + message.obj = type; + message.arg1 = battery; + message.arg2 = isCharging ? 1 : 0; // boolean转int(Message无boolean参数,兼容Java7) try { handler.sendMessage(message); - LogUtils.d(TAG, "提醒消息发送成功"); + LogUtils.d(TAG, "提醒消息发送成功:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging); } catch (Exception e) { LogUtils.e(TAG, "消息发送异常", e); if (message != null) message.recycle();