服务启停调试中。。。

This commit is contained in:
2025-12-17 20:40:04 +08:00
parent d61d1da5d1
commit 5e6de91430
4 changed files with 271 additions and 370 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Dec 17 08:49:06 GMT 2025
#Wed Dec 17 12:37:54 GMT 2025
stageCount=10
libraryProject=
baseVersion=15.14
publishVersion=15.14.9
buildCount=18
buildCount=25
baseBetaVersion=15.14.10

View File

@@ -45,6 +45,7 @@ import cc.winboll.studio.powerbell.views.MainContentView;
* @Date 2025/12/17 13:14
* @Describe 主活动类:应用核心页面,管理电池监控、背景设置、服务控制等核心功能
* 适配Java7 | API30 | 小米手机,优化性能与稳定性,杜绝内存泄漏
* 核心规则UI开关仅管理本地配置服务启停仅在应用启动时根据配置执行
*/
public class MainActivity extends WinBoLLActivity implements MainContentView.OnViewActionListener {
// ======================== 静态常量(置顶统一,抽离魔法值,便于维护)========================
@@ -58,11 +59,11 @@ 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(新增)
private static final int MSG_UPDATE_SERVICE_SWITCH = 4; // 更新服务开关UI
// ======================== 静态成员(全局共享,严格管控生命周期)========================
public static MainActivity sMainActivity; // 全局Activity实例销毁时必须置空
private static Handler sGlobalHandler; // 全局Handler统一消息分发避免多Handler混乱
private static MainActivity sMainActivity; // 全局Activity实例销毁时必须置空
private static Handler sGlobalHandler; // 全局Handler统一消息分发
// ======================== 成员属性(按「依赖→核心→辅助」分类,优先级递减)========================
// 工具类实例(单例,避免重复初始化)
@@ -71,15 +72,15 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
private BackgroundSourceUtils mBgSourceUtils;
// 应用核心实例
private App mApplication;
private MainContentView mMainContentView; // 核心视图封装类(统一管理视图)
private MainContentView mMainContentView; // 核心视图封装类
// 基础视图组件
private Toolbar mToolbar;
private ViewStub mAdsViewStub;
private ADsBannerView mADsBannerView; // 广告视图(非核心,延迟加载)
private ADsBannerView mADsBannerView; // 广告视图(非核心,延迟加载)
// 资源与菜单
private Menu mMenu;
private Drawable mFrameDrawable; // 框架背景资源适配API30主题
// 新增:服务控制核心Bean(本地持久化,管理服务自启动状态)
private Drawable mFrameDrawable; // 框架背景资源
// 服务控制核心Bean
private ControlCenterServiceBean mServiceControlBean;
// ======================== 生命周期方法(按系统调用顺序排列,逻辑闭环)========================
@@ -97,15 +98,15 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
protected void onCreate(Bundle savedInstanceState) {
LogUtils.d(TAG, "onCreate: 进入");
super.onCreate(savedInstanceState);
// 1. 初始化全局核心组件(Handler优先避免消息丢失
// 1. 初始化全局Handler优先初始化,避免消息丢失)
initGlobalHandler();
// 2. 加载布局+初始化核心流程
setContentView(R.layout.activity_main);
initPermissionUtils(); // 权限工具类(提前初始化,保障权限请求时效)
initMainContentView(); // 核心视图封装替代原ViewHolder
initCriticalView(); // 首屏核心视图Toolbar等保障首屏加载速度
initCoreUtilsAsync(); // 异步初始化工具类(避免主线程阻塞)
loadNonCriticalViewDelayed(); // 延迟加载非核心视图(广告,不影响首屏体验)
initPermissionUtils();
initMainContentView();
initCriticalView();
initCoreUtilsAsync();
loadNonCriticalViewDelayed();
LogUtils.d(TAG, "onCreate: 退出");
}
@@ -113,7 +114,6 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
protected void onPostCreate(Bundle savedInstanceState) {
LogUtils.d(TAG, "onPostCreate: 进入");
super.onPostCreate(savedInstanceState);
// 视图初始化完成后请求权限(避免视图未就绪导致的异常)
mPermissionUtils.startPermissionRequest(this);
LogUtils.d(TAG, "onPostCreate: 退出");
}
@@ -122,19 +122,17 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
protected void onResume() {
LogUtils.d(TAG, "onResume: 进入");
super.onResume();
// 1. 恢复背景(适配后台切前台场景,小米手机保活优化
// 恢复背景+同步服务开关UI仅本地配置
if (sGlobalHandler != null) {
sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
LogUtils.d(TAG, "onResume: 发送加载背景消息");
// 新增恢复时同步服务开关UI避免开关状态与实际服务状态不一致
sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH);
LogUtils.d(TAG, "onResume: 发送背景加载+服务开关更新消息");
}
// 2. 恢复广告(非核心,容错处理)
// 恢复广告
if (mADsBannerView != null) {
mADsBannerView.resumeADs(this);
LogUtils.d(TAG, "onResume: 恢复广告展示");
}
// 3. 关键调整:应用切前台,设置RemindThread前台状态(开启提醒)
// 设置RemindThread前台状态
setRemindThreadForeground(true);
LogUtils.d(TAG, "onResume: 退出");
}
@@ -143,7 +141,7 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
protected void onPause() {
LogUtils.d(TAG, "onPause: 进入");
super.onPause();
// 关键调整:应用切后台,设置RemindThread后台状态(停止提醒+重置无效电量)
// 设置RemindThread后台状态
setRemindThreadForeground(false);
LogUtils.d(TAG, "onPause: 退出");
}
@@ -152,40 +150,33 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
protected void onDestroy() {
LogUtils.d(TAG, "onDestroy: 进入");
super.onDestroy();
// 1. 释放广告资源避免内存泄漏适配API30资源管控
// 1. 释放广告资源
if (mADsBannerView != null) {
mADsBannerView.releaseAdResources();
mADsBannerView = null;
LogUtils.d(TAG, "onDestroy: 释放广告资源完成");
}
// 2. 释放核心视图资源(调用封装类释放方法,彻底断开引用)
// 2. 释放核心视图资源
if (mMainContentView != null) {
mMainContentView.releaseResources();
mMainContentView = null;
LogUtils.d(TAG, "onDestroy: 释放核心视图资源完成");
}
// 3. 关键调整Activity销毁强制销毁RemindThread单例彻底停止线程
// 3. 销毁线程+清理Handler
RemindThread.destroyInstance();
LogUtils.d(TAG, "onDestroy: 强制销毁RemindThread单例完成");
// 4. 置空全局实例(杜绝内存泄漏,必须执行)
sMainActivity = null;
// 5. 清理Handler移除所有消息避免Activity销毁后回调
if (sGlobalHandler != null) {
sGlobalHandler.removeCallbacksAndMessages(null);
sGlobalHandler = null;
LogUtils.d(TAG, "onDestroy: 清理全局Handler完成");
}
// 6. 释放Drawable资源断开回调辅助GC回收
// 4. 释放Drawable+置空全局实例
if (mFrameDrawable != null) {
mFrameDrawable.setCallback(null);
mFrameDrawable = null;
LogUtils.d(TAG, "onDestroy: 释放Drawable资源完成");
}
// 7. 置空所有辅助实例辅助GC避免残留引用
sMainActivity = null;
// 5. 置空所有辅助实例
mPermissionUtils = null;
mAppConfigUtils = null;
mBgSourceUtils = null;
mServiceControlBean = null; // 新增置空服务控制Bean
mServiceControlBean = null;
mMenu = null;
mApplication = null;
mToolbar = null;
@@ -197,32 +188,26 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
LogUtils.d(TAG, "onActivityResult: 进入requestCode=" + requestCode + "resultCode=" + resultCode);
super.onActivityResult(requestCode, resultCode, data);
// 1. 权限请求结果处理
mPermissionUtils.handlePermissionRequest(this, requestCode, resultCode, data);
// 2. 背景设置完成后重新加载背景
// 背景设置完成后重新加载背景
if (requestCode == REQUEST_READ_MEDIA_IMAGES) {
if (sGlobalHandler != null) {
sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
LogUtils.d(TAG, "onActivityResult: 背景设置完成,发送加载背景消息");
}
}
LogUtils.d(TAG, "onActivityResult: 退出");
}
// ======================== 菜单与导航方法(统一归类,用户交互入口)========================
// ======================== 菜单与导航方法 ========================
@Override
public boolean onCreateOptionsMenu(Menu menu) {
LogUtils.d(TAG, "onCreateOptionsMenu: 进入");
mMenu = menu;
// 1. 加载主题菜单
AESThemeUtil.inflateMenu(this, menu);
// 2. 调试模式加载开发菜单+单元测试菜单
if (App.isDebugging()) {
DevelopUtils.inflateMenu(this, menu);
getMenuInflater().inflate(R.menu.toolbar_unittest, mMenu);
LogUtils.d(TAG, "onCreateOptionsMenu: 调试模式,加载开发/单元测试菜单");
}
// 3. 加载主菜单
getMenuInflater().inflate(R.menu.toolbar_main, mMenu);
LogUtils.d(TAG, "onCreateOptionsMenu: 退出");
return true;
@@ -231,18 +216,13 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
@Override
public boolean onOptionsItemSelected(MenuItem item) {
LogUtils.d(TAG, "onOptionsItemSelected: 进入itemId=" + item.getItemId());
// 1. 主题切换处理切换后重建Activity生效
if (AESThemeUtil.onAppThemeItemSelected(this, item)) {
recreate();
LogUtils.d(TAG, "onOptionsItemSelected: 主题切换重建Activity");
return true;
}
// 2. 开发模式菜单处理
if (DevelopUtils.onDevelopItemSelected(this, item)) {
LogUtils.d(TAG, "onOptionsItemSelected: 开发菜单操作完成");
return true;
}
// 3. 主菜单逻辑分发(按功能分类,清晰易维护)
switch (item.getItemId()) {
case R.id.action_settings:
startActivity(new Intent(this, SettingsActivity.class));
@@ -272,31 +252,23 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
@Override
public void setupToolbar() {
super.setupToolbar();
// 主页面隐藏返回按钮(符合用户操作习惯)
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
LogUtils.d(TAG, "setupToolbar: 隐藏Toolbar返回按钮");
}
}
// ======================== 页面交互方法(用户操作响应,统一归类)========================
@Override
public void onBackPressed() {
// 退到后台不销毁Activity提升用户体验小米手机后台保活优化
moveTaskToBack(true);
LogUtils.d(TAG, "onBackPressed: 应用退到后台");
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// 保留系统默认按键分发逻辑,无自定义需求
return super.dispatchKeyEvent(event);
}
// ======================== 核心初始化方法(按优先级排序,核心优先加载)========================
/**
* 初始化权限工具类(提前初始化,保障权限请求无延迟)
*/
// ======================== 核心初始化方法 ========================
private void initPermissionUtils() {
LogUtils.d(TAG, "initPermissionUtils: 进入");
mPermissionUtils = PermissionUtils.getInstance();
@@ -304,7 +276,7 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
}
/**
* 初始化全局Handler统一消息分发Java7显式实现避免内存泄漏)
* 初始化全局Handler核心优化:弱引用+生命周期校验,杜绝内存泄漏)
*/
private void initGlobalHandler() {
LogUtils.d(TAG, "initGlobalHandler: 进入");
@@ -312,19 +284,20 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
sGlobalHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
LogUtils.d(TAG, "handleMessage: 接收消息what=" + msg.what);
// 消息处理前先校验Activity状态避免销毁后操作UI
if (sMainActivity == null || sMainActivity.isFinishing()) {
// 严格校验Activity状态避免销毁后操作UI
if (sMainActivity == null || sMainActivity.isFinishing() || sMainActivity.isDestroyed()) {
LogUtils.w(TAG, "handleMessage: Activity已销毁跳过消息处理");
return;
}
// 按消息类型分发逻辑
LogUtils.d(TAG, "handleMessage: 接收消息what=" + msg.what);
switch (msg.what) {
case MSG_RELOAD_APPCONFIG:
sMainActivity.updateViewData();
break;
case MSG_CURRENTVALUEBATTERY:
sMainActivity.mMainContentView.updateCurrentBattery(msg.arg1);
if (sMainActivity.mMainContentView != null) {
sMainActivity.mMainContentView.updateCurrentBattery(msg.arg1);
}
break;
case MSG_LOAD_BACKGROUND:
sMainActivity.reloadBackground();
@@ -334,7 +307,6 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
AppConfigBean configBean = (AppConfigBean) msg.obj;
sMainActivity.restartRemindThread(configBean);
break;
// 新增更新服务开关UI同步实际服务状态避免开关与服务状态不一致
case MSG_UPDATE_SERVICE_SWITCH:
sMainActivity.updateServiceSwitchUI();
break;
@@ -347,9 +319,6 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
LogUtils.d(TAG, "initGlobalHandler: 退出");
}
/**
* 初始化核心视图封装类统一管理视图替代原ViewHolder模式
*/
private void initMainContentView() {
LogUtils.d(TAG, "initMainContentView: 进入");
View rootView = findViewById(android.R.id.content);
@@ -358,26 +327,20 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
LogUtils.d(TAG, "initMainContentView: 退出");
}
/**
* 初始化首屏核心视图Toolbar等保障首屏加载速度优先渲染
*/
private void initCriticalView() {
LogUtils.d(TAG, "initCriticalView: 进入");
sMainActivity = this; // 赋值全局实例用于Handler回调
// 初始化Toolbar
sMainActivity = this;
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
if (mToolbar != null) {
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
LogUtils.d(TAG, "initCriticalView: Toolbar样式设置完成");
}
// 初始化广告ViewStub仅初始化Stub不立即加载广告
mAdsViewStub = findViewById(R.id.stub_ads_banner);
LogUtils.d(TAG, "initCriticalView: 退出");
}
/**
* 异步初始化核心工具类(子线程执行,避免主线程阻塞,提升首屏加载速度
* 异步初始化核心工具类(保留服务启停逻辑:应用启动时根据配置执行
*/
private void initCoreUtilsAsync() {
LogUtils.d(TAG, "initCoreUtilsAsync: 进入");
@@ -385,41 +348,59 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
@Override
public void run() {
LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程启动");
// 子线程初始化工具类无UI操作安全执行
mApplication = (App) getApplication();
mAppConfigUtils = AppConfigUtils.getInstance(getApplicationContext());
mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity());
// 新增初始化服务控制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
// 初始化服务控制Bean优先读取本地配置无则创建默认
mServiceControlBean = ControlCenterServiceBean.loadBean(getApplicationContext(), ControlCenterServiceBean.class);
if (mServiceControlBean == null) {
mServiceControlBean = new ControlCenterServiceBean(false);
ControlCenterServiceBean.saveBean(getApplicationContext(), mServiceControlBean);
LogUtils.d(TAG, "initCoreUtilsAsync: 创建默认服务控制配置(禁用)");
}
// 应用启动时:根据本地配置执行服务启停(仅执行一次)
if (mServiceControlBean.isEnableService()) {
if (!ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName())) {
runOnUiThread(new Runnable() {
@Override
public void run() {
ControlCenterService.startControlCenterService(getApplicationContext());
LogUtils.d(TAG, "initCoreUtilsAsync: 应用启动,根据配置启动服务");
}
});
}
} else {
if (ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName())) {
runOnUiThread(new Runnable() {
@Override
public void run() {
ControlCenterService.stopControlCenterService(getApplicationContext());
LogUtils.d(TAG, "initCoreUtilsAsync: 应用启动,根据配置停止服务");
}
});
}
}
// 切换主线程更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
LogUtils.d(TAG, "initCoreUtilsAsync: 切换主线程更新UI");
if (isFinishing()) {
if (isFinishing() || isDestroyed()) {
LogUtils.w(TAG, "initCoreUtilsAsync: Activity已销毁跳过UI更新");
return;
}
// 适配API30加载Drawable(添加主题参数,避免系统警告)
// 适配API30+主题加载Drawable
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mFrameDrawable = getResources().getDrawable(R.drawable.bg_frame, getTheme());
} else {
mFrameDrawable = getResources().getDrawable(R.drawable.bg_frame);
}
// 初始化完成后执行核心业务
updateViewData(); // 更新视图数据
checkServiceAsync(); // 异步检查服务状态
sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); // 加载背景
// 新增初始化服务开关UI确保开关状态与本地配置一致
sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH);
// 执行核心业务
updateViewData();
sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH);
}
});
LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程结束");
@@ -428,49 +409,38 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
LogUtils.d(TAG, "initCoreUtilsAsync: 退出");
}
/**
* 延迟加载非核心视图广告延迟500ms加载不影响首屏体验
*/
private void loadNonCriticalViewDelayed() {
LogUtils.d(TAG, "loadNonCriticalViewDelayed: 进入");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (isFinishing()) {
LogUtils.w(TAG, "loadNonCriticalViewDelayed: Activity已销毁跳过广告加载");
if (isFinishing() || isDestroyed()) {
return;
}
loadAdsView(); // 加载广告视图
loadAdsView();
}
}, DELAY_LOAD_NON_CRITICAL);
LogUtils.d(TAG, "loadNonCriticalViewDelayed: 退出");
}
// ======================== 视图操作方法UI更新统一归类========================
/**
* 加载广告视图(非核心,容错处理,避免广告加载失败影响主功能)
*/
// ======================== 视图操作方法 ========================
private void loadAdsView() {
LogUtils.d(TAG, "loadAdsView: 进入");
if (mAdsViewStub == null) {
LogUtils.e(TAG, "loadAdsView: 广告ViewStub为空,加载失败");
LogUtils.e(TAG, "loadAdsView: 广告ViewStub为空");
return;
}
if (mADsBannerView == null) {
View adsView = mAdsViewStub.inflate();
mADsBannerView = adsView.findViewById(R.id.adsbanner);
LogUtils.d(TAG, "loadAdsView: 广告视图加载完成");
}
LogUtils.d(TAG, "loadAdsView: 退出");
}
/**
* 更新视图数据调用核心视图封装类方法统一更新UI
*/
private void updateViewData() {
LogUtils.d(TAG, "updateViewData: 进入");
if (mMainContentView == null || mFrameDrawable == null) {
LogUtils.e(TAG, "updateViewData: 核心视图/框架资源为空,跳过更新");
LogUtils.e(TAG, "updateViewData: 核心视图/框架资源为空");
return;
}
mMainContentView.updateViewData(mFrameDrawable);
@@ -478,234 +448,145 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
LogUtils.d(TAG, "updateViewData: 退出");
}
/**
* 重新加载背景(适配背景切换,小米手机图片加载优化,容错处理)
*/
private void reloadBackground() {
LogUtils.d(TAG, "reloadBackground: 进入");
if (mMainContentView == null || mBgSourceUtils == null) {
LogUtils.e(TAG, "reloadBackground: 核心视图/背景工具类为空,跳过加载");
LogUtils.e(TAG, "reloadBackground: 核心视图/背景工具类为空");
return;
}
BackgroundBean currentBgBean = mBgSourceUtils.getCurrentBackgroundBean();
if (currentBgBean != null) {
mMainContentView.backgroundView.loadBackgroundBean(currentBgBean);
LogUtils.d(TAG, "reloadBackground: 加载自定义背景完成");
} else {
LogUtils.e(TAG, "reloadBackground: 无自定义背景,加载默认背景");
mMainContentView.backgroundView.setBackgroundResource(R.drawable.default_background);
}
LogUtils.d(TAG, "reloadBackground: 退出");
}
/**
* 设置主布局背景颜色适配背景配置API30安全防护
*/
private void setMainLayoutBackgroundColor() {
LogUtils.d(TAG, "setMainLayoutBackgroundColor: 进入");
if (isFinishing() || mMainContentView == null || mBgSourceUtils == null) {
LogUtils.e(TAG, "setMainLayoutBackgroundColor: 环境无效,跳过设置");
if (isFinishing() || isDestroyed() || mMainContentView == null || mBgSourceUtils == null) {
LogUtils.e(TAG, "setMainLayoutBackgroundColor: 环境无效");
return;
}
BackgroundBean currentBgBean = mBgSourceUtils.getCurrentBackgroundBean();
if (currentBgBean != null) {
int bgColor = currentBgBean.getPixelColor();
mMainContentView.mainLayout.setBackgroundColor(bgColor);
LogUtils.d(TAG, "setMainLayoutBackgroundColor: 主布局颜色设置完成");
mMainContentView.mainLayout.setBackgroundColor(currentBgBean.getPixelColor());
}
LogUtils.d(TAG, "setMainLayoutBackgroundColor: 退出");
}
/**
* 新增:更新服务开关UI同步本地配置+实际服务状态」,避免开关与服务状态不一致
* 更新服务开关UI同步本地配置,不校验服务状态)
*/
private void updateServiceSwitchUI() {
LogUtils.d(TAG, "updateServiceSwitchUI: 进入");
if (mMainContentView == null || mServiceControlBean == null) {
LogUtils.e(TAG, "updateServiceSwitchUI: 核心视图/服务控制Bean为空,跳过更新");
LogUtils.e(TAG, "updateServiceSwitchUI: 核心视图/服务控制Bean为空");
return;
}
// 开关状态 = 本地服务控制配置启用状态(自启动状态)
boolean isServiceEnabled = mServiceControlBean.isEnableService();
// 禁用开关点击避免更新UI时触发重复回调
// 仅读取本地配置状态,不涉及服务状态校验
boolean configEnabled = mServiceControlBean.isEnableService();
LogUtils.d(TAG, "updateServiceSwitchUI: 本地配置状态=" + configEnabled);
// 仅更新UI开关状态
mMainContentView.setServiceSwitchEnabled(false);
// 更新开关状态
mMainContentView.setServiceSwitchChecked(isServiceEnabled);
// 启用开关点击
mMainContentView.setServiceSwitchChecked(configEnabled);
mMainContentView.setServiceSwitchEnabled(true);
LogUtils.d(TAG, "updateServiceSwitchUI: 服务开关UI更新完成当前状态=" + isServiceEnabled);
LogUtils.d(TAG, "updateServiceSwitchUI: 退出");
}
// ======================== 服务与线程管理方法(业务核心,统一归类)========================
// ======================== 服务与线程管理方法 ========================
/**
* 异步检查服务状态子线程执行避免主线程阻塞适配API30前台服务规范
*/
private void checkServiceAsync() {
LogUtils.d(TAG, "checkServiceAsync: 进入");
new Thread(new Runnable() {
@Override
public void run() {
LogUtils.d(TAG, "checkServiceAsync: 异步线程启动");
if (mAppConfigUtils == null || mServiceControlBean == null || isFinishing()) {
LogUtils.e(TAG, "checkServiceAsync: 配置工具类/服务控制Bean/Activity无效跳过检查");
return;
}
// 服务启用本地配置且未运行时启动前台服务API30必须前台服务小米保活优化
if (mServiceControlBean.isEnableService() && !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) {
LogUtils.d(TAG, "checkServiceAsync: 服务未运行,启动前台服务");
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
ControlCenterService.startControlCenterService(getActivity());
}
}
});
}
LogUtils.d(TAG, "checkServiceAsync: 异步线程结束");
}
}).start();
LogUtils.d(TAG, "checkServiceAsync: 退出");
}
/**
* 重启RemindThread线程通过服务统一管理保障线程安全适配API30服务规范
* 重启提醒线程仅由配置变更触发不与UI开关联动
*/
private void restartRemindThread(AppConfigBean configBean) {
LogUtils.d(TAG, "restartRemindThread: 进入");
if (configBean == null || mServiceControlBean == null) {
LogUtils.e(TAG, "restartRemindThread: 配置为空/服务控制Bean为空,跳过重启");
LogUtils.e(TAG, "restartRemindThread: 配置/服务控制Bean为空");
return;
}
// 服务未启用时,不重启线程(直接返回)
if (!mServiceControlBean.isEnableService()) {
LogUtils.w(TAG, "restartRemindThread: 服务已禁用,跳过线程重启");
return;
}
// 服务未运行时,先启动服务
if (!ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) {
ControlCenterService.startControlCenterService(this);
LogUtils.d(TAG, "restartRemindThread: 服务未运行,先启动服务");
if (!ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName())) {
ControlCenterService.startControlCenterService(getApplicationContext());
}
// 关键优化:重启前先销毁旧线程,避免线程复用导致的状态脏数据
RemindThread.destroyInstance();
LogUtils.d(TAG, "restartRemindThread: 销毁旧线程完成,准备启动新线程");
// 发送重启指令到服务通过Intent传递最新配置
ControlCenterService.updateStatus(this, configBean);
LogUtils.d(TAG, "restartRemindThread: 发送线程重启指令到服务完成");
ControlCenterService.updateStatus(getApplicationContext(), configBean);
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())) {
if (!ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName())) {
LogUtils.w(TAG, "setRemindThreadForeground: 服务未运行,跳过状态设置");
return;
}
// 调用RemindThread新增方法设置前台状态线程安全
try {
RemindThread.getInstance(this, null).setAppForeground(isForeground);
LogUtils.d(TAG, "setRemindThreadForeground: RemindThread状态设置完成");
RemindThread.getInstance(getApplicationContext(), null).setAppForeground(isForeground);
} catch (IllegalArgumentException e) {
// 首次启动时Handler可能未就绪容错处理服务会后续同步状态
LogUtils.w(TAG, "setRemindThreadForeground: 线程未初始化完成,后续服务会同步状态", e);
LogUtils.w(TAG, "setRemindThreadForeground: 线程未初始化", e);
}
LogUtils.d(TAG, "setRemindThreadForeground: 退出");
}
/**
* 新增:切换服务启用/禁用状态(核心修复:持久化配置+启停服务+更新UI+重启线程
* @param isEnable true=启用服务(自启动+启动服务false=禁用服务(停止服务)
* 切换服务启用状态仅持久化本地配置点击UI开关时不联动服务启停
*/
private void toggleServiceEnableState(boolean isEnable) {
LogUtils.d(TAG, "toggleServiceEnableState: 进入,目标状态=" + isEnable);
if (mServiceControlBean == null) {
LogUtils.e(TAG, "toggleServiceEnableState: 服务控制Bean为空,切换失败");
LogUtils.e(TAG, "toggleServiceEnableState: 服务控制Bean为空");
return;
}
// 1. 更新本地服务控制配置持久化,实现自启动)
// 更新本地配置持久化
mServiceControlBean.setIsEnableService(isEnable);
ControlCenterService.updateServiceControlConfig(this, mServiceControlBean);
LogUtils.d(TAG, "toggleServiceEnableState: 服务控制配置已持久化,自启动状态=" + isEnable);
ControlCenterServiceBean.saveBean(getApplicationContext(), 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确保开关状态与实际服务状态一致
// 仅更新UI无服务启停操作
sGlobalHandler.sendEmptyMessage(MSG_UPDATE_SERVICE_SWITCH);
LogUtils.d(TAG, "toggleServiceEnableState: 服务状态切换完成");
LogUtils.d(TAG, "toggleServiceEnableState: 退出");
}
// ======================== 页面跳转方法(封装逻辑,减少冗余)========================
/**
* 启动关于页面(封装跳转逻辑,统一参数传递,便于维护)
*/
// ======================== 页面跳转方法 ========================
private void startAboutActivity() {
LogUtils.d(TAG, "startAboutActivity: 进入");
Intent aboutIntent = new Intent(getApplicationContext(), AboutActivity.class);
APPInfo appInfo = genDefaultAppInfo();
aboutIntent.putExtra(AboutActivity.EXTRA_APPINFO, appInfo);
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), aboutIntent, AboutActivity.class);
LogUtils.d(TAG, "startAboutActivity: 启动关于页面完成");
LogUtils.d(TAG, "startAboutActivity: 退出");
}
// ======================== 消息发送方法(统一封装,避免冗余,保障消息安全)========================
/**
* 发送重启RemindThread线程消息携带最新配置确保线程使用最新参数
*/
// ======================== 消息发送方法 ========================
private void sendRestartRemindThreadMessage() {
LogUtils.d(TAG, "sendRestartRemindThreadMessage: 进入");
if (sGlobalHandler == null || mAppConfigUtils == null || mAppConfigUtils.mAppConfigBean == null) {
LogUtils.e(TAG, "sendRestartRemindThreadMessage: Handler/配置为空,跳过发送");
LogUtils.e(TAG, "sendRestartRemindThreadMessage: 环境无效");
return;
}
// 复用Message避免内存抖动优化性能
Message msg = sGlobalHandler.obtainMessage(MSG_RESTART_REMIND_THREAD);
msg.obj = mAppConfigUtils.mAppConfigBean;
sGlobalHandler.sendMessage(msg);
LogUtils.d(TAG, "sendRestartRemindThreadMessage: 发送线程重启消息完成");
LogUtils.d(TAG, "sendRestartRemindThreadMessage: 退出");
}
// ======================== 静态工具方法(全局调用,统一入口)========================
/**
* 重新加载应用配置(全局调用,触发视图更新)
*/
// ======================== 静态工具方法 ========================
public static void reloadAppConfig() {
LogUtils.d(TAG, "reloadAppConfig: 发送重新加载配置消息");
LogUtils.d(TAG, "reloadAppConfig: 发送配置更新消息");
if (sGlobalHandler != null) {
sGlobalHandler.sendEmptyMessage(MSG_RELOAD_APPCONFIG);
}
}
/**
* 发送当前电量更新消息全局调用实时更新UI
*/
public static void sendCurrentBatteryValueMessage(int value) {
LogUtils.d(TAG, "sendCurrentBatteryValueMessage: 电量=" + value + ",发送更新消息");
LogUtils.d(TAG, "sendCurrentBatteryValueMessage: 电量=" + value);
if (sGlobalHandler != null) {
Message msg = sGlobalHandler.obtainMessage(MSG_CURRENTVALUEBATTERY);
msg.arg1 = value;
@@ -713,14 +594,11 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
}
}
// ======================== 辅助工具方法(封装重复逻辑,提升复用性)========================
/**
* 从URI获取图片真实路径适配API30媒体存储权限Java7显式Cursor操作避免资源泄漏
*/
// ======================== 辅助工具方法 ========================
private String getRealPathFromUri(Uri contentUri) {
LogUtils.d(TAG, "getRealPathFromUri: 进入");
if (contentUri == null) {
LogUtils.e(TAG, "getRealPathFromUri: URI为空,获取失败");
LogUtils.e(TAG, "getRealPathFromUri: URI为空");
return null;
}
String realPath = null;
@@ -732,24 +610,16 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
if (columnIndex > -1) {
realPath = cursor.getString(columnIndex);
LogUtils.d(TAG, "getRealPathFromUri: 获取图片路径=" + realPath);
}
}
} finally {
// Java7必须显式关闭Cursor避免资源泄漏无try-with-resources语法
cursor.close();
}
}
if (realPath == null) {
LogUtils.e(TAG, "getRealPathFromUri: 获取图片路径失败");
}
LogUtils.d(TAG, "getRealPathFromUri: 退出");
LogUtils.d(TAG, "getRealPathFromUri: 退出,路径=" + realPath);
return realPath;
}
/**
* 生成默认应用信息(关于页面使用,封装参数赋值,便于维护)
*/
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo: 进入");
String branchName = "powerbell";
@@ -764,12 +634,11 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=PowerBell");
appInfo.setAppAPKName("PowerBell");
appInfo.setAppAPKFolderName("PowerBell");
LogUtils.d(TAG, "genDefaultAppInfo: 生成应用信息完成");
LogUtils.d(TAG, "genDefaultAppInfo: 退出");
return appInfo;
}
// ======================== MainContentView 事件回调实现(视图事件触发业务逻辑)========================
// ======================== MainContentView 事件回调 ========================
@Override
public void onChargeReminderSwitchChanged(boolean isChecked) {
LogUtils.d(TAG, "onChargeReminderSwitchChanged: 状态=" + isChecked);
@@ -785,7 +654,7 @@ public class MainActivity extends WinBoLLActivity implements MainContentView.OnV
@Override
public void onServiceSwitchChanged(boolean isChecked) {
LogUtils.d(TAG, "onServiceSwitchChanged: 状态=" + isChecked);
// 核心修复:点击服务开关,触发「配置持久化+服务启停+UI更新」全链路逻辑
// 仅更新本地配置,不触发服务启停
toggleServiceEnableState(isChecked);
}

View File

@@ -1,6 +1,7 @@
package cc.winboll.studio.powerbell.services;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -10,24 +11,26 @@ import android.os.IBinder;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import java.io.Serializable;
import java.util.List;
import androidx.core.app.NotificationCompat;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.threads.RemindThread;
import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import java.io.Serializable;
import java.util.List;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 15:48
* @Describe 电池提醒核心服务:通过本地持久化 ControlCenterServiceBean 控制自启动,统一管理线程、通知、配置适配Java7+API30
* @Describe 电池提醒核心服务:修复前台服务超时异常优先在onCreate发送通知适配Java7+API36
*/
public class ControlCenterService extends Service {
// ================================== 静态常量(置顶统一管理,避免魔法值)=================================
private static final String TAG = "ControlCenterService";
public static final String TAG = "ControlCenterService";
// 服务指令常量(带包名前缀,防止冲突)
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";
@@ -52,7 +55,7 @@ public class ControlCenterService extends Service {
private boolean mIsDestroyed = false; // 服务销毁标记
private final Object mServiceLock = new Object(); // 全局并发锁
// ================================== 服务生命周期方法onCreate→onStartCommand→onDestroy=================================
// ================================== 服务生命周期方法(核心修复:onCreate优先发送通知=================================
@Override
public void onCreate() {
super.onCreate();
@@ -60,7 +63,11 @@ public class ControlCenterService extends Service {
synchronized (mServiceLock) {
isServiceRunning = true;
mIsDestroyed = false;
// 初始化服务控制配置:优先读取本地持久化配置,无则创建默认禁用配置
// ================================== 【修复点1最优先执行前台通知发送】=================================
initForegroundNotificationImmediately();
// 原有逻辑:读取本地服务控制配置
mServiceControlBean = ControlCenterServiceBean.loadBean(this, ControlCenterServiceBean.class);
if (mServiceControlBean == null) {
mServiceControlBean = new ControlCenterServiceBean(false);
@@ -70,7 +77,7 @@ public class ControlCenterService extends Service {
LogUtils.d(TAG, "onCreate: 从本地读取服务控制配置,启用状态=" + mServiceControlBean.isEnableService());
}
}
LogUtils.d(TAG, "onCreate: 服务创建完成,当前服务控制状态:" + (mServiceControlBean.isEnableService() ? "启用" : "禁用"));
LogUtils.d(TAG, "onCreate: 服务创建完成,前台通知已发送,当前服务控制状态:" + (mServiceControlBean.isEnableService() ? "启用" : "禁用"));
}
@Override
@@ -82,9 +89,9 @@ public class ControlCenterService extends Service {
synchronized (mServiceLock) {
if (mServiceControlBean.isEnableService()) {
LogUtils.d(TAG, "onStartCommand: 服务已启用,执行核心逻辑");
// 启用状态:处理指令+绑定前台通知+启动业务返回START_STICKY回收后重启
// 启用状态:处理指令+启动业务返回START_STICKY回收后重启
handleExternalCommand(intent);
rebindForegroundNotify();
// ================================== 【修复点2删除rebindForegroundNotify(),避免重复调用】=================================
initServiceBusinessIfNeed(); // 按需初始化业务(避免重复初始化)
// 新增:业务初始化完成后,同步应用前台状态到线程(防止状态丢失)
syncAppForegroundStateToThread();
@@ -123,6 +130,54 @@ public class ControlCenterService extends Service {
return null; // 无需绑定服务返回null
}
// ================================== 【新增核心方法:优先发送前台通知,带兜底方案】=================================
/**
* 独立初始化前台通知并立即发送不依赖任何业务逻辑确保5秒内完成startForeground()
* 防止工具类异常导致超时,内置兜底方案
*/
private void initForegroundNotificationImmediately() {
LogUtils.d(TAG, "initForegroundNotificationImmediately: 强制初始化前台通知");
try {
// 1. 初始化通知工具类
if (mNotificationManager == null) {
mNotificationManager = new NotificationManagerUtils(this);
}
// 2. 构建极简前台通知模型(不依赖业务配置,确保最快速度发送)
if (mForegroundNotifyMsg == null) {
mForegroundNotifyMsg = new NotificationMessage();
mForegroundNotifyMsg.setTitle("电池监测服务");
mForegroundNotifyMsg.setContent("后台运行中");
mForegroundNotifyMsg.setRemindMSG("service_running");
}
// 3. 立即调用工具类发送前台通知
mNotificationManager.startForegroundServiceNotify(this, mForegroundNotifyMsg);
LogUtils.d(TAG, "initForegroundNotificationImmediately: 前台通知发送成功ID=" + NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE);
} catch (Exception e) {
LogUtils.e(TAG, "initForegroundNotificationImmediately: 工具类发送失败,执行兜底方案", e);
// 兜底方案绕过工具类直接构建Notification调用startForeground
fallbackStartForeground();
}
}
/**
* 前台通知兜底方案:当工具类异常时,直接创建通知,确保不触发超时异常
*/
private void fallbackStartForeground() {
try {
Notification notification = new NotificationCompat.Builder(this, NotificationManagerUtils.CHANNEL_ID_FOREGROUND)
.setContentTitle("电池监测服务")
.setContentText("后台运行中")
.setSmallIcon(R.drawable.ic_launcher) // 必须设置小图标,否则通知不显示
.setPriority(NotificationCompat.PRIORITY_LOW)
.build();
// 直接调用startForeground不依赖任何外部工具
startForeground(NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE, notification);
LogUtils.d(TAG, "fallbackStartForeground: 兜底前台通知发送成功");
} catch (Exception e) {
LogUtils.e(TAG, "fallbackStartForeground: 兜底发送失败,服务会被系统强制杀死", e);
}
}
// ================================== 核心服务控制配置本地持久化读写替代Intent传递=================================
/**
* 读取本地最新服务控制配置(实时同步,确保与用户设置一致)
@@ -203,6 +258,7 @@ public class ControlCenterService extends Service {
// ================================== 核心业务初始化方法(按依赖顺序排列)=================================
/**
* 初始化通知管理工具类,构建前台保活通知模型
* 【修复点3移除内部的startForegroundNotifyImmediately()调用,避免重复发送】
*/
private void initNotificationManager() {
LogUtils.d(TAG, "initNotificationManager: 初始化通知工具类");
@@ -215,8 +271,7 @@ public class ControlCenterService extends Service {
mForegroundNotifyMsg.setTitle("电池提醒服务运行中");
mForegroundNotifyMsg.setContent("后台持续监测电池状态,确保提醒及时");
mForegroundNotifyMsg.setRemindMSG("service_running");
// 启动前台保活通知
startForegroundNotifyImmediately();
// 移除此处不再发送通知通知已在onCreate中发送
}
LogUtils.d(TAG, "initNotificationManager: 通知工具类初始化完成");
} catch (Exception e) {
@@ -226,6 +281,7 @@ public class ControlCenterService extends Service {
/**
* 立即启动前台保活通知API26+ 前台服务必需)
* 注当前仅作为备用方法实际已在initForegroundNotificationImmediately中调用
*/
private void startForegroundNotifyImmediately() {
LogUtils.d(TAG, "startForegroundNotifyImmediately: 启动前台保活通知");
@@ -243,7 +299,7 @@ public class ControlCenterService extends Service {
}
/**
* 重新绑定前台通知(onStartCommand触发防止保活状态丢失
* 重新绑定前台通知(已删除调用防止重复执行startForeground
*/
private void rebindForegroundNotify() {
if (mNotificationManager == null || mNotificationManager.getForegroundServiceNotify() == null) {
@@ -447,9 +503,7 @@ public class ControlCenterService extends Service {
LogUtils.w(TAG, "handleExternalCommand: 服务已禁用,忽略线程重启指令");
}
}
}
// 2. 新增:处理应用前台/后台状态同步指令MainActivity触发
else if (ACTION_SYNC_APP_FOREGROUND_STATE.equals(action)) {
} else if (ACTION_SYNC_APP_FOREGROUND_STATE.equals(action)) {
boolean isForeground = intent.getBooleanExtra(EXTRA_APP_FOREGROUND_STATE, false);
synchronized (mServiceLock) {
mIsAppForeground = isForeground;
@@ -514,9 +568,10 @@ public class ControlCenterService extends Service {
LogUtils.d(TAG, "clearAllReferences: 所有引用已置空");
}
// ================================== 静态工具方法(新增前台状态同步入口,完善保活优化=================================
// ================================== 静态工具方法(修复启停逻辑颠倒问题=================================
/**
* 启动服务静态入口从本地读取控制配置显式绑定包名适配API30+
* 【修复点4统一使用startForegroundService适配API26+】
*/
public static void startControlCenterService(Context context) {
LogUtils.d(TAG, "startControlCenterService: 启动服务入口");
@@ -524,26 +579,14 @@ public class ControlCenterService extends Service {
LogUtils.e(TAG, "startControlCenterService: Context为空启动失败");
return;
}
if (isServiceRunning(context, ControlCenterService.class)) {
LogUtils.d(TAG, "startControlCenterService: 服务已运行,同步本地控制配置");
// 服务已运行触发onStartCommand同步最新配置
sendSyncConfigCommand(context);
return;
}
Intent intent = new Intent(context, ControlCenterService.class);
intent.setPackage(context.getPackageName());
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
LogUtils.d(TAG, "startControlCenterService: 服务启动指令发送成功");
} catch (Exception e) {
LogUtils.e(TAG, "startControlCenterService: 启动服务异常", e);
// 修复API26+必须调用startForegroundService避免系统异常
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
LogUtils.d(TAG, "startControlCenterService: 服务启动指令发送成功");
}
/**
@@ -555,19 +598,14 @@ public class ControlCenterService extends Service {
LogUtils.e(TAG, "stopControlCenterService: Context为空停止失败");
return;
}
if (!isServiceRunning(context, ControlCenterService.class)) {
LogUtils.d(TAG, "stopControlCenterService: 服务未运行,跳过停止");
return;
}
Intent intent = new Intent(context, ControlCenterService.class);
intent.setPackage(context.getPackageName());
context.stopService(intent);
LogUtils.d(TAG, "stopControlCenterService: 服务停止指令发送成功");
}
/**
* 新增:更新服务控制配置(本地持久化,动态切换服务启用/禁用状态)
* 【修复点5修复启停逻辑颠倒问题】
*/
public static void updateServiceControlConfig(Context context, ControlCenterServiceBean controlBean) {
LogUtils.d(TAG, "updateServiceControlConfig: 更新服务控制配置,新状态=" + controlBean.isEnableService());
@@ -580,32 +618,14 @@ public class ControlCenterService extends Service {
ControlCenterServiceBean.saveBean(context, controlBean);
LogUtils.d(TAG, "updateServiceControlConfig: 控制配置已持久化");
// 第二步:若服务已运行,触发同步(让服务立即生效
if (isServiceRunning(context, ControlCenterService.class)) {
sendSyncConfigCommand(context);
LogUtils.d(TAG, "updateServiceControlConfig: 服务已运行,已触发配置同步");
} else {
// 服务未运行,按需启动服务(根据新配置决定是否启用业务)
// 第二步:启停服务(修复:启用则启动,禁用则停止
if (controlBean.isEnableService()) {
stopControlCenterService(context);
startControlCenterService(context);
LogUtils.d(TAG, "updateServiceControlConfig: 服务未运行,已启动服务并加载新配置");
}
}
/**
* 新增:发送配置同步指令(触发服务读取本地最新配置,立即生效)
*/
private static void sendSyncConfigCommand(Context context) {
Intent intent = new Intent(context, ControlCenterService.class);
intent.setPackage(context.getPackageName());
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
} catch (Exception e) {
LogUtils.e(TAG, "sendSyncConfigCommand: 发送同步指令异常", e);
LogUtils.d(TAG, "updateServiceControlConfig: 服务已设置启动");
} else {
stopControlCenterService(context);
LogUtils.d(TAG, "updateServiceControlConfig: 服务已设置停止");
}
}

View File

@@ -66,6 +66,8 @@ public class MainContentView {
// 配置变更确认对话框(单例复用,避免重复创建)
private AlertDialog mConfigConfirmDialog;
// 对话框 Builder核心新增解决 setMessage 不生效问题)
private AlertDialog.Builder mDialogBuilder;
// 临时存储变更数据(对话框确认前缓存,取消时恢复)
private TempConfigData mTempConfigData;
// 对话框状态锁(避免快速点击重复弹窗)
@@ -116,7 +118,7 @@ public class MainContentView {
// 执行核心初始化流程(按顺序执行,避免依赖空指针)
bindViews(rootView);
initBatteryDrawables();
initConfirmDialog();
initConfirmDialog(); // 先初始化对话框,再绑定监听
bindViewListeners();
LogUtils.d(TAG, "constructor: 整体初始化完成");
@@ -176,7 +178,7 @@ public class MainContentView {
}
/**
* 初始化配置变更确认对话框(单例复用,无模式,点击外部关闭,适配 API30 对话框机制
* 初始化配置变更确认对话框(核心优化:保存 Builder 实例,解决消息不生效问题
*/
private void initConfirmDialog() {
LogUtils.d(TAG, "initConfirmDialog: 开始初始化确认对话框");
@@ -185,29 +187,31 @@ public class MainContentView {
return;
}
// 构建无模式对话框(点击外部可关闭,取消改动
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("配置变更确认");
// 优化:区分服务开关/普通配置,显示不同提示语(提升用户体验)
builder.setMessage("是否确认修改当前配置?");
// 1. 初始化 Builder核心后续通过 Builder 更新消息
mDialogBuilder = new AlertDialog.Builder(mContext);
mDialogBuilder.setTitle("配置变更确认");
mDialogBuilder.setMessage("是否确认修改当前配置?");
// 确定按钮:保存配置+回调+更新视图
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
mDialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
confirmConfigChange(); // 确认变更,保存配置
dialog.dismiss();
}
});
// 取消按钮:恢复原始配置(补充物理取消按钮,提升用户体验)
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
mDialogBuilder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelConfigChange(); // 取消变更,恢复原始值
dialog.dismiss();
}
});
// 对话框外部点击监听:关闭对话框+恢复原始配置
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
mDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancelConfigChange(); // 取消变更,恢复原始值
@@ -215,8 +219,8 @@ public class MainContentView {
}
});
// 初始化对话框实例(设置可取消,支持外部点击关闭)
mConfigConfirmDialog = builder.create();
// 2. 初始化对话框实例(设置可取消,支持外部点击关闭)
mConfigConfirmDialog = mDialogBuilder.create();
mConfigConfirmDialog.setCancelable(true);
mConfigConfirmDialog.setCanceledOnTouchOutside(true);
LogUtils.d(TAG, "initConfirmDialog: 确认对话框初始化完成");
@@ -246,6 +250,7 @@ public class MainContentView {
}
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_CHARGE_SEEKBAR, originalValue, progress);
updateDialogMessageByChangeType(); // 更新提示语
showConfigConfirmDialog();
LogUtils.d(TAG, "ChargeReminderSeekBar: 触摸抬起触发变更,原始值=" + originalValue + ", 新进度=" + progress);
}
@@ -277,6 +282,7 @@ public class MainContentView {
if (originalValue == newValue) return;
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_CHARGE_SWITCH, originalValue, newValue);
updateDialogMessageByChangeType(); // 更新提示语
showConfigConfirmDialog();
LogUtils.d(TAG, "cbEnableChargeReminder: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue);
}
@@ -297,6 +303,7 @@ public class MainContentView {
}
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_USAGE_SEEKBAR, originalValue, progress);
updateDialogMessageByChangeType(); // 更新提示语
showConfigConfirmDialog();
LogUtils.d(TAG, "UsageReminderSeekBar: 触摸抬起触发变更,原始值=" + originalValue + ", 新进度=" + progress);
}
@@ -328,6 +335,7 @@ public class MainContentView {
if (originalValue == newValue) return;
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_USAGE_SWITCH, originalValue, newValue);
updateDialogMessageByChangeType(); // 更新提示语
showConfigConfirmDialog();
LogUtils.d(TAG, "cbEnableUsageReminder: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue);
}
@@ -335,19 +343,21 @@ public class MainContentView {
LogUtils.d(TAG, "bindViewListeners: 耗电提醒开关监听绑定完成");
}
// 服务总开关监听(核心优化:从 AppConfig 改为读取服务控制Bean确保状态一致
// 服务总开关监听(核心优化:逻辑与其他控件完全对齐
if (swEnableService != null) {
swEnableService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 关键调整服务开关状态从服务控制Bean读取而非AppConfig
// 从服务控制Bean读取原始状态确保与实际一致
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);
}
@@ -376,7 +386,7 @@ public class MainContentView {
int currentVal = mAppConfigUtils.getCurrentBatteryValue();
boolean chargeEnable = mAppConfigUtils.isChargeReminderEnabled();
boolean usageEnable = mAppConfigUtils.isUsageReminderEnabled();
// 关键调整:服务开关状态从服务控制Bean读取确保与服务实际状态一致
// 从服务控制Bean读取状态,确保UI与实际一致
boolean serviceEnable = getServiceEnableState();
LogUtils.d(TAG, "updateViewData: 配置数据读取完成charge=" + chargeVal + ", usage=" + usageVal + ", current=" + currentVal + ", serviceEnable=" + serviceEnable);
@@ -424,7 +434,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));
@@ -471,6 +481,8 @@ public class MainContentView {
mConfigConfirmDialog.setOnCancelListener(null);
mConfigConfirmDialog = null;
}
// 释放 Builder
mDialogBuilder = null;
// 释放临时数据
mTempConfigData = null;
@@ -506,7 +518,7 @@ public class MainContentView {
}
/**
* 新增:设置服务开关启用状态(外部调用,同步 UI 与服务状态,适配 Activity 视图刷新)
* 设置服务开关启用状态(外部调用,同步 UI 与服务状态,适配 Activity 视图刷新)
* @param enabled 服务启用状态
*/
public void setServiceSwitchChecked(boolean enabled) {
@@ -517,7 +529,7 @@ public class MainContentView {
}
/**
* 新增:设置服务开关点击状态(外部调用,避免更新 UI 时触发重复回调)
* 设置服务开关点击状态(外部调用,避免更新 UI 时触发重复回调)
* @param enabled 是否允许点击
*/
public void setServiceSwitchEnabled(boolean enabled) {
@@ -537,9 +549,9 @@ public class MainContentView {
LogUtils.d(TAG, "showConfigConfirmDialog: 对话框已显示,跳过重复调用");
return;
}
// 基础校验:对话框/上下文为空
if (mConfigConfirmDialog == null || mContext == null) {
LogUtils.e(TAG, "showConfigConfirmDialog: 对话框/上下文异常,无法显示");
// 基础校验:对话框/上下文/Builder 为空
if (mDialogBuilder == null || mContext == null) {
LogUtils.e(TAG, "showConfigConfirmDialog: 对话框Builder/上下文异常,无法显示");
if (mTempConfigData != null) cancelConfigChange();
return;
}
@@ -550,20 +562,20 @@ public class MainContentView {
if (mTempConfigData != null) cancelConfigChange();
return;
}
// 重新构建对话框(核心:确保最新消息生效)
mConfigConfirmDialog = mDialogBuilder.create();
// 显示对话框,设置状态锁+关闭监听
if (!mConfigConfirmDialog.isShowing()) {
isDialogShowing = true;
mConfigConfirmDialog.show();
// 对话框关闭时解锁(无论确认/取消/外部点击,均解锁)
mConfigConfirmDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
isDialogShowing = false;
mConfigConfirmDialog.setOnDismissListener(null); // 移除监听,避免内存泄漏
}
});
LogUtils.d(TAG, "showConfigConfirmDialog: 确认对话框显示成功");
}
mConfigConfirmDialog.show();
isDialogShowing = true;
// 对话框关闭时解锁
mConfigConfirmDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
isDialogShowing = false;
mConfigConfirmDialog.setOnDismissListener(null);
}
});
LogUtils.d(TAG, "showConfigConfirmDialog: 确认对话框显示成功");
}
/**
@@ -576,36 +588,36 @@ public class MainContentView {
}
switch (mTempConfigData.changeType) {
// 充电提醒开关(适配 AppConfigUtils 无 Activity 参数调用)
// 充电提醒开关
case CHANGE_TYPE_CHARGE_SWITCH:
mAppConfigUtils.setChargeReminderEnabled(mTempConfigData.newBooleanValue);
mActionListener.onChargeReminderSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 充电提醒开关确认,值=" + mTempConfigData.newBooleanValue);
break;
// 耗电提醒开关(适配 AppConfigUtils 无 Activity 参数调用)
// 耗电提醒开关
case CHANGE_TYPE_USAGE_SWITCH:
mAppConfigUtils.setUsageReminderEnabled(mTempConfigData.newBooleanValue);
mActionListener.onUsageReminderSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 耗电提醒开关确认,值=" + mTempConfigData.newBooleanValue);
break;
// 服务总开关(核心优化同步更新服务控制Bean而非AppConfig
// 服务总开关(核心:持久化配置+触发 Activity 回调
case CHANGE_TYPE_SERVICE_SWITCH:
// 1. 保存服务控制配置到本地(持久化自启动状态)
// 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,触发服务启停
// 2. 强制触发 Activity 回调,执行服务启停逻辑
mActionListener.onServiceSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 服务开关确认,值=" + mTempConfigData.newBooleanValue + ",已持久化配置");
break;
// 充电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用)
// 充电提醒进度条
case CHANGE_TYPE_CHARGE_SEEKBAR:
mAppConfigUtils.setChargeReminderValue(mTempConfigData.newIntValue);
mActionListener.onChargeReminderProgressChanged(mTempConfigData.newIntValue);
LogUtils.d(TAG, "confirmConfigChange: 充电提醒进度确认,值=" + mTempConfigData.newIntValue);
break;
// 耗电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用)
// 耗电提醒进度条
case CHANGE_TYPE_USAGE_SEEKBAR:
mAppConfigUtils.setUsageReminderValue(mTempConfigData.newIntValue);
mActionListener.onUsageReminderProgressChanged(mTempConfigData.newIntValue);
@@ -630,28 +642,24 @@ public class MainContentView {
}
switch (mTempConfigData.changeType) {
// 充电提醒开关:恢复原始状态
case CHANGE_TYPE_CHARGE_SWITCH:
if (cbEnableChargeReminder != null) {
cbEnableChargeReminder.setChecked(mTempConfigData.originalBooleanValue);
}
LogUtils.d(TAG, "cancelConfigChange: 充电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break;
// 耗电提醒开关:恢复原始状态
case CHANGE_TYPE_USAGE_SWITCH:
if (cbEnableUsageReminder != null) {
cbEnableUsageReminder.setChecked(mTempConfigData.originalBooleanValue);
}
LogUtils.d(TAG, "cancelConfigChange: 耗电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break;
// 服务总开关恢复原始状态核心优化从服务控制Bean读取原始值
case CHANGE_TYPE_SERVICE_SWITCH:
if (swEnableService != null) {
swEnableService.setChecked(mTempConfigData.originalBooleanValue);
}
LogUtils.d(TAG, "cancelConfigChange: 服务开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break;
// 充电提醒进度条:恢复原始进度+更新视图
case CHANGE_TYPE_CHARGE_SEEKBAR:
if (sbChargeReminder != null) {
sbChargeReminder.setProgress(mTempConfigData.originalIntValue);
@@ -663,7 +671,6 @@ public class MainContentView {
}
LogUtils.d(TAG, "cancelConfigChange: 充电提醒进度取消,恢复值=" + mTempConfigData.originalIntValue);
break;
// 耗电提醒进度条:恢复原始进度+更新视图
case CHANGE_TYPE_USAGE_SEEKBAR:
if (sbUsageReminder != null) {
sbUsageReminder.setProgress(mTempConfigData.originalIntValue);
@@ -705,7 +712,7 @@ public class MainContentView {
}
/**
* 新增:获取服务启用状态统一从服务控制Bean读取确保全链路状态一致
* 获取服务启用状态统一从服务控制Bean读取确保全链路状态一致
* @return 服务启用状态true=启用false=禁用)
*/
private boolean getServiceEnableState() {
@@ -715,17 +722,22 @@ public class MainContentView {
}
/**
* 新增:根据变更类型更新对话框提示语(差异化提示,提升用户体验
* 根据变更类型更新对话框提示语(核心优化:通过 Builder 更新,确保生效
*/
private void updateDialogMessageByChangeType() {
if (mConfigConfirmDialog == null || mTempConfigData == null) return;
if (mDialogBuilder == null || mTempConfigData == null) return;
String message;
if (mTempConfigData.changeType == CHANGE_TYPE_SERVICE_SWITCH) {
message = mTempConfigData.newBooleanValue ? "启用服务后,将后台持续监控电池状态,是否确认?" : "禁用服务后,电池监控功能将停止,是否确认?";
// 服务开关差异化提示语
message = mTempConfigData.newBooleanValue ?
"启用服务后,将后台持续监控电池状态,是否确认?" :
"禁用服务后,电池监控功能将停止,是否确认?";
} else {
// 普通配置默认提示语
message = "是否确认修改当前配置?";
}
mConfigConfirmDialog.setMessage(message);
// 通过 Builder 设置消息,确保弹窗显示最新内容
mDialogBuilder.setMessage(message);
}
// ======================== 事件回调接口(解耦视图与业务,提升扩展性)========================