diff --git a/powerbell/build.properties b/powerbell/build.properties index 3b75d1f..d2c9587 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Dec 17 08:23:18 GMT 2025 +#Wed Dec 17 08:49:06 GMT 2025 stageCount=10 libraryProject= baseVersion=15.14 publishVersion=15.14.9 -buildCount=16 +buildCount=18 baseBetaVersion=15.14.10 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java index 91db82f..129b3ad 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java @@ -30,16 +30,15 @@ import cc.winboll.studio.powerbell.activities.SettingsActivity; import cc.winboll.studio.powerbell.activities.WinBoLLActivity; import cc.winboll.studio.powerbell.models.AppConfigBean; import cc.winboll.studio.powerbell.models.BackgroundBean; +import cc.winboll.studio.powerbell.models.ControlCenterServiceBean; import cc.winboll.studio.powerbell.services.ControlCenterService; +import cc.winboll.studio.powerbell.threads.RemindThread; import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity; import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.PermissionUtils; import cc.winboll.studio.powerbell.utils.ServiceUtils; -import cc.winboll.studio.powerbell.views.BackgroundView; import cc.winboll.studio.powerbell.views.MainContentView; -import cc.winboll.studio.powerbell.views.VerticalSeekBar; -import java.io.Serializable; /** * @Author ZhanGSKen&豆包大模型 @@ -59,6 +58,7 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV public static final int MSG_CURRENTVALUEBATTERY = 1; // 更新当前电量 public static final int MSG_LOAD_BACKGROUND = 2; // 加载背景 private static final int MSG_RESTART_REMIND_THREAD = 3; // 重启RemindThread线程 + private static final int MSG_UPDATE_SERVICE_SWITCH = 4; // 更新服务开关UI(新增) // ======================== 静态成员(全局共享,严格管控生命周期)======================== public static MainActivity sMainActivity; // 全局Activity实例(销毁时必须置空) @@ -79,6 +79,8 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV // 资源与菜单 private Menu mMenu; private Drawable mFrameDrawable; // 框架背景资源(适配API30主题) + // 新增:服务控制核心Bean(本地持久化,管理服务自启动状态) + private ControlCenterServiceBean mServiceControlBean; // ======================== 生命周期方法(按系统调用顺序排列,逻辑闭环)======================== @Override @@ -124,15 +126,28 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV if (sGlobalHandler != null) { sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); LogUtils.d(TAG, "onResume: 发送加载背景消息"); + // 新增:恢复时同步服务开关UI(避免开关状态与实际服务状态不一致) + sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH); } // 2. 恢复广告(非核心,容错处理) if (mADsBannerView != null) { mADsBannerView.resumeADs(this); LogUtils.d(TAG, "onResume: 恢复广告展示"); } + // 3. 关键调整:应用切前台,设置RemindThread为前台状态(开启提醒) + setRemindThreadForeground(true); LogUtils.d(TAG, "onResume: 退出"); } + @Override + protected void onPause() { + LogUtils.d(TAG, "onPause: 进入"); + super.onPause(); + // 关键调整:应用切后台,设置RemindThread为后台状态(停止提醒+重置无效电量) + setRemindThreadForeground(false); + LogUtils.d(TAG, "onPause: 退出"); + } + @Override protected void onDestroy() { LogUtils.d(TAG, "onDestroy: 进入"); @@ -149,24 +164,28 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV mMainContentView = null; LogUtils.d(TAG, "onDestroy: 释放核心视图资源完成"); } - // 3. 置空全局实例(杜绝内存泄漏,必须执行) + // 3. 关键调整:Activity销毁,强制销毁RemindThread单例(彻底停止线程) + RemindThread.destroyInstance(); + LogUtils.d(TAG, "onDestroy: 强制销毁RemindThread单例完成"); + // 4. 置空全局实例(杜绝内存泄漏,必须执行) sMainActivity = null; - // 4. 清理Handler(移除所有消息,避免Activity销毁后回调) + // 5. 清理Handler(移除所有消息,避免Activity销毁后回调) if (sGlobalHandler != null) { sGlobalHandler.removeCallbacksAndMessages(null); sGlobalHandler = null; LogUtils.d(TAG, "onDestroy: 清理全局Handler完成"); } - // 5. 释放Drawable资源(断开回调,辅助GC回收) + // 6. 释放Drawable资源(断开回调,辅助GC回收) if (mFrameDrawable != null) { mFrameDrawable.setCallback(null); mFrameDrawable = null; LogUtils.d(TAG, "onDestroy: 释放Drawable资源完成"); } - // 6. 置空所有辅助实例(辅助GC,避免残留引用) + // 7. 置空所有辅助实例(辅助GC,避免残留引用) mPermissionUtils = null; mAppConfigUtils = null; mBgSourceUtils = null; + mServiceControlBean = null; // 新增:置空服务控制Bean mMenu = null; mApplication = null; mToolbar = null; @@ -314,6 +333,10 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV case MSG_RESTART_REMIND_THREAD: AppConfigBean configBean = (AppConfigBean) msg.obj; sMainActivity.restartRemindThread(configBean); + break; + // 新增:更新服务开关UI(同步实际服务状态,避免开关与服务状态不一致) + case MSG_UPDATE_SERVICE_SWITCH: + sMainActivity.updateServiceSwitchUI(); break; } super.handleMessage(msg); @@ -366,7 +389,15 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV mApplication = (App) getApplication(); mAppConfigUtils = AppConfigUtils.getInstance(getApplicationContext()); mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity()); - LogUtils.d(TAG, "initCoreUtilsAsync: 核心工具类初始化完成"); + // 新增:初始化服务控制Bean(读取本地持久化状态,管理自启动) + mServiceControlBean = ControlCenterServiceBean.loadBean(getApplicationContext(), ControlCenterServiceBean.class); + if (mServiceControlBean == null) { + // 本地无配置,默认禁用服务(与服务初始化逻辑一致) + mServiceControlBean = new ControlCenterServiceBean(false); + ControlCenterServiceBean.saveBean(getApplicationContext(), mServiceControlBean); + LogUtils.d(TAG, "initCoreUtilsAsync: 本地无服务控制配置,创建默认禁用配置"); + } + LogUtils.d(TAG, "initCoreUtilsAsync: 核心工具类+服务控制Bean初始化完成"); // 切换主线程更新UI(Java7显式runOnUiThread) runOnUiThread(new Runnable() { @@ -387,6 +418,8 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV updateViewData(); // 更新视图数据 checkServiceAsync(); // 异步检查服务状态 sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); // 加载背景 + // 新增:初始化服务开关UI(确保开关状态与本地配置一致) + sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH); } }); LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程结束"); @@ -483,6 +516,27 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV LogUtils.d(TAG, "setMainLayoutBackgroundColor: 退出"); } + /** + * 新增:更新服务开关UI(同步「本地配置+实际服务状态」,避免开关与服务状态不一致) + */ + private void updateServiceSwitchUI() { + LogUtils.d(TAG, "updateServiceSwitchUI: 进入"); + if (mMainContentView == null || mServiceControlBean == null) { + LogUtils.e(TAG, "updateServiceSwitchUI: 核心视图/服务控制Bean为空,跳过更新"); + return; + } + // 开关状态 = 本地服务控制配置启用状态(自启动状态) + boolean isServiceEnabled = mServiceControlBean.isEnableService(); + // 禁用开关点击(避免更新UI时触发重复回调) + mMainContentView.setServiceSwitchEnabled(false); + // 更新开关状态 + mMainContentView.setServiceSwitchChecked(isServiceEnabled); + // 启用开关点击 + mMainContentView.setServiceSwitchEnabled(true); + LogUtils.d(TAG, "updateServiceSwitchUI: 服务开关UI更新完成,当前状态=" + isServiceEnabled); + LogUtils.d(TAG, "updateServiceSwitchUI: 退出"); + } + // ======================== 服务与线程管理方法(业务核心,统一归类)======================== /** * 异步检查服务状态(子线程执行,避免主线程阻塞,适配API30前台服务规范) @@ -493,23 +547,18 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV @Override public void run() { LogUtils.d(TAG, "checkServiceAsync: 异步线程启动"); - if (mAppConfigUtils == null || isFinishing()) { - LogUtils.e(TAG, "checkServiceAsync: 配置工具类/Activity无效,跳过检查"); + if (mAppConfigUtils == null || mServiceControlBean == null || isFinishing()) { + LogUtils.e(TAG, "checkServiceAsync: 配置工具类/服务控制Bean/Activity无效,跳过检查"); return; } - // 服务启用且未运行时,启动前台服务(API30必须前台服务,小米保活优化) - if (mAppConfigUtils.isServiceEnabled() && !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) { + // 服务启用(本地配置)且未运行时,启动前台服务(API30必须前台服务,小米保活优化) + if (mServiceControlBean.isEnableService() && !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) { LogUtils.d(TAG, "checkServiceAsync: 服务未运行,启动前台服务"); runOnUiThread(new Runnable() { @Override public void run() { if (!isFinishing()) { - Intent serviceIntent = new Intent(getActivity(), ControlCenterService.class); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - getActivity().startForegroundService(serviceIntent); - } else { - getActivity().startService(serviceIntent); - } + ControlCenterService.startControlCenterService(getActivity()); } } }); @@ -525,24 +574,90 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV */ private void restartRemindThread(AppConfigBean configBean) { LogUtils.d(TAG, "restartRemindThread: 进入"); - if (configBean == null || !ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) { - LogUtils.e(TAG, "restartRemindThread: 配置为空/服务未运行,跳过重启"); + if (configBean == null || mServiceControlBean == null) { + LogUtils.e(TAG, "restartRemindThread: 配置为空/服务控制Bean为空,跳过重启"); return; } - // 发送重启指令到服务(通过Intent传递最新配置) - Intent restartIntent = new Intent(this, ControlCenterService.class); - restartIntent.setAction(ControlCenterService.ACTION_RESTART_REMIND_THREAD); - restartIntent.putExtra(ControlCenterService.EXTRA_APP_CONFIG_BEAN, (Serializable) configBean); - // 适配API26+前台服务启动规范 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(restartIntent); - } else { - startService(restartIntent); + // 服务未启用时,不重启线程(直接返回) + if (!mServiceControlBean.isEnableService()) { + LogUtils.w(TAG, "restartRemindThread: 服务已禁用,跳过线程重启"); + return; } + // 服务未运行时,先启动服务 + if (!ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) { + ControlCenterService.startControlCenterService(this); + LogUtils.d(TAG, "restartRemindThread: 服务未运行,先启动服务"); + } + // 关键优化:重启前先销毁旧线程,避免线程复用导致的状态脏数据 + RemindThread.destroyInstance(); + LogUtils.d(TAG, "restartRemindThread: 销毁旧线程完成,准备启动新线程"); + + // 发送重启指令到服务(通过Intent传递最新配置) + ControlCenterService.updateStatus(this, configBean); LogUtils.d(TAG, "restartRemindThread: 发送线程重启指令到服务完成"); LogUtils.d(TAG, "restartRemindThread: 退出"); } + /** + * 关键新增:设置RemindThread前台/后台状态(核心修复无限提醒问题) + * @param isForeground true=前台(开启提醒),false=后台(停止提醒) + */ + private void setRemindThreadForeground(boolean isForeground) { + LogUtils.d(TAG, "setRemindThreadForeground: 进入,前台状态=" + isForeground); + // 服务未运行时,跳过状态设置(线程未启动) + if (!ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) { + LogUtils.w(TAG, "setRemindThreadForeground: 服务未运行,跳过状态设置"); + return; + } + // 调用RemindThread新增方法,设置前台状态(线程安全) + try { + RemindThread.getInstance(this, null).setAppForeground(isForeground); + LogUtils.d(TAG, "setRemindThreadForeground: RemindThread状态设置完成"); + } catch (IllegalArgumentException e) { + // 首次启动时Handler可能未就绪,容错处理(服务会后续同步状态) + LogUtils.w(TAG, "setRemindThreadForeground: 线程未初始化完成,后续服务会同步状态", e); + } + LogUtils.d(TAG, "setRemindThreadForeground: 退出"); + } + + /** + * 新增:切换服务启用/禁用状态(核心修复:持久化配置+启停服务+更新UI+重启线程) + * @param isEnable true=启用服务(自启动+启动服务),false=禁用服务(停止服务) + */ + private void toggleServiceEnableState(boolean isEnable) { + LogUtils.d(TAG, "toggleServiceEnableState: 进入,目标状态=" + isEnable); + if (mServiceControlBean == null) { + LogUtils.e(TAG, "toggleServiceEnableState: 服务控制Bean为空,切换失败"); + return; + } + // 1. 更新本地服务控制配置(持久化,实现自启动) + mServiceControlBean.setIsEnableService(isEnable); + ControlCenterService.updateServiceControlConfig(this, mServiceControlBean); + LogUtils.d(TAG, "toggleServiceEnableState: 服务控制配置已持久化,自启动状态=" + isEnable); + + // 2. 启停服务(通过服务静态方法,适配API30前台服务规范) + if (isEnable) { + // 启用:启动前台服务+检查电池优化(避免服务被杀死) + ControlCenterService.startControlCenterService(this); + ControlCenterService.checkIgnoreBatteryOptimization(this); + LogUtils.d(TAG, "toggleServiceEnableState: 服务已启动,已引导忽略电池优化"); + } else { + // 禁用:停止服务(服务内部会自动停止线程+释放资源) + ControlCenterService.stopControlCenterService(this); + LogUtils.d(TAG, "toggleServiceEnableState: 服务已停止"); + } + + // 3. 服务启用时,重启线程(确保线程使用最新配置) + if (isEnable && mAppConfigUtils != null && mAppConfigUtils.mAppConfigBean != null) { + sendRestartRemindThreadMessage(); + } + + // 4. 更新服务开关UI(确保开关状态与实际服务状态一致) + sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH); + LogUtils.d(TAG, "toggleServiceEnableState: 服务状态切换完成"); + LogUtils.d(TAG, "toggleServiceEnableState: 退出"); + } + // ======================== 页面跳转方法(封装逻辑,减少冗余)======================== /** * 启动关于页面(封装跳转逻辑,统一参数传递,便于维护) @@ -670,7 +785,8 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV @Override public void onServiceSwitchChanged(boolean isChecked) { LogUtils.d(TAG, "onServiceSwitchChanged: 状态=" + isChecked); - sendRestartRemindThreadMessage(); + // 核心修复:点击服务开关,触发「配置持久化+服务启停+UI更新」全链路逻辑 + toggleServiceEnableState(isChecked); } @Override 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 5cdd330..65313ed 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 @@ -31,6 +31,9 @@ public class ControlCenterService extends Service { // 服务指令常量(带包名前缀,防止冲突) 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"; + // 新增:应用前台/后台状态同步指令(用于MainActivity与服务联动) + public static final String ACTION_SYNC_APP_FOREGROUND_STATE = "cc.winboll.studio.powerbell.action.SYNC_APP_FOREGROUND_STATE"; + public static final String EXTRA_APP_FOREGROUND_STATE = "cc.winboll.studio.powerbell.extra.APP_FOREGROUND_STATE"; // ================================== 核心成员变量(按依赖优先级排序,私有封装)================================= // 服务控制核心(本地持久化管理,替代Intent传递) @@ -42,6 +45,8 @@ public class ControlCenterService extends Service { private AppConfigBean mCurrentConfigBean; // 当前应用配置 // 前台服务相关 private NotificationMessage mForegroundNotifyMsg; // 前台保活通知模型 + // 新增:应用前台状态标记(内存缓存,同步MainActivity状态) + private boolean mIsAppForeground = false; // 状态标记(并发安全) private boolean isServiceRunning = false; // 服务运行状态 private boolean mIsDestroyed = false; // 服务销毁标记 @@ -81,6 +86,8 @@ public class ControlCenterService extends Service { handleExternalCommand(intent); rebindForegroundNotify(); initServiceBusinessIfNeed(); // 按需初始化业务(避免重复初始化) + // 新增:业务初始化完成后,同步应用前台状态到线程(防止状态丢失) + syncAppForegroundStateToThread(); LogUtils.d(TAG, "onStartCommand: 核心逻辑执行完成,返回START_STICKY"); return START_STICKY; } else { @@ -100,6 +107,7 @@ public class ControlCenterService extends Service { synchronized (mServiceLock) { isServiceRunning = false; mIsDestroyed = true; + mIsAppForeground = false; // 新增:重置前台状态 } // 顺序释放资源(前台服务→线程→Handler→通知→配置→控制Bean) stopForegroundService(); @@ -187,6 +195,7 @@ public class ControlCenterService extends Service { releaseNotificationResource(); mCurrentConfigBean = null; mForegroundNotifyMsg = null; + mIsAppForeground = false; // 新增:重置前台状态 LogUtils.d(TAG, "stopAllBusinessSafely: 所有核心业务已停止"); } } @@ -308,12 +317,14 @@ public class ControlCenterService extends Service { mRemindThread = RemindThread.getInstance(this, mServiceHandler); // 同步配置并开启提醒 syncConfigToRemindThread(); + // 新增:同步应用前台状态(确保新线程启动时状态正确) + syncAppForegroundStateToThread(); // 双重校验,避免线程重复启动 if (mRemindThread != null && !mRemindThread.isAlive() && !mRemindThread.isThreadStarted()) { try { mRemindThread.start(); - LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功,ID=" + mRemindThread.getId()); + LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功,ID=" + mRemindThread.getId() + ",前台状态=" + mIsAppForeground); } catch (IllegalThreadStateException e) { LogUtils.e(TAG, "restartRemindThreadSafely: 线程重复启动异常", e); mRemindThread = null; @@ -383,9 +394,26 @@ public class ControlCenterService extends Service { } } - // ================================== 外部指令处理方法(接收重启/更新配置指令,移除控制Bean传递)================================= /** - * 处理外部发送的服务指令(如重启线程、更新配置) + * 新增:同步应用前台状态到RemindThread(核心联动逻辑) + * 确保线程实时感知应用前台/后台状态,控制提醒开关 + */ + private void syncAppForegroundStateToThread() { + LogUtils.d(TAG, "syncAppForegroundStateToThread: 同步前台状态,当前状态=" + mIsAppForeground); + synchronized (mServiceLock) { + if (mRemindThread == null || mIsDestroyed || !isServiceRunning) { + LogUtils.w(TAG, "syncAppForegroundStateToThread: 线程未启动/服务已销毁,同步失败"); + return; + } + // 调用RemindThread新增方法,同步前台状态(线程安全) + mRemindThread.setAppForeground(mIsAppForeground); + LogUtils.d(TAG, "syncAppForegroundStateToThread: 前台状态同步完成"); + } + } + + // ================================== 外部指令处理方法(新增前台状态同步指令,完善链路)================================= + /** + * 处理外部发送的服务指令(如重启线程、更新配置、同步前台状态) */ private void handleExternalCommand(Intent intent) { LogUtils.d(TAG, "handleExternalCommand: 开始处理外部指令"); @@ -400,7 +428,7 @@ public class ControlCenterService extends Service { return; } - // 处理线程重启指令(仅同步业务配置,服务控制配置从本地读取) + // 1. 处理线程重启指令(仅同步业务配置,服务控制配置从本地读取) if (ACTION_RESTART_REMIND_THREAD.equals(action)) { // 读取本地最新控制配置,确保状态同步 loadLatestServiceControlConfig(); @@ -420,6 +448,16 @@ public class ControlCenterService extends Service { } } } + // 2. 新增:处理应用前台/后台状态同步指令(MainActivity触发) + else if (ACTION_SYNC_APP_FOREGROUND_STATE.equals(action)) { + boolean isForeground = intent.getBooleanExtra(EXTRA_APP_FOREGROUND_STATE, false); + synchronized (mServiceLock) { + mIsAppForeground = isForeground; + LogUtils.d(TAG, "handleExternalCommand: 收到前台状态同步指令,更新状态=" + isForeground); + // 同步状态到线程(实时生效) + syncAppForegroundStateToThread(); + } + } LogUtils.d(TAG, "handleExternalCommand: 外部指令处理完成"); } @@ -476,7 +514,7 @@ public class ControlCenterService extends Service { LogUtils.d(TAG, "clearAllReferences: 所有引用已置空"); } - // ================================== 静态工具方法(服务启动/停止/状态判断/电池优化,简化控制Bean传递)================================= + // ================================== 静态工具方法(新增前台状态同步入口,完善保活优化)================================= /** * 启动服务(静态入口,从本地读取控制配置,显式绑定包名,适配API30+) */ @@ -572,7 +610,41 @@ public class ControlCenterService extends Service { } /** - * 判断服务是否运行(适配API30+ ActivityManager限制) + * 新增:同步应用前台/后台状态到服务(MainActivity专用入口) + * @param context 上下文 + * @param isForeground true=前台,false=后台 + */ + public static void syncAppForegroundState(Context context, boolean isForeground) { + LogUtils.d(TAG, "syncAppForegroundState: 发送前台状态同步指令,状态=" + isForeground); + if (context == null) { + LogUtils.e(TAG, "syncAppForegroundState: Context为空,同步失败"); + return; + } + if (!isServiceRunning(context, ControlCenterService.class)) { + LogUtils.w(TAG, "syncAppForegroundState: 服务未运行,跳过同步"); + return; + } + + Intent intent = new Intent(context, ControlCenterService.class); + intent.setAction(ACTION_SYNC_APP_FOREGROUND_STATE); + intent.putExtra(EXTRA_APP_FOREGROUND_STATE, isForeground); + 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); + } + LogUtils.d(TAG, "syncAppForegroundState: 前台状态同步指令发送成功"); + } catch (Exception e) { + LogUtils.e(TAG, "syncAppForegroundState: 发送同步指令异常", e); + } + } + + /** + * 判断服务是否运行(适配API30+ ActivityManager限制,优化判断逻辑) */ private static boolean isServiceRunning(Context context, Class serviceClass) { if (context == null || serviceClass == null) { @@ -586,21 +658,24 @@ public class ControlCenterService extends Service { return false; } - // API30+ 兼容:getRunningServices失效,通过进程状态判断 + // API30+ 兼容:getRunningServices失效,通过进程状态判断(优化兜底逻辑) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { String packageName = context.getPackageName(); List processes = am.getRunningAppProcesses(); if (processes != null && !processes.isEmpty()) { for (ActivityManager.RunningAppProcessInfo process : processes) { - if (packageName.equals(process.processName) - && process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { - return true; + if (packageName.equals(process.processName)) { + // 前台服务/前台进程均视为服务运行(适配部分机型保活机制) + if (process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE + || process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + return true; + } } } } - // 兜底:进程存活则视为服务运行(适配部分机型) - return true; + // 兜底:检查服务是否已启动(避免进程后台时误判) + return isServiceStarted(context, serviceClass); } catch (Exception e) { LogUtils.e(TAG, "isServiceRunning: API30+ 判断服务状态异常", e); return false; @@ -621,7 +696,21 @@ public class ControlCenterService extends Service { } /** - * 引导用户开启忽略电池优化(避免服务被后台杀死,适配API23+) + * 新增:辅助判断服务是否已启动(API30+ 兜底,避免进程后台误判) + */ + private static boolean isServiceStarted(Context context, Class serviceClass) { + try { + // 通过ServiceControlBean状态兜底(服务启动时会初始化配置) + ControlCenterServiceBean controlBean = ControlCenterServiceBean.loadBean(context, ControlCenterServiceBean.class); + return controlBean != null && controlBean.isEnableService(); + } catch (Exception e) { + LogUtils.e(TAG, "isServiceStarted: 兜底判断服务状态异常", e); + return false; + } + } + + /** + * 引导用户开启忽略电池优化(避免服务被后台杀死,适配API23+,优化机型兼容) */ public static void checkIgnoreBatteryOptimization(Context context) { LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 检查电池优化状态"); @@ -643,11 +732,14 @@ public class ControlCenterService extends Service { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); 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: 已引导用户开启忽略电池优化"); } else { LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已开启忽略电池优化,无需操作"); } + } catch (SecurityException e) { + LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 机型限制,无法引导电池优化(系统权限管控)", e); } catch (Exception e) { LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 引导优化异常(部分机型限制)", e); } @@ -684,7 +776,7 @@ public class ControlCenterService extends Service { } } - // ================================== Getter/Setter 方法(按需提供,线程安全)================================= + // ================================== Getter/Setter 方法(新增前台状态相关,线程安全)================================= public RemindThread getRemindThread() { synchronized (mServiceLock) { return mRemindThread; @@ -744,5 +836,21 @@ public class ControlCenterService extends Service { return mIsDestroyed; } } + + // 新增:获取应用前台状态(外部查询,线程安全) + public boolean isAppForeground() { + synchronized (mServiceLock) { + return mIsAppForeground; + } + } + + // 新增:设置应用前台状态(内部/外部调用,同步到线程) + public void setAppForeground(boolean isForeground) { + LogUtils.d(TAG, "setAppForeground: 更新应用前台状态=" + isForeground); + synchronized (mServiceLock) { + mIsAppForeground = isForeground; + syncAppForegroundStateToThread(); + } + } } 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 73b8c25..2357c61 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 @@ -20,6 +20,8 @@ public class RemindThread extends Thread { private static final int DEFAULT_SLEEP_TIME = 1000; // 默认检测间隔(ms) private static final long REMIND_INTERVAL = 3000L; // 重复提醒间隔(ms,防骚扰) private static final int CONFIG_RETRY_MAX = 3; // 配置同步重试次数(兜底) + private static final int INVALID_BATTERY_VALUE = -1; // 无效电量标记(新增) + private static final long BACKGROUND_SLEEP_TIME = 5000L;// 后台休眠间隔(ms,减少资源占用,新增) // ================================== 单例核心(线程安全,避免复用旧实例)================================= private static volatile RemindThread sInstance; @@ -34,14 +36,15 @@ public class RemindThread extends Thread { private volatile boolean isThreadStarted; // 启动标记(区分isAlive()) private volatile boolean isReminding; // 全局提醒开关 private volatile boolean isRemindTimerRunning; // 提醒恢复计时器状态 + private volatile boolean isAppForeground; // 应用前台状态(新增:控制提醒开关) // 业务配置(实时同步,范围校验) private volatile boolean isEnableChargeReminder; // 充电提醒开关 private volatile boolean isEnableUsageReminder; // 耗电提醒开关 - private volatile int sleepTime; // 检测间隔(ms) + private volatile long sleepTime; // 检测间隔(ms,改为long适配大间隔) private volatile int chargeReminderValue; // 充电提醒阈值(0-100) private volatile int usageReminderValue; // 耗电提醒阈值(0-100) - private volatile int quantityOfElectricity; // 当前电量(0-100) + private volatile int quantityOfElectricity; // 当前电量(0-100,无效为-1) private volatile boolean isCharging; // 充电状态标记 // 辅助变量(并发安全+防重复提醒) @@ -102,7 +105,7 @@ public class RemindThread extends Thread { } } - // ================================== 线程核心逻辑(run方法,高效退出+异常容错)================================= + // ================================== 线程核心逻辑(run方法,高效退出+异常容错+前台判断)================================= @Override public void run() { LogUtils.d(TAG, "线程启动,ID:" + Thread.currentThread().getId() + ",状态:" + getState()); @@ -114,6 +117,7 @@ public class RemindThread extends Thread { } isThreadStarted = true; lastRemindTime = 0; + isAppForeground = true; // 初始默认前台(窗口打开状态) } // 配置同步重试(失败则兜底默认值) @@ -138,26 +142,39 @@ public class RemindThread extends Thread { return; } - // 核心检测循环(快速退出+并发安全) + // 核心检测循环(快速退出+并发安全+前台判断) LogUtils.d(TAG, "进入电量检测循环,间隔:" + sleepTime + "ms"); while (!isExist) { try { if (isExist) break; - // 提醒关闭/电量无效,直接休眠 - if (!isReminding) { - safeSleep(sleepTime); - continue; - } - if (quantityOfElectricity < 0 || quantityOfElectricity > 100) { - LogUtils.w(TAG, "电量无效(" + quantityOfElectricity + "),休眠重试"); - safeSleep(sleepTime); - continue; + // 核心防护:应用后台/提醒关闭/电量无效,直接休眠(跳过提醒检测) + synchronized (mLock) { + // 应用后台:延长休眠间隔,减少资源占用+停止提醒 + if (!isAppForeground) { + safeSleep(BACKGROUND_SLEEP_TIME); + continue; + } + // 提醒关闭/电量无效:正常休眠,不检测 + if (!isReminding || quantityOfElectricity == INVALID_BATTERY_VALUE) { + safeSleep(sleepTime); + continue; + } + // 电量超出范围:修正为无效,休眠重试 + if (quantityOfElectricity < 0 || quantityOfElectricity > 100) { + LogUtils.w(TAG, "电量无效(" + quantityOfElectricity + "),标记为无效值"); + quantityOfElectricity = INVALID_BATTERY_VALUE; + safeSleep(sleepTime); + continue; + } } - // 锁保护核心提醒逻辑 + // 锁保护核心提醒逻辑(仅前台+提醒开启+电量有效时执行) synchronized (mLock) { - if (isExist) break; + if (isExist || !isAppForeground || !isReminding || quantityOfElectricity == INVALID_BATTERY_VALUE) { + safeSleep(sleepTime); + continue; + } // 防重复提醒(间隔未到/计时器运行中) long currentTime = System.currentTimeMillis(); @@ -166,14 +183,14 @@ public class RemindThread extends Thread { continue; } - // 充电提醒触发(新增:携带当前电量+充电状态发送) + // 充电提醒触发(携带当前电量+充电状态发送) if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) { LogUtils.d(TAG, "触发充电提醒:充电中,电量" + quantityOfElectricity + "≥阈值" + chargeReminderValue); sendNotificationMessage("+", quantityOfElectricity, isCharging); lastRemindTime = currentTime; startRemindRecoveryTimer(); } - // 耗电提醒触发(新增:携带当前电量+充电状态发送) + // 耗电提醒触发(携带当前电量+充电状态发送) else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { LogUtils.d(TAG, "触发耗电提醒:未充电,电量" + quantityOfElectricity + "≤阈值" + usageReminderValue); sendNotificationMessage("-", quantityOfElectricity, isCharging); @@ -218,25 +235,27 @@ public class RemindThread extends Thread { return false; } } - // 兜底默认阈值 + // 兜底默认阈值(避免无效值触发提醒) synchronized (mLock) { chargeReminderValue = (chargeReminderValue < 0 || chargeReminderValue > 100) ? 80 : chargeReminderValue; usageReminderValue = (usageReminderValue < 0 || usageReminderValue > 100) ? 20 : usageReminderValue; - LogUtils.e(TAG, "配置重试失败,兜底阈值:充电" + chargeReminderValue + ",耗电" + usageReminderValue); + quantityOfElectricity = INVALID_BATTERY_VALUE; // 兜底电量设为无效,防止误触发 + LogUtils.e(TAG, "配置重试失败,兜底阈值:充电" + chargeReminderValue + ",耗电" + usageReminderValue + "RuntimeException : Config sync failed."); } return false; } /** - * 发送提醒消息(新增:携带当前电量+充电状态,弱引用Handler+Message复用,防泄漏) + * 发送提醒消息(携带当前电量+充电状态,弱引用Handler+Message复用,防泄漏) * @param type 提醒类型(+:充电提醒,-:耗电提醒) * @param battery 当前电量(0-100) * @param isCharging 当前充电状态(true=充电中) */ private void sendNotificationMessage(String type, int battery, boolean isCharging) { LogUtils.d(TAG, "准备发送提醒消息:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging); - if (isExist || !isReminding) { - LogUtils.d(TAG, "线程退出/提醒关闭,跳过发送"); + // 双重防护:线程退出/应用后台/提醒关闭,跳过发送 + if (isExist || !isAppForeground || !isReminding) { + LogUtils.d(TAG, "线程退出/应用后台/提醒关闭,跳过发送"); return; } @@ -250,7 +269,7 @@ public class RemindThread extends Thread { 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) + message.arg2 = isCharging ? 1 : 0; // boolean转int(兼容Java7) try { handler.sendMessage(message); LogUtils.d(TAG, "提醒消息发送成功:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging); @@ -266,7 +285,7 @@ public class RemindThread extends Thread { private void startRemindRecoveryTimer() { LogUtils.d(TAG, "启动提醒恢复计时器,间隔:" + REMIND_INTERVAL + "ms"); synchronized (mLock) { - if (isExist || isRemindTimerRunning || sInstance != this) { + if (isExist || isRemindTimerRunning || sInstance != this || !isAppForeground) { LogUtils.w(TAG, "计时器启动条件不满足,跳过"); return; } @@ -287,8 +306,9 @@ public class RemindThread extends Thread { } synchronized (thread.mLock) { - if (!thread.isExist && sInstance == thread - && (thread.isEnableChargeReminder || thread.isEnableUsageReminder)) { + // 仅前台+有启用提醒时,才恢复提醒 + if (!thread.isExist && sInstance == thread && thread.isAppForeground + && (thread.isEnableChargeReminder || thread.isEnableUsageReminder)) { thread.isReminding = true; LogUtils.d(TAG, "提醒功能恢复开启"); } @@ -319,6 +339,7 @@ public class RemindThread extends Thread { isExist = true; isReminding = false; isRemindTimerRunning = false; + isAppForeground = false; // 标记后台,强制停止提醒 } if (isAlive()) { interrupt(); @@ -358,7 +379,9 @@ public class RemindThread extends Thread { isExist = true; isThreadStarted = false; isRemindTimerRunning = false; + isAppForeground = false; lastRemindTime = 0; + quantityOfElectricity = INVALID_BATTERY_VALUE; // 重置为无效电量,防残留 } if (isAlive()) interrupt(); if (sInstance == this) sInstance = null; @@ -376,6 +399,7 @@ public class RemindThread extends Thread { isReminding = false; isThreadStarted = false; isRemindTimerRunning = false; + isAppForeground = false; lastRemindTime = 0; // 业务配置 isEnableChargeReminder = false; @@ -383,7 +407,7 @@ public class RemindThread extends Thread { sleepTime = DEFAULT_SLEEP_TIME; chargeReminderValue = -1; usageReminderValue = -1; - quantityOfElectricity = -1; + quantityOfElectricity = INVALID_BATTERY_VALUE; // 初始设为无效 isCharging = false; } } @@ -397,39 +421,44 @@ public class RemindThread extends Thread { isExist = false; isRemindTimerRunning = false; lastRemindTime = 0; - isReminding = (isEnableChargeReminder || isEnableUsageReminder); + isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground; // 仅前台时开启提醒 } LogUtils.d(TAG, "运行状态重置完成,全局提醒:" + isReminding); } - // ================================== 配置同步方法(完整同步配置,原子操作)================================= + // ================================== 配置同步方法(完整同步配置,原子操作+无效电量防护)================================= /** * 同步应用配置(参数校验+范围限制,立即生效) */ public void setAppConfigBean(AppConfigBean config) { if (config == null) { LogUtils.e(TAG, "配置Bean为空,同步失败"); + synchronized (mLock) { + quantityOfElectricity = INVALID_BATTERY_VALUE; // 配置为空,标记电量无效 + } return; } - LogUtils.d(TAG, "开始同步配置:充电阈值" + config.getChargeReminderValue() + ",耗电阈值" + config.getUsageReminderValue()); + LogUtils.d(TAG, "开始同步配置:充电阈值" + config.getChargeReminderValue() + ",耗电阈值" + config.getUsageReminderValue() + ",当前电量" + config.getCurrentBatteryValue()); synchronized (mLock) { // 同步开关 isEnableChargeReminder = config.isEnableChargeReminder(); isEnableUsageReminder = config.isEnableUsageReminder(); - isReminding = (isEnableChargeReminder || isEnableUsageReminder); // 同步阈值(0-100限制) chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), 0), 100); usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), 0), 100); // 同步检测间隔(≥最小间隔) sleepTime = Math.max(config.getBatteryDetectInterval(), MIN_SLEEP_TIME); - // 同步电池状态 - quantityOfElectricity = Math.min(Math.max(config.getCurrentBatteryValue(), 0), 100); + // 同步电池状态(校验有效,无效则标记为-1) + int currentBattery = config.getCurrentBatteryValue(); + quantityOfElectricity = (currentBattery >= 0 && currentBattery <= 100) ? currentBattery : INVALID_BATTERY_VALUE; isCharging = config.isCharging(); + // 提醒开关:前台+有启用提醒才开启 + isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground; } - LogUtils.d(TAG, "配置同步完成:检测间隔" + sleepTime + "ms,全局提醒" + isReminding); + LogUtils.d(TAG, "配置同步完成:检测间隔" + sleepTime + "ms,全局提醒" + isReminding + ",当前电量" + quantityOfElectricity); } - // ================================== Setter/Getter(锁保护,线程安全+精准日志)================================= + // ================================== Setter/Getter(锁保护,线程安全+精准日志,新增前台状态控制)================================= public boolean isExist() { synchronized (mLock) { return isExist; @@ -451,8 +480,32 @@ public class RemindThread extends Thread { public void setIsReminding(boolean isReminding) { synchronized (mLock) { - this.isReminding = isReminding; - LogUtils.d(TAG, "设置全局提醒开关:" + isReminding); + // 仅前台时可开启提醒 + this.isReminding = isReminding && isAppForeground; + LogUtils.d(TAG, "设置全局提醒开关:" + this.isReminding + "(应用前台状态:" + isAppForeground + ")"); + } + } + + // 新增:应用前台/后台状态设置(主窗口关闭/打开时调用) + public void setAppForeground(boolean isForeground) { + synchronized (mLock) { + this.isAppForeground = isForeground; + // 后台时强制关闭提醒,前台时根据开关自动开启 + if (!isForeground) { + this.isReminding = false; + this.quantityOfElectricity = INVALID_BATTERY_VALUE; // 后台重置为无效电量 + LogUtils.d(TAG, "应用切换到后台,停止提醒+重置无效电量"); + } else { + this.isReminding = (isEnableChargeReminder || isEnableUsageReminder); + LogUtils.d(TAG, "应用切换到前台,提醒状态:" + this.isReminding); + } + } + } + + // 新增:获取应用前台状态 + public boolean isAppForeground() { + synchronized (mLock) { + return isAppForeground; } } @@ -471,7 +524,7 @@ public class RemindThread extends Thread { public void setIsEnableUsageReminder(boolean isEnableUsageReminder) { synchronized (mLock) { this.isEnableUsageReminder = isEnableUsageReminder; - isReminding = (isEnableChargeReminder || isEnableUsageReminder); + isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground; LogUtils.d(TAG, "设置耗电提醒开关:" + isEnableUsageReminder + ",全局提醒:" + isReminding); } } @@ -485,18 +538,18 @@ public class RemindThread extends Thread { public void setIsEnableChargeReminder(boolean isEnableChargeReminder) { synchronized (mLock) { this.isEnableChargeReminder = isEnableChargeReminder; - isReminding = (isEnableChargeReminder || isEnableUsageReminder); + isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground; LogUtils.d(TAG, "设置充电提醒开关:" + isEnableChargeReminder + ",全局提醒:" + isReminding); } } - public int getSleepTime() { + public long getSleepTime() { synchronized (mLock) { return sleepTime; } } - public void setSleepTime(int sleepTime) { + public void setSleepTime(long sleepTime) { synchronized (mLock) { this.sleepTime = Math.max(sleepTime, MIN_SLEEP_TIME); LogUtils.d(TAG, "设置检测间隔:" + this.sleepTime + "ms"); @@ -537,8 +590,14 @@ public class RemindThread extends Thread { public void setQuantityOfElectricity(int quantityOfElectricity) { synchronized (mLock) { - this.quantityOfElectricity = Math.min(Math.max(quantityOfElectricity, 0), 100); - LogUtils.d(TAG, "更新当前电量:" + this.quantityOfElectricity); + // 仅前台时更新电量,后台直接设为无效 + if (isAppForeground) { + this.quantityOfElectricity = Math.min(Math.max(quantityOfElectricity, 0), 100); + LogUtils.d(TAG, "更新当前电量:" + this.quantityOfElectricity); + } else { + this.quantityOfElectricity = INVALID_BATTERY_VALUE; + LogUtils.w(TAG, "应用后台,不更新电量,标记为无效"); + } } } @@ -559,7 +618,7 @@ public class RemindThread extends Thread { public boolean isRunning() { synchronized (mLock) { boolean running = !isExist && isThreadStarted && isAlive(); - LogUtils.d(TAG, "线程运行状态:" + running + "(退出:" + isExist + ",已启动:" + isThreadStarted + ",存活:" + isAlive() + ")"); + LogUtils.d(TAG, "线程运行状态:" + running + "(退出:" + isExist + ",已启动:" + isThreadStarted + ",存活:" + isAlive() + ",前台:" + isAppForeground + ")"); return running; } } @@ -585,6 +644,7 @@ public class RemindThread extends Thread { "threadId=" + getId() + ", threadName='" + getName() + '\'' + ", isRunning=" + isRunning() + + ", isForeground=" + isAppForeground() + ", isReminding=" + isReminding() + ", chargeThreshold=" + getChargeReminderValue() + ", usageThreshold=" + getUsageReminderValue() + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java index ec7291c..0093f98 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java @@ -15,6 +15,8 @@ import android.widget.Switch; import android.widget.TextView; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.R; +import cc.winboll.studio.powerbell.models.ControlCenterServiceBean; +import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.utils.AppConfigUtils; /** @@ -186,6 +188,7 @@ public class MainContentView { // 构建无模式对话框(点击外部可关闭,取消改动) AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle("配置变更确认"); + // 优化:区分服务开关/普通配置,显示不同提示语(提升用户体验) builder.setMessage("是否确认修改当前配置?"); // 确定按钮:保存配置+回调+更新视图 builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @@ -332,17 +335,19 @@ public class MainContentView { LogUtils.d(TAG, "bindViewListeners: 耗电提醒开关监听绑定完成"); } - // 服务总开关监听 + // 服务总开关监听(核心优化:从 AppConfig 改为读取服务控制Bean,确保状态一致) if (swEnableService != null) { swEnableService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - boolean originalValue = mAppConfigUtils.isServiceEnabled(); + // 关键调整:服务开关状态从服务控制Bean读取,而非AppConfig + boolean originalValue = getServiceEnableState(); boolean newValue = ((Switch) v).isChecked(); // 状态无变化,不处理 if (originalValue == newValue) return; - // 缓存变更数据,显示确认对话框 + // 缓存变更数据,显示确认对话框(优化:服务开关弹窗提示语差异化) mTempConfigData = new TempConfigData(CHANGE_TYPE_SERVICE_SWITCH, originalValue, newValue); + updateDialogMessageByChangeType(); showConfigConfirmDialog(); LogUtils.d(TAG, "swEnableService: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue); } @@ -371,8 +376,9 @@ public class MainContentView { int currentVal = mAppConfigUtils.getCurrentBatteryValue(); boolean chargeEnable = mAppConfigUtils.isChargeReminderEnabled(); boolean usageEnable = mAppConfigUtils.isUsageReminderEnabled(); - boolean serviceEnable = mAppConfigUtils.isServiceEnabled(); - LogUtils.d(TAG, "updateViewData: 配置数据读取完成,charge=" + chargeVal + ", usage=" + usageVal + ", current=" + currentVal); + // 关键调整:服务开关状态从服务控制Bean读取,确保与服务实际状态一致 + boolean serviceEnable = getServiceEnableState(); + LogUtils.d(TAG, "updateViewData: 配置数据读取完成,charge=" + chargeVal + ", usage=" + usageVal + ", current=" + currentVal + ", serviceEnable=" + serviceEnable); // 进度条背景更新 if (frameDrawable != null) { @@ -418,7 +424,7 @@ public class MainContentView { if (cbEnableUsageReminder != null) cbEnableUsageReminder.setChecked(usageEnable); LogUtils.d(TAG, "updateViewData: 耗电提醒视图更新完成"); - // 服务开关+提示文本更新 + // 服务开关+提示文本更新(核心调整:使用服务控制Bean状态) if (swEnableService != null) { swEnableService.setChecked(serviceEnable); swEnableService.setText(mContext.getString(R.string.txt_aboveswitch)); @@ -499,6 +505,28 @@ public class MainContentView { LogUtils.d(TAG, "releaseResources: 所有资源释放完成"); } + /** + * 新增:设置服务开关启用状态(外部调用,同步 UI 与服务状态,适配 Activity 视图刷新) + * @param enabled 服务启用状态 + */ + public void setServiceSwitchChecked(boolean enabled) { + if (swEnableService != null) { + swEnableService.setChecked(enabled); + LogUtils.d(TAG, "setServiceSwitchChecked: 服务开关UI更新为=" + enabled); + } + } + + /** + * 新增:设置服务开关点击状态(外部调用,避免更新 UI 时触发重复回调) + * @param enabled 是否允许点击 + */ + public void setServiceSwitchEnabled(boolean enabled) { + if (swEnableService != null) { + swEnableService.setEnabled(enabled); + LogUtils.d(TAG, "setServiceSwitchEnabled: 服务开关点击状态更新为=" + enabled); + } + } + // ======================== 内部核心逻辑方法(对话框相关,封装确认/取消逻辑)======================== /** * 显示配置变更确认对话框(确保 Activity 处于前台,避免异常,防止重复弹窗) @@ -560,11 +588,16 @@ public class MainContentView { mActionListener.onUsageReminderSwitchChanged(mTempConfigData.newBooleanValue); LogUtils.d(TAG, "confirmConfigChange: 耗电提醒开关确认,值=" + mTempConfigData.newBooleanValue); break; - // 服务总开关(适配 AppConfigUtils 无 Activity 参数调用) + // 服务总开关(核心优化:同步更新服务控制Bean,而非AppConfig) case CHANGE_TYPE_SERVICE_SWITCH: - mAppConfigUtils.setServiceEnabled(mTempConfigData.newBooleanValue); + // 1. 保存服务控制配置到本地(持久化自启动状态) + ControlCenterServiceBean serviceBean = ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class); + if (serviceBean == null) serviceBean = new ControlCenterServiceBean(false); + serviceBean.setIsEnableService(mTempConfigData.newBooleanValue); + ControlCenterService.updateServiceControlConfig(mContext, serviceBean); + // 2. 回调 Activity,触发服务启停 mActionListener.onServiceSwitchChanged(mTempConfigData.newBooleanValue); - LogUtils.d(TAG, "confirmConfigChange: 服务开关确认,值=" + mTempConfigData.newBooleanValue); + LogUtils.d(TAG, "confirmConfigChange: 服务开关确认,值=" + mTempConfigData.newBooleanValue + ",已持久化配置"); break; // 充电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用) case CHANGE_TYPE_CHARGE_SEEKBAR: @@ -611,7 +644,7 @@ public class MainContentView { } LogUtils.d(TAG, "cancelConfigChange: 耗电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue); break; - // 服务总开关:恢复原始状态 + // 服务总开关:恢复原始状态(核心优化:从服务控制Bean读取原始值) case CHANGE_TYPE_SERVICE_SWITCH: if (swEnableService != null) { swEnableService.setChecked(mTempConfigData.originalBooleanValue); @@ -671,6 +704,30 @@ public class MainContentView { } } + /** + * 新增:获取服务启用状态(统一从服务控制Bean读取,确保全链路状态一致) + * @return 服务启用状态(true=启用,false=禁用) + */ + private boolean getServiceEnableState() { + ControlCenterServiceBean serviceBean = ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class); + // 本地无配置时,默认禁用服务(与服务初始化逻辑对齐) + return serviceBean != null && serviceBean.isEnableService(); + } + + /** + * 新增:根据变更类型更新对话框提示语(差异化提示,提升用户体验) + */ + private void updateDialogMessageByChangeType() { + if (mConfigConfirmDialog == null || mTempConfigData == null) return; + String message; + if (mTempConfigData.changeType == CHANGE_TYPE_SERVICE_SWITCH) { + message = mTempConfigData.newBooleanValue ? "启用服务后,将后台持续监控电池状态,是否确认?" : "禁用服务后,电池监控功能将停止,是否确认?"; + } else { + message = "是否确认修改当前配置?"; + } + mConfigConfirmDialog.setMessage(message); + } + // ======================== 事件回调接口(解耦视图与业务,提升扩展性)======================== public interface OnViewActionListener { void onChargeReminderSwitchChanged(boolean isChecked);