服务自启动修复中。。。

This commit is contained in:
2025-12-17 16:50:38 +08:00
parent 5cf47172f4
commit d61d1da5d1
5 changed files with 441 additions and 100 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #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 stageCount=10
libraryProject= libraryProject=
baseVersion=15.14 baseVersion=15.14
publishVersion=15.14.9 publishVersion=15.14.9
buildCount=16 buildCount=18
baseBetaVersion=15.14.10 baseBetaVersion=15.14.10

View File

@@ -30,16 +30,15 @@ import cc.winboll.studio.powerbell.activities.SettingsActivity;
import cc.winboll.studio.powerbell.activities.WinBoLLActivity; import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
import cc.winboll.studio.powerbell.models.AppConfigBean; import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.BackgroundBean; 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.services.ControlCenterService;
import cc.winboll.studio.powerbell.threads.RemindThread;
import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity; import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity;
import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
import cc.winboll.studio.powerbell.utils.PermissionUtils; import cc.winboll.studio.powerbell.utils.PermissionUtils;
import cc.winboll.studio.powerbell.utils.ServiceUtils; 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.MainContentView;
import cc.winboll.studio.powerbell.views.VerticalSeekBar;
import java.io.Serializable;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
@@ -59,6 +58,7 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
public static final int MSG_CURRENTVALUEBATTERY = 1; // 更新当前电量 public static final int MSG_CURRENTVALUEBATTERY = 1; // 更新当前电量
public static final int MSG_LOAD_BACKGROUND = 2; // 加载背景 public static final int MSG_LOAD_BACKGROUND = 2; // 加载背景
private static final int MSG_RESTART_REMIND_THREAD = 3; // 重启RemindThread线程 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实例销毁时必须置空 public static MainActivity sMainActivity; // 全局Activity实例销毁时必须置空
@@ -79,6 +79,8 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
// 资源与菜单 // 资源与菜单
private Menu mMenu; private Menu mMenu;
private Drawable mFrameDrawable; // 框架背景资源适配API30主题 private Drawable mFrameDrawable; // 框架背景资源适配API30主题
// 新增服务控制核心Bean本地持久化管理服务自启动状态
private ControlCenterServiceBean mServiceControlBean;
// ======================== 生命周期方法(按系统调用顺序排列,逻辑闭环)======================== // ======================== 生命周期方法(按系统调用顺序排列,逻辑闭环)========================
@Override @Override
@@ -124,15 +126,28 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
if (sGlobalHandler != null) { if (sGlobalHandler != null) {
sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
LogUtils.d(TAG, "onResume: 发送加载背景消息"); LogUtils.d(TAG, "onResume: 发送加载背景消息");
// 新增恢复时同步服务开关UI避免开关状态与实际服务状态不一致
sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH);
} }
// 2. 恢复广告(非核心,容错处理) // 2. 恢复广告(非核心,容错处理)
if (mADsBannerView != null) { if (mADsBannerView != null) {
mADsBannerView.resumeADs(this); mADsBannerView.resumeADs(this);
LogUtils.d(TAG, "onResume: 恢复广告展示"); LogUtils.d(TAG, "onResume: 恢复广告展示");
} }
// 3. 关键调整应用切前台设置RemindThread为前台状态开启提醒
setRemindThreadForeground(true);
LogUtils.d(TAG, "onResume: 退出"); LogUtils.d(TAG, "onResume: 退出");
} }
@Override
protected void onPause() {
LogUtils.d(TAG, "onPause: 进入");
super.onPause();
// 关键调整应用切后台设置RemindThread为后台状态停止提醒+重置无效电量)
setRemindThreadForeground(false);
LogUtils.d(TAG, "onPause: 退出");
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
LogUtils.d(TAG, "onDestroy: 进入"); LogUtils.d(TAG, "onDestroy: 进入");
@@ -149,24 +164,28 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
mMainContentView = null; mMainContentView = null;
LogUtils.d(TAG, "onDestroy: 释放核心视图资源完成"); LogUtils.d(TAG, "onDestroy: 释放核心视图资源完成");
} }
// 3. 置空全局实例(杜绝内存泄漏,必须执行 // 3. 关键调整Activity销毁强制销毁RemindThread单例彻底停止线程
RemindThread.destroyInstance();
LogUtils.d(TAG, "onDestroy: 强制销毁RemindThread单例完成");
// 4. 置空全局实例(杜绝内存泄漏,必须执行)
sMainActivity = null; sMainActivity = null;
// 4. 清理Handler移除所有消息避免Activity销毁后回调 // 5. 清理Handler移除所有消息避免Activity销毁后回调
if (sGlobalHandler != null) { if (sGlobalHandler != null) {
sGlobalHandler.removeCallbacksAndMessages(null); sGlobalHandler.removeCallbacksAndMessages(null);
sGlobalHandler = null; sGlobalHandler = null;
LogUtils.d(TAG, "onDestroy: 清理全局Handler完成"); LogUtils.d(TAG, "onDestroy: 清理全局Handler完成");
} }
// 5. 释放Drawable资源断开回调辅助GC回收 // 6. 释放Drawable资源断开回调辅助GC回收
if (mFrameDrawable != null) { if (mFrameDrawable != null) {
mFrameDrawable.setCallback(null); mFrameDrawable.setCallback(null);
mFrameDrawable = null; mFrameDrawable = null;
LogUtils.d(TAG, "onDestroy: 释放Drawable资源完成"); LogUtils.d(TAG, "onDestroy: 释放Drawable资源完成");
} }
// 6. 置空所有辅助实例辅助GC避免残留引用 // 7. 置空所有辅助实例辅助GC避免残留引用
mPermissionUtils = null; mPermissionUtils = null;
mAppConfigUtils = null; mAppConfigUtils = null;
mBgSourceUtils = null; mBgSourceUtils = null;
mServiceControlBean = null; // 新增置空服务控制Bean
mMenu = null; mMenu = null;
mApplication = null; mApplication = null;
mToolbar = null; mToolbar = null;
@@ -314,6 +333,10 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
case MSG_RESTART_REMIND_THREAD: case MSG_RESTART_REMIND_THREAD:
AppConfigBean configBean = (AppConfigBean) msg.obj; AppConfigBean configBean = (AppConfigBean) msg.obj;
sMainActivity.restartRemindThread(configBean); sMainActivity.restartRemindThread(configBean);
break;
// 新增更新服务开关UI同步实际服务状态避免开关与服务状态不一致
case MSG_UPDATE_SERVICE_SWITCH:
sMainActivity.updateServiceSwitchUI();
break; break;
} }
super.handleMessage(msg); super.handleMessage(msg);
@@ -366,7 +389,15 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
mApplication = (App) getApplication(); mApplication = (App) getApplication();
mAppConfigUtils = AppConfigUtils.getInstance(getApplicationContext()); mAppConfigUtils = AppConfigUtils.getInstance(getApplicationContext());
mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity()); 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初始化完成");
// 切换主线程更新UIJava7显式runOnUiThread // 切换主线程更新UIJava7显式runOnUiThread
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@@ -387,6 +418,8 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
updateViewData(); // 更新视图数据 updateViewData(); // 更新视图数据
checkServiceAsync(); // 异步检查服务状态 checkServiceAsync(); // 异步检查服务状态
sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); // 加载背景 sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); // 加载背景
// 新增初始化服务开关UI确保开关状态与本地配置一致
sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH);
} }
}); });
LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程结束"); LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程结束");
@@ -483,6 +516,27 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
LogUtils.d(TAG, "setMainLayoutBackgroundColor: 退出"); 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前台服务规范 * 异步检查服务状态子线程执行避免主线程阻塞适配API30前台服务规范
@@ -493,23 +547,18 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
@Override @Override
public void run() { public void run() {
LogUtils.d(TAG, "checkServiceAsync: 异步线程启动"); LogUtils.d(TAG, "checkServiceAsync: 异步线程启动");
if (mAppConfigUtils == null || isFinishing()) { if (mAppConfigUtils == null || mServiceControlBean == null || isFinishing()) {
LogUtils.e(TAG, "checkServiceAsync: 配置工具类/Activity无效跳过检查"); LogUtils.e(TAG, "checkServiceAsync: 配置工具类/服务控制Bean/Activity无效跳过检查");
return; return;
} }
// 服务启用且未运行时启动前台服务API30必须前台服务小米保活优化 // 服务启用(本地配置)且未运行时启动前台服务API30必须前台服务小米保活优化
if (mAppConfigUtils.isServiceEnabled() && !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) { if (mServiceControlBean.isEnableService() && !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) {
LogUtils.d(TAG, "checkServiceAsync: 服务未运行,启动前台服务"); LogUtils.d(TAG, "checkServiceAsync: 服务未运行,启动前台服务");
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
if (!isFinishing()) { if (!isFinishing()) {
Intent serviceIntent = new Intent(getActivity(), ControlCenterService.class); ControlCenterService.startControlCenterService(getActivity());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getActivity().startForegroundService(serviceIntent);
} else {
getActivity().startService(serviceIntent);
}
} }
} }
}); });
@@ -525,24 +574,90 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
*/ */
private void restartRemindThread(AppConfigBean configBean) { private void restartRemindThread(AppConfigBean configBean) {
LogUtils.d(TAG, "restartRemindThread: 进入"); LogUtils.d(TAG, "restartRemindThread: 进入");
if (configBean == null || !ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) { if (configBean == null || mServiceControlBean == null) {
LogUtils.e(TAG, "restartRemindThread: 配置为空/服务未运行,跳过重启"); LogUtils.e(TAG, "restartRemindThread: 配置为空/服务控制Bean为空,跳过重启");
return; return;
} }
// 发送重启指令到服务通过Intent传递最新配置 // 服务未启用时,不重启线程(直接返回
Intent restartIntent = new Intent(this, ControlCenterService.class); if (!mServiceControlBean.isEnableService()) {
restartIntent.setAction(ControlCenterService.ACTION_RESTART_REMIND_THREAD); LogUtils.w(TAG, "restartRemindThread: 服务已禁用,跳过线程重启");
restartIntent.putExtra(ControlCenterService.EXTRA_APP_CONFIG_BEAN, (Serializable) configBean); return;
// 适配API26+前台服务启动规范
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(restartIntent);
} else {
startService(restartIntent);
} }
// 服务未运行时,先启动服务
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: 发送线程重启指令到服务完成");
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 @Override
public void onServiceSwitchChanged(boolean isChecked) { public void onServiceSwitchChanged(boolean isChecked) {
LogUtils.d(TAG, "onServiceSwitchChanged: 状态=" + isChecked); LogUtils.d(TAG, "onServiceSwitchChanged: 状态=" + isChecked);
sendRestartRemindThreadMessage(); // 核心修复:点击服务开关,触发「配置持久化+服务启停+UI更新」全链路逻辑
toggleServiceEnableState(isChecked);
} }
@Override @Override

View File

@@ -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 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"; 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传递 // 服务控制核心本地持久化管理替代Intent传递
@@ -42,6 +45,8 @@ public class ControlCenterService extends Service {
private AppConfigBean mCurrentConfigBean; // 当前应用配置 private AppConfigBean mCurrentConfigBean; // 当前应用配置
// 前台服务相关 // 前台服务相关
private NotificationMessage mForegroundNotifyMsg; // 前台保活通知模型 private NotificationMessage mForegroundNotifyMsg; // 前台保活通知模型
// 新增应用前台状态标记内存缓存同步MainActivity状态
private boolean mIsAppForeground = false;
// 状态标记(并发安全) // 状态标记(并发安全)
private boolean isServiceRunning = false; // 服务运行状态 private boolean isServiceRunning = false; // 服务运行状态
private boolean mIsDestroyed = false; // 服务销毁标记 private boolean mIsDestroyed = false; // 服务销毁标记
@@ -81,6 +86,8 @@ public class ControlCenterService extends Service {
handleExternalCommand(intent); handleExternalCommand(intent);
rebindForegroundNotify(); rebindForegroundNotify();
initServiceBusinessIfNeed(); // 按需初始化业务(避免重复初始化) initServiceBusinessIfNeed(); // 按需初始化业务(避免重复初始化)
// 新增:业务初始化完成后,同步应用前台状态到线程(防止状态丢失)
syncAppForegroundStateToThread();
LogUtils.d(TAG, "onStartCommand: 核心逻辑执行完成返回START_STICKY"); LogUtils.d(TAG, "onStartCommand: 核心逻辑执行完成返回START_STICKY");
return START_STICKY; return START_STICKY;
} else { } else {
@@ -100,6 +107,7 @@ public class ControlCenterService extends Service {
synchronized (mServiceLock) { synchronized (mServiceLock) {
isServiceRunning = false; isServiceRunning = false;
mIsDestroyed = true; mIsDestroyed = true;
mIsAppForeground = false; // 新增:重置前台状态
} }
// 顺序释放资源前台服务→线程→Handler→通知→配置→控制Bean // 顺序释放资源前台服务→线程→Handler→通知→配置→控制Bean
stopForegroundService(); stopForegroundService();
@@ -187,6 +195,7 @@ public class ControlCenterService extends Service {
releaseNotificationResource(); releaseNotificationResource();
mCurrentConfigBean = null; mCurrentConfigBean = null;
mForegroundNotifyMsg = null; mForegroundNotifyMsg = null;
mIsAppForeground = false; // 新增:重置前台状态
LogUtils.d(TAG, "stopAllBusinessSafely: 所有核心业务已停止"); LogUtils.d(TAG, "stopAllBusinessSafely: 所有核心业务已停止");
} }
} }
@@ -308,12 +317,14 @@ public class ControlCenterService extends Service {
mRemindThread = RemindThread.getInstance(this, mServiceHandler); mRemindThread = RemindThread.getInstance(this, mServiceHandler);
// 同步配置并开启提醒 // 同步配置并开启提醒
syncConfigToRemindThread(); syncConfigToRemindThread();
// 新增:同步应用前台状态(确保新线程启动时状态正确)
syncAppForegroundStateToThread();
// 双重校验,避免线程重复启动 // 双重校验,避免线程重复启动
if (mRemindThread != null && !mRemindThread.isAlive() && !mRemindThread.isThreadStarted()) { if (mRemindThread != null && !mRemindThread.isAlive() && !mRemindThread.isThreadStarted()) {
try { try {
mRemindThread.start(); mRemindThread.start();
LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功ID=" + mRemindThread.getId()); LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功ID=" + mRemindThread.getId() + ",前台状态=" + mIsAppForeground);
} catch (IllegalThreadStateException e) { } catch (IllegalThreadStateException e) {
LogUtils.e(TAG, "restartRemindThreadSafely: 线程重复启动异常", e); LogUtils.e(TAG, "restartRemindThreadSafely: 线程重复启动异常", e);
mRemindThread = null; 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) { private void handleExternalCommand(Intent intent) {
LogUtils.d(TAG, "handleExternalCommand: 开始处理外部指令"); LogUtils.d(TAG, "handleExternalCommand: 开始处理外部指令");
@@ -400,7 +428,7 @@ public class ControlCenterService extends Service {
return; return;
} }
// 处理线程重启指令(仅同步业务配置,服务控制配置从本地读取) // 1. 处理线程重启指令(仅同步业务配置,服务控制配置从本地读取)
if (ACTION_RESTART_REMIND_THREAD.equals(action)) { if (ACTION_RESTART_REMIND_THREAD.equals(action)) {
// 读取本地最新控制配置,确保状态同步 // 读取本地最新控制配置,确保状态同步
loadLatestServiceControlConfig(); 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: 外部指令处理完成"); LogUtils.d(TAG, "handleExternalCommand: 外部指令处理完成");
} }
@@ -476,7 +514,7 @@ public class ControlCenterService extends Service {
LogUtils.d(TAG, "clearAllReferences: 所有引用已置空"); LogUtils.d(TAG, "clearAllReferences: 所有引用已置空");
} }
// ================================== 静态工具方法(服务启动/停止/状态判断/电池优化简化控制Bean传递================================= // ================================== 静态工具方法(新增前台状态同步入口,完善保活优化=================================
/** /**
* 启动服务静态入口从本地读取控制配置显式绑定包名适配API30+ * 启动服务静态入口从本地读取控制配置显式绑定包名适配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) { private static boolean isServiceRunning(Context context, Class<?> serviceClass) {
if (context == null || serviceClass == null) { if (context == null || serviceClass == null) {
@@ -586,21 +658,24 @@ public class ControlCenterService extends Service {
return false; return false;
} }
// API30+ 兼容getRunningServices失效通过进程状态判断 // API30+ 兼容getRunningServices失效通过进程状态判断(优化兜底逻辑)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try { try {
String packageName = context.getPackageName(); String packageName = context.getPackageName();
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses(); List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
if (processes != null && !processes.isEmpty()) { if (processes != null && !processes.isEmpty()) {
for (ActivityManager.RunningAppProcessInfo process : processes) { for (ActivityManager.RunningAppProcessInfo process : processes) {
if (packageName.equals(process.processName) if (packageName.equals(process.processName)) {
&& process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { // 前台服务/前台进程均视为服务运行(适配部分机型保活机制)
return true; 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) { } catch (Exception e) {
LogUtils.e(TAG, "isServiceRunning: API30+ 判断服务状态异常", e); LogUtils.e(TAG, "isServiceRunning: API30+ 判断服务状态异常", e);
return false; 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) { public static void checkIgnoreBatteryOptimization(Context context) {
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 检查电池优化状态"); LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 检查电池优化状态");
@@ -643,11 +732,14 @@ public class ControlCenterService extends Service {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + context.getPackageName())); intent.setData(Uri.parse("package:" + context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 适配部分机型限制,添加异常捕获
context.startActivity(intent); context.startActivity(intent);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已引导用户开启忽略电池优化"); LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已引导用户开启忽略电池优化");
} else { } else {
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已开启忽略电池优化,无需操作"); LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 已开启忽略电池优化,无需操作");
} }
} catch (SecurityException e) {
LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 机型限制,无法引导电池优化(系统权限管控)", e);
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 引导优化异常(部分机型限制)", e); LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 引导优化异常(部分机型限制)", e);
} }
@@ -684,7 +776,7 @@ public class ControlCenterService extends Service {
} }
} }
// ================================== Getter/Setter 方法(按需提供,线程安全)================================= // ================================== Getter/Setter 方法(新增前台状态相关,线程安全)=================================
public RemindThread getRemindThread() { public RemindThread getRemindThread() {
synchronized (mServiceLock) { synchronized (mServiceLock) {
return mRemindThread; return mRemindThread;
@@ -744,5 +836,21 @@ public class ControlCenterService extends Service {
return mIsDestroyed; return mIsDestroyed;
} }
} }
// 新增:获取应用前台状态(外部查询,线程安全)
public boolean isAppForeground() {
synchronized (mServiceLock) {
return mIsAppForeground;
}
}
// 新增:设置应用前台状态(内部/外部调用,同步到线程)
public void setAppForeground(boolean isForeground) {
LogUtils.d(TAG, "setAppForeground: 更新应用前台状态=" + isForeground);
synchronized (mServiceLock) {
mIsAppForeground = isForeground;
syncAppForegroundStateToThread();
}
}
} }

View File

@@ -20,6 +20,8 @@ public class RemindThread extends Thread {
private static final int DEFAULT_SLEEP_TIME = 1000; // 默认检测间隔ms private static final int DEFAULT_SLEEP_TIME = 1000; // 默认检测间隔ms
private static final long REMIND_INTERVAL = 3000L; // 重复提醒间隔ms防骚扰 private static final long REMIND_INTERVAL = 3000L; // 重复提醒间隔ms防骚扰
private static final int CONFIG_RETRY_MAX = 3; // 配置同步重试次数(兜底) 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; private static volatile RemindThread sInstance;
@@ -34,14 +36,15 @@ public class RemindThread extends Thread {
private volatile boolean isThreadStarted; // 启动标记区分isAlive() private volatile boolean isThreadStarted; // 启动标记区分isAlive()
private volatile boolean isReminding; // 全局提醒开关 private volatile boolean isReminding; // 全局提醒开关
private volatile boolean isRemindTimerRunning; // 提醒恢复计时器状态 private volatile boolean isRemindTimerRunning; // 提醒恢复计时器状态
private volatile boolean isAppForeground; // 应用前台状态(新增:控制提醒开关)
// 业务配置(实时同步,范围校验) // 业务配置(实时同步,范围校验)
private volatile boolean isEnableChargeReminder; // 充电提醒开关 private volatile boolean isEnableChargeReminder; // 充电提醒开关
private volatile boolean isEnableUsageReminder; // 耗电提醒开关 private volatile boolean isEnableUsageReminder; // 耗电提醒开关
private volatile int sleepTime; // 检测间隔ms private volatile long sleepTime; // 检测间隔ms改为long适配大间隔
private volatile int chargeReminderValue; // 充电提醒阈值0-100 private volatile int chargeReminderValue; // 充电提醒阈值0-100
private volatile int usageReminderValue; // 耗电提醒阈值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; // 充电状态标记 private volatile boolean isCharging; // 充电状态标记
// 辅助变量(并发安全+防重复提醒) // 辅助变量(并发安全+防重复提醒)
@@ -102,7 +105,7 @@ public class RemindThread extends Thread {
} }
} }
// ================================== 线程核心逻辑run方法高效退出+异常容错)================================= // ================================== 线程核心逻辑run方法高效退出+异常容错+前台判断=================================
@Override @Override
public void run() { public void run() {
LogUtils.d(TAG, "线程启动ID" + Thread.currentThread().getId() + ",状态:" + getState()); LogUtils.d(TAG, "线程启动ID" + Thread.currentThread().getId() + ",状态:" + getState());
@@ -114,6 +117,7 @@ public class RemindThread extends Thread {
} }
isThreadStarted = true; isThreadStarted = true;
lastRemindTime = 0; lastRemindTime = 0;
isAppForeground = true; // 初始默认前台(窗口打开状态)
} }
// 配置同步重试(失败则兜底默认值) // 配置同步重试(失败则兜底默认值)
@@ -138,26 +142,39 @@ public class RemindThread extends Thread {
return; return;
} }
// 核心检测循环(快速退出+并发安全) // 核心检测循环(快速退出+并发安全+前台判断
LogUtils.d(TAG, "进入电量检测循环,间隔:" + sleepTime + "ms"); LogUtils.d(TAG, "进入电量检测循环,间隔:" + sleepTime + "ms");
while (!isExist) { while (!isExist) {
try { try {
if (isExist) break; if (isExist) break;
// 提醒关闭/电量无效,直接休眠 // 核心防护:应用后台/提醒关闭/电量无效,直接休眠(跳过提醒检测)
if (!isReminding) { synchronized (mLock) {
safeSleep(sleepTime); // 应用后台:延长休眠间隔,减少资源占用+停止提醒
continue; if (!isAppForeground) {
} safeSleep(BACKGROUND_SLEEP_TIME);
if (quantityOfElectricity < 0 || quantityOfElectricity > 100) { continue;
LogUtils.w(TAG, "电量无效(" + quantityOfElectricity + "),休眠重试"); }
safeSleep(sleepTime); // 提醒关闭/电量无效:正常休眠,不检测
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) { synchronized (mLock) {
if (isExist) break; if (isExist || !isAppForeground || !isReminding || quantityOfElectricity == INVALID_BATTERY_VALUE) {
safeSleep(sleepTime);
continue;
}
// 防重复提醒(间隔未到/计时器运行中) // 防重复提醒(间隔未到/计时器运行中)
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
@@ -166,14 +183,14 @@ public class RemindThread extends Thread {
continue; continue;
} }
// 充电提醒触发(新增:携带当前电量+充电状态发送) // 充电提醒触发(携带当前电量+充电状态发送)
if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) { if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) {
LogUtils.d(TAG, "触发充电提醒:充电中,电量" + quantityOfElectricity + "≥阈值" + chargeReminderValue); LogUtils.d(TAG, "触发充电提醒:充电中,电量" + quantityOfElectricity + "≥阈值" + chargeReminderValue);
sendNotificationMessage("+", quantityOfElectricity, isCharging); sendNotificationMessage("+", quantityOfElectricity, isCharging);
lastRemindTime = currentTime; lastRemindTime = currentTime;
startRemindRecoveryTimer(); startRemindRecoveryTimer();
} }
// 耗电提醒触发(新增:携带当前电量+充电状态发送) // 耗电提醒触发(携带当前电量+充电状态发送)
else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) {
LogUtils.d(TAG, "触发耗电提醒:未充电,电量" + quantityOfElectricity + "≤阈值" + usageReminderValue); LogUtils.d(TAG, "触发耗电提醒:未充电,电量" + quantityOfElectricity + "≤阈值" + usageReminderValue);
sendNotificationMessage("-", quantityOfElectricity, isCharging); sendNotificationMessage("-", quantityOfElectricity, isCharging);
@@ -218,25 +235,27 @@ public class RemindThread extends Thread {
return false; return false;
} }
} }
// 兜底默认阈值 // 兜底默认阈值(避免无效值触发提醒)
synchronized (mLock) { synchronized (mLock) {
chargeReminderValue = (chargeReminderValue < 0 || chargeReminderValue > 100) ? 80 : chargeReminderValue; chargeReminderValue = (chargeReminderValue < 0 || chargeReminderValue > 100) ? 80 : chargeReminderValue;
usageReminderValue = (usageReminderValue < 0 || usageReminderValue > 100) ? 20 : usageReminderValue; 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; return false;
} }
/** /**
* 发送提醒消息(新增:携带当前电量+充电状态弱引用Handler+Message复用防泄漏 * 发送提醒消息(携带当前电量+充电状态弱引用Handler+Message复用防泄漏
* @param type 提醒类型(+:充电提醒,-:耗电提醒) * @param type 提醒类型(+:充电提醒,-:耗电提醒)
* @param battery 当前电量0-100 * @param battery 当前电量0-100
* @param isCharging 当前充电状态true=充电中) * @param isCharging 当前充电状态true=充电中)
*/ */
private void sendNotificationMessage(String type, int battery, boolean isCharging) { private void sendNotificationMessage(String type, int battery, boolean isCharging) {
LogUtils.d(TAG, "准备发送提醒消息:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging); LogUtils.d(TAG, "准备发送提醒消息:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging);
if (isExist || !isReminding) { // 双重防护:线程退出/应用后台/提醒关闭,跳过发送
LogUtils.d(TAG, "线程退出/提醒关闭,跳过发送"); if (isExist || !isAppForeground || !isReminding) {
LogUtils.d(TAG, "线程退出/应用后台/提醒关闭,跳过发送");
return; return;
} }
@@ -250,7 +269,7 @@ public class RemindThread extends Thread {
Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT); Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT);
message.obj = type; message.obj = type;
message.arg1 = battery; message.arg1 = battery;
message.arg2 = isCharging ? 1 : 0; // boolean转intMessage无boolean参数兼容Java7 message.arg2 = isCharging ? 1 : 0; // boolean转int兼容Java7
try { try {
handler.sendMessage(message); handler.sendMessage(message);
LogUtils.d(TAG, "提醒消息发送成功:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging); LogUtils.d(TAG, "提醒消息发送成功:类型=" + type + ",电量=" + battery + ",充电状态=" + isCharging);
@@ -266,7 +285,7 @@ public class RemindThread extends Thread {
private void startRemindRecoveryTimer() { private void startRemindRecoveryTimer() {
LogUtils.d(TAG, "启动提醒恢复计时器,间隔:" + REMIND_INTERVAL + "ms"); LogUtils.d(TAG, "启动提醒恢复计时器,间隔:" + REMIND_INTERVAL + "ms");
synchronized (mLock) { synchronized (mLock) {
if (isExist || isRemindTimerRunning || sInstance != this) { if (isExist || isRemindTimerRunning || sInstance != this || !isAppForeground) {
LogUtils.w(TAG, "计时器启动条件不满足,跳过"); LogUtils.w(TAG, "计时器启动条件不满足,跳过");
return; return;
} }
@@ -287,8 +306,9 @@ public class RemindThread extends Thread {
} }
synchronized (thread.mLock) { 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; thread.isReminding = true;
LogUtils.d(TAG, "提醒功能恢复开启"); LogUtils.d(TAG, "提醒功能恢复开启");
} }
@@ -319,6 +339,7 @@ public class RemindThread extends Thread {
isExist = true; isExist = true;
isReminding = false; isReminding = false;
isRemindTimerRunning = false; isRemindTimerRunning = false;
isAppForeground = false; // 标记后台,强制停止提醒
} }
if (isAlive()) { if (isAlive()) {
interrupt(); interrupt();
@@ -358,7 +379,9 @@ public class RemindThread extends Thread {
isExist = true; isExist = true;
isThreadStarted = false; isThreadStarted = false;
isRemindTimerRunning = false; isRemindTimerRunning = false;
isAppForeground = false;
lastRemindTime = 0; lastRemindTime = 0;
quantityOfElectricity = INVALID_BATTERY_VALUE; // 重置为无效电量,防残留
} }
if (isAlive()) interrupt(); if (isAlive()) interrupt();
if (sInstance == this) sInstance = null; if (sInstance == this) sInstance = null;
@@ -376,6 +399,7 @@ public class RemindThread extends Thread {
isReminding = false; isReminding = false;
isThreadStarted = false; isThreadStarted = false;
isRemindTimerRunning = false; isRemindTimerRunning = false;
isAppForeground = false;
lastRemindTime = 0; lastRemindTime = 0;
// 业务配置 // 业务配置
isEnableChargeReminder = false; isEnableChargeReminder = false;
@@ -383,7 +407,7 @@ public class RemindThread extends Thread {
sleepTime = DEFAULT_SLEEP_TIME; sleepTime = DEFAULT_SLEEP_TIME;
chargeReminderValue = -1; chargeReminderValue = -1;
usageReminderValue = -1; usageReminderValue = -1;
quantityOfElectricity = -1; quantityOfElectricity = INVALID_BATTERY_VALUE; // 初始设为无效
isCharging = false; isCharging = false;
} }
} }
@@ -397,39 +421,44 @@ public class RemindThread extends Thread {
isExist = false; isExist = false;
isRemindTimerRunning = false; isRemindTimerRunning = false;
lastRemindTime = 0; lastRemindTime = 0;
isReminding = (isEnableChargeReminder || isEnableUsageReminder); isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground; // 仅前台时开启提醒
} }
LogUtils.d(TAG, "运行状态重置完成,全局提醒:" + isReminding); LogUtils.d(TAG, "运行状态重置完成,全局提醒:" + isReminding);
} }
// ================================== 配置同步方法(完整同步配置,原子操作)================================= // ================================== 配置同步方法(完整同步配置,原子操作+无效电量防护=================================
/** /**
* 同步应用配置(参数校验+范围限制,立即生效) * 同步应用配置(参数校验+范围限制,立即生效)
*/ */
public void setAppConfigBean(AppConfigBean config) { public void setAppConfigBean(AppConfigBean config) {
if (config == null) { if (config == null) {
LogUtils.e(TAG, "配置Bean为空同步失败"); LogUtils.e(TAG, "配置Bean为空同步失败");
synchronized (mLock) {
quantityOfElectricity = INVALID_BATTERY_VALUE; // 配置为空,标记电量无效
}
return; return;
} }
LogUtils.d(TAG, "开始同步配置:充电阈值" + config.getChargeReminderValue() + ",耗电阈值" + config.getUsageReminderValue()); LogUtils.d(TAG, "开始同步配置:充电阈值" + config.getChargeReminderValue() + ",耗电阈值" + config.getUsageReminderValue() + ",当前电量" + config.getCurrentBatteryValue());
synchronized (mLock) { synchronized (mLock) {
// 同步开关 // 同步开关
isEnableChargeReminder = config.isEnableChargeReminder(); isEnableChargeReminder = config.isEnableChargeReminder();
isEnableUsageReminder = config.isEnableUsageReminder(); isEnableUsageReminder = config.isEnableUsageReminder();
isReminding = (isEnableChargeReminder || isEnableUsageReminder);
// 同步阈值0-100限制 // 同步阈值0-100限制
chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), 0), 100); chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), 0), 100);
usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), 0), 100); usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), 0), 100);
// 同步检测间隔(≥最小间隔) // 同步检测间隔(≥最小间隔)
sleepTime = Math.max(config.getBatteryDetectInterval(), MIN_SLEEP_TIME); sleepTime = Math.max(config.getBatteryDetectInterval(), MIN_SLEEP_TIME);
// 同步电池状态 // 同步电池状态(校验有效,无效则标记为-1
quantityOfElectricity = Math.min(Math.max(config.getCurrentBatteryValue(), 0), 100); int currentBattery = config.getCurrentBatteryValue();
quantityOfElectricity = (currentBattery >= 0 && currentBattery <= 100) ? currentBattery : INVALID_BATTERY_VALUE;
isCharging = config.isCharging(); 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() { public boolean isExist() {
synchronized (mLock) { synchronized (mLock) {
return isExist; return isExist;
@@ -451,8 +480,32 @@ public class RemindThread extends Thread {
public void setIsReminding(boolean isReminding) { public void setIsReminding(boolean isReminding) {
synchronized (mLock) { 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) { public void setIsEnableUsageReminder(boolean isEnableUsageReminder) {
synchronized (mLock) { synchronized (mLock) {
this.isEnableUsageReminder = isEnableUsageReminder; this.isEnableUsageReminder = isEnableUsageReminder;
isReminding = (isEnableChargeReminder || isEnableUsageReminder); isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground;
LogUtils.d(TAG, "设置耗电提醒开关:" + isEnableUsageReminder + ",全局提醒:" + isReminding); LogUtils.d(TAG, "设置耗电提醒开关:" + isEnableUsageReminder + ",全局提醒:" + isReminding);
} }
} }
@@ -485,18 +538,18 @@ public class RemindThread extends Thread {
public void setIsEnableChargeReminder(boolean isEnableChargeReminder) { public void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
synchronized (mLock) { synchronized (mLock) {
this.isEnableChargeReminder = isEnableChargeReminder; this.isEnableChargeReminder = isEnableChargeReminder;
isReminding = (isEnableChargeReminder || isEnableUsageReminder); isReminding = (isEnableChargeReminder || isEnableUsageReminder) && isAppForeground;
LogUtils.d(TAG, "设置充电提醒开关:" + isEnableChargeReminder + ",全局提醒:" + isReminding); LogUtils.d(TAG, "设置充电提醒开关:" + isEnableChargeReminder + ",全局提醒:" + isReminding);
} }
} }
public int getSleepTime() { public long getSleepTime() {
synchronized (mLock) { synchronized (mLock) {
return sleepTime; return sleepTime;
} }
} }
public void setSleepTime(int sleepTime) { public void setSleepTime(long sleepTime) {
synchronized (mLock) { synchronized (mLock) {
this.sleepTime = Math.max(sleepTime, MIN_SLEEP_TIME); this.sleepTime = Math.max(sleepTime, MIN_SLEEP_TIME);
LogUtils.d(TAG, "设置检测间隔:" + this.sleepTime + "ms"); LogUtils.d(TAG, "设置检测间隔:" + this.sleepTime + "ms");
@@ -537,8 +590,14 @@ public class RemindThread extends Thread {
public void setQuantityOfElectricity(int quantityOfElectricity) { public void setQuantityOfElectricity(int quantityOfElectricity) {
synchronized (mLock) { 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() { public boolean isRunning() {
synchronized (mLock) { synchronized (mLock) {
boolean running = !isExist && isThreadStarted && isAlive(); boolean running = !isExist && isThreadStarted && isAlive();
LogUtils.d(TAG, "线程运行状态:" + running + "(退出:" + isExist + ",已启动:" + isThreadStarted + ",存活:" + isAlive() + ""); LogUtils.d(TAG, "线程运行状态:" + running + "(退出:" + isExist + ",已启动:" + isThreadStarted + ",存活:" + isAlive() + ",前台:" + isAppForeground + "");
return running; return running;
} }
} }
@@ -585,6 +644,7 @@ public class RemindThread extends Thread {
"threadId=" + getId() + "threadId=" + getId() +
", threadName='" + getName() + '\'' + ", threadName='" + getName() + '\'' +
", isRunning=" + isRunning() + ", isRunning=" + isRunning() +
", isForeground=" + isAppForeground() +
", isReminding=" + isReminding() + ", isReminding=" + isReminding() +
", chargeThreshold=" + getChargeReminderValue() + ", chargeThreshold=" + getChargeReminderValue() +
", usageThreshold=" + getUsageReminderValue() + ", usageThreshold=" + getUsageReminderValue() +

View File

@@ -15,6 +15,8 @@ import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.R; 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; import cc.winboll.studio.powerbell.utils.AppConfigUtils;
/** /**
@@ -186,6 +188,7 @@ public class MainContentView {
// 构建无模式对话框(点击外部可关闭,取消改动) // 构建无模式对话框(点击外部可关闭,取消改动)
AlertDialog.Builder builder = new AlertDialog.Builder(mContext); AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("配置变更确认"); builder.setTitle("配置变更确认");
// 优化:区分服务开关/普通配置,显示不同提示语(提升用户体验)
builder.setMessage("是否确认修改当前配置?"); builder.setMessage("是否确认修改当前配置?");
// 确定按钮:保存配置+回调+更新视图 // 确定按钮:保存配置+回调+更新视图
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@@ -332,17 +335,19 @@ public class MainContentView {
LogUtils.d(TAG, "bindViewListeners: 耗电提醒开关监听绑定完成"); LogUtils.d(TAG, "bindViewListeners: 耗电提醒开关监听绑定完成");
} }
// 服务总开关监听 // 服务总开关监听(核心优化:从 AppConfig 改为读取服务控制Bean确保状态一致
if (swEnableService != null) { if (swEnableService != null) {
swEnableService.setOnClickListener(new View.OnClickListener() { swEnableService.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
boolean originalValue = mAppConfigUtils.isServiceEnabled(); // 关键调整服务开关状态从服务控制Bean读取而非AppConfig
boolean originalValue = getServiceEnableState();
boolean newValue = ((Switch) v).isChecked(); boolean newValue = ((Switch) v).isChecked();
// 状态无变化,不处理 // 状态无变化,不处理
if (originalValue == newValue) return; if (originalValue == newValue) return;
// 缓存变更数据,显示确认对话框 // 缓存变更数据,显示确认对话框(优化:服务开关弹窗提示语差异化)
mTempConfigData = new TempConfigData(CHANGE_TYPE_SERVICE_SWITCH, originalValue, newValue); mTempConfigData = new TempConfigData(CHANGE_TYPE_SERVICE_SWITCH, originalValue, newValue);
updateDialogMessageByChangeType();
showConfigConfirmDialog(); showConfigConfirmDialog();
LogUtils.d(TAG, "swEnableService: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue); LogUtils.d(TAG, "swEnableService: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue);
} }
@@ -371,8 +376,9 @@ public class MainContentView {
int currentVal = mAppConfigUtils.getCurrentBatteryValue(); int currentVal = mAppConfigUtils.getCurrentBatteryValue();
boolean chargeEnable = mAppConfigUtils.isChargeReminderEnabled(); boolean chargeEnable = mAppConfigUtils.isChargeReminderEnabled();
boolean usageEnable = mAppConfigUtils.isUsageReminderEnabled(); boolean usageEnable = mAppConfigUtils.isUsageReminderEnabled();
boolean serviceEnable = mAppConfigUtils.isServiceEnabled(); // 关键调整服务开关状态从服务控制Bean读取确保与服务实际状态一致
LogUtils.d(TAG, "updateViewData: 配置数据读取完成charge=" + chargeVal + ", usage=" + usageVal + ", current=" + currentVal); boolean serviceEnable = getServiceEnableState();
LogUtils.d(TAG, "updateViewData: 配置数据读取完成charge=" + chargeVal + ", usage=" + usageVal + ", current=" + currentVal + ", serviceEnable=" + serviceEnable);
// 进度条背景更新 // 进度条背景更新
if (frameDrawable != null) { if (frameDrawable != null) {
@@ -418,7 +424,7 @@ public class MainContentView {
if (cbEnableUsageReminder != null) cbEnableUsageReminder.setChecked(usageEnable); if (cbEnableUsageReminder != null) cbEnableUsageReminder.setChecked(usageEnable);
LogUtils.d(TAG, "updateViewData: 耗电提醒视图更新完成"); LogUtils.d(TAG, "updateViewData: 耗电提醒视图更新完成");
// 服务开关+提示文本更新 // 服务开关+提示文本更新核心调整使用服务控制Bean状态
if (swEnableService != null) { if (swEnableService != null) {
swEnableService.setChecked(serviceEnable); swEnableService.setChecked(serviceEnable);
swEnableService.setText(mContext.getString(R.string.txt_aboveswitch)); swEnableService.setText(mContext.getString(R.string.txt_aboveswitch));
@@ -499,6 +505,28 @@ public class MainContentView {
LogUtils.d(TAG, "releaseResources: 所有资源释放完成"); 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 处于前台,避免异常,防止重复弹窗) * 显示配置变更确认对话框(确保 Activity 处于前台,避免异常,防止重复弹窗)
@@ -560,11 +588,16 @@ public class MainContentView {
mActionListener.onUsageReminderSwitchChanged(mTempConfigData.newBooleanValue); mActionListener.onUsageReminderSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 耗电提醒开关确认,值=" + mTempConfigData.newBooleanValue); LogUtils.d(TAG, "confirmConfigChange: 耗电提醒开关确认,值=" + mTempConfigData.newBooleanValue);
break; break;
// 服务总开关(适配 AppConfigUtils 无 Activity 参数调用 // 服务总开关(核心优化同步更新服务控制Bean而非AppConfig
case CHANGE_TYPE_SERVICE_SWITCH: 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); mActionListener.onServiceSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 服务开关确认,值=" + mTempConfigData.newBooleanValue); LogUtils.d(TAG, "confirmConfigChange: 服务开关确认,值=" + mTempConfigData.newBooleanValue + ",已持久化配置");
break; break;
// 充电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用) // 充电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用)
case CHANGE_TYPE_CHARGE_SEEKBAR: case CHANGE_TYPE_CHARGE_SEEKBAR:
@@ -611,7 +644,7 @@ public class MainContentView {
} }
LogUtils.d(TAG, "cancelConfigChange: 耗电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue); LogUtils.d(TAG, "cancelConfigChange: 耗电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break; break;
// 服务总开关:恢复原始状态 // 服务总开关:恢复原始状态核心优化从服务控制Bean读取原始值
case CHANGE_TYPE_SERVICE_SWITCH: case CHANGE_TYPE_SERVICE_SWITCH:
if (swEnableService != null) { if (swEnableService != null) {
swEnableService.setChecked(mTempConfigData.originalBooleanValue); 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 { public interface OnViewActionListener {
void onChargeReminderSwitchChanged(boolean isChecked); void onChargeReminderSwitchChanged(boolean isChecked);