diff --git a/powerbell/build.properties b/powerbell/build.properties index 4ff6039..8c9342f 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Dec 16 21:18:25 HKT 2025 +#Wed Dec 17 05:49:07 GMT 2025 stageCount=10 libraryProject= baseVersion=15.14 publishVersion=15.14.9 -buildCount=0 -baseBetaVersion=15.14.10 \ No newline at end of file +buildCount=2 +baseBetaVersion=15.14.10 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java index f7182a4..91db82f 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java @@ -15,13 +15,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewStub; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.SeekBar; -import android.widget.Switch; -import android.widget.TextView; import androidx.appcompat.widget.Toolbar; import cc.winboll.studio.libaes.activitys.AboutActivity; import cc.winboll.studio.libaes.models.APPInfo; @@ -44,70 +37,50 @@ import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.PermissionUtils; import cc.winboll.studio.powerbell.utils.ServiceUtils; import cc.winboll.studio.powerbell.views.BackgroundView; -import cc.winboll.studio.powerbell.views.BatteryDrawable; +import cc.winboll.studio.powerbell.views.MainContentView; import cc.winboll.studio.powerbell.views.VerticalSeekBar; import java.io.Serializable; /** - * 主活动类:应用核心页面,管理电池监控、背景设置、服务控制等核心功能 - * 适配:Java7 | API29-30 | 小米手机,优化性能与稳定性,杜绝内存泄漏 + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 13:14 + * @Describe 主活动类:应用核心页面,管理电池监控、背景设置、服务控制等核心功能 + * 适配:Java7 | API30 | 小米手机,优化性能与稳定性,杜绝内存泄漏 */ -public class MainActivity extends WinBoLLActivity { - // ======================== 静态常量(顶部统一,抽离魔法值,便于维护)======================== +public class MainActivity extends WinBoLLActivity implements MainContentView.OnViewActionListener { + // ======================== 静态常量(置顶统一,抽离魔法值,便于维护)======================== public static final String TAG = "MainActivity"; + // 请求码 private static final int REQUEST_READ_MEDIA_IMAGES = 1001; // 背景设置请求码 - private static final int DELAY_LOAD_NON_CRITICAL = 500; // 非核心视图延迟加载时间(ms) - // Handler消息标识 + // 延迟时间 + private static final int DELAY_LOAD_NON_CRITICAL = 500; // 非核心视图延迟加载(ms) + // Handler消息标识(按业务优先级排序) public static final int MSG_RELOAD_APPCONFIG = 0; // 重新加载配置 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线程 - // ======================== 静态成员(全局共享,严格控制生命周期)======================== - public static MainActivity sMainActivity; // 全局Activity实例(避免内存泄漏,及时置空) - private static Handler sGlobalHandler; // 全局Handler(统一消息分发) + // ======================== 静态成员(全局共享,严格管控生命周期)======================== + public static MainActivity sMainActivity; // 全局Activity实例(销毁时必须置空) + private static Handler sGlobalHandler; // 全局Handler(统一消息分发,避免多Handler混乱) - // ======================== 成员属性(按功能分类,优先核心依赖)======================== - // 工具类实例(单例模式,避免重复初始化) + // ======================== 成员属性(按「依赖→核心→辅助」分类,优先级递减)======================== + // 工具类实例(单例,避免重复初始化) private PermissionUtils mPermissionUtils; private AppConfigUtils mAppConfigUtils; private BackgroundSourceUtils mBgSourceUtils; - // 应用与视图核心实例 + // 应用核心实例 private App mApplication; + private MainContentView mMainContentView; // 核心视图封装类(统一管理视图) + // 基础视图组件 private Toolbar mToolbar; private ViewStub mAdsViewStub; - private ADsBannerView mADsBannerView; - // 视图缓存容器(ViewHolder模式,减少findViewById调用) - private ViewHolder mViewHolder; - // 菜单与资源实例 + private ADsBannerView mADsBannerView; // 广告视图(非核心,延迟加载) + // 资源与菜单 private Menu mMenu; - private Drawable mFrameDrawable; - // 电池图标Drawable(复用实例,减少内存分配) - private BatteryDrawable mCurrentBatteryDrawable; - private BatteryDrawable mChargeReminderBatteryDrawable; - private BatteryDrawable mUsageReminderBatteryDrawable; + private Drawable mFrameDrawable; // 框架背景资源(适配API30主题) - // ======================== 视图缓存容器(ViewHolder模式,优化性能)======================== - private static class ViewHolder { - BackgroundView backgroundView; // 背景视图 - RelativeLayout mainLayout; // 主布局 - LinearLayout llLeftSeekBar; // 充电提醒进度条布局 - LinearLayout llRightSeekBar; // 耗电提醒进度条布局 - CheckBox cbEnableChargeReminder; // 充电提醒开关 - CheckBox cbEnableUsageReminder; // 耗电提醒开关 - Switch swEnableService; // 服务总开关 - TextView tvTips; // 提示文本 - TextView tvChargeReminderValue; // 充电提醒阈值文本 - TextView tvUsageReminderValue; // 耗电提醒阈值文本 - TextView tvCurrentBatteryValue; // 当前电量文本 - VerticalSeekBar sbChargeReminder; // 充电提醒进度条 - VerticalSeekBar sbUsageReminder; // 耗电提醒进度条 - ImageView ivCurrentBattery; // 当前电量图标 - ImageView ivChargeReminderBattery; // 充电提醒电量图标 - ImageView ivUsageReminderBattery; // 耗电提醒电量图标 - } - - // ======================== 生命周期方法(按执行顺序排列,清晰可控)======================== + // ======================== 生命周期方法(按系统调用顺序排列,逻辑闭环)======================== @Override public Activity getActivity() { return this; @@ -120,124 +93,137 @@ public class MainActivity extends WinBoLLActivity { @Override protected void onCreate(Bundle savedInstanceState) { - LogUtils.d(TAG, "onCreate: enter"); + LogUtils.d(TAG, "onCreate: 进入"); super.onCreate(savedInstanceState); - // 初始化全局核心组件(Handler优先,避免消息丢失) + // 1. 初始化全局核心组件(Handler优先,避免消息丢失) initGlobalHandler(); - // 加载布局+初始化核心流程 + // 2. 加载布局+初始化核心流程 setContentView(R.layout.activity_main); - initPermissionUtils(); - initViewHolder(); - initCriticalView(); - initCoreUtilsAsync(); - loadNonCriticalViewDelayed(); - LogUtils.d(TAG, "onCreate: exit"); + initPermissionUtils(); // 权限工具类(提前初始化,保障权限请求时效) + initMainContentView(); // 核心视图封装(替代原ViewHolder) + initCriticalView(); // 首屏核心视图(Toolbar等,保障首屏加载速度) + initCoreUtilsAsync(); // 异步初始化工具类(避免主线程阻塞) + loadNonCriticalViewDelayed(); // 延迟加载非核心视图(广告,不影响首屏体验) + LogUtils.d(TAG, "onCreate: 退出"); } @Override protected void onPostCreate(Bundle savedInstanceState) { - LogUtils.d(TAG, "onPostCreate: enter"); + LogUtils.d(TAG, "onPostCreate: 进入"); super.onPostCreate(savedInstanceState); - // 权限请求(PostCreate确保视图初始化完成) + // 视图初始化完成后请求权限(避免视图未就绪导致的异常) mPermissionUtils.startPermissionRequest(this); - LogUtils.d(TAG, "onPostCreate: exit"); + LogUtils.d(TAG, "onPostCreate: 退出"); } @Override protected void onResume() { - LogUtils.d(TAG, "onResume: enter"); + LogUtils.d(TAG, "onResume: 进入"); super.onResume(); - // 恢复时加载背景(适配小米手机后台切前台场景) + // 1. 恢复背景(适配后台切前台场景,小米手机保活优化) if (sGlobalHandler != null) { sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); + LogUtils.d(TAG, "onResume: 发送加载背景消息"); } - // 恢复广告(非核心,容错处理) + // 2. 恢复广告(非核心,容错处理) if (mADsBannerView != null) { mADsBannerView.resumeADs(this); + LogUtils.d(TAG, "onResume: 恢复广告展示"); } - LogUtils.d(TAG, "onResume: exit"); + LogUtils.d(TAG, "onResume: 退出"); } @Override protected void onDestroy() { - LogUtils.d(TAG, "onDestroy: enter"); + LogUtils.d(TAG, "onDestroy: 进入"); super.onDestroy(); - // 释放广告资源(避免内存泄漏,适配API29+内存管控) + // 1. 释放广告资源(避免内存泄漏,适配API30资源管控) if (mADsBannerView != null) { mADsBannerView.releaseAdResources(); mADsBannerView = null; + LogUtils.d(TAG, "onDestroy: 释放广告资源完成"); } - // 置空全局实例(杜绝内存泄漏) + // 2. 释放核心视图资源(调用封装类释放方法,彻底断开引用) + if (mMainContentView != null) { + mMainContentView.releaseResources(); + mMainContentView = null; + LogUtils.d(TAG, "onDestroy: 释放核心视图资源完成"); + } + // 3. 置空全局实例(杜绝内存泄漏,必须执行) sMainActivity = null; - // 移除Handler所有消息(避免内存泄漏,适配组件生命周期) + // 4. 清理Handler(移除所有消息,避免Activity销毁后回调) if (sGlobalHandler != null) { sGlobalHandler.removeCallbacksAndMessages(null); sGlobalHandler = null; + LogUtils.d(TAG, "onDestroy: 清理全局Handler完成"); } - // 释放Drawable资源(API30 图片资源泄漏防护) - releaseDrawableResources(); - // 置空工具类与视图实例(辅助GC回收) + // 5. 释放Drawable资源(断开回调,辅助GC回收) + if (mFrameDrawable != null) { + mFrameDrawable.setCallback(null); + mFrameDrawable = null; + LogUtils.d(TAG, "onDestroy: 释放Drawable资源完成"); + } + // 6. 置空所有辅助实例(辅助GC,避免残留引用) mPermissionUtils = null; mAppConfigUtils = null; mBgSourceUtils = null; - mViewHolder = null; mMenu = null; - mFrameDrawable = null; mApplication = null; mToolbar = null; mAdsViewStub = null; - LogUtils.d(TAG, "onDestroy: exit"); + LogUtils.d(TAG, "onDestroy: 退出"); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - LogUtils.d(TAG, "onActivityResult: enter, requestCode=" + requestCode + ", resultCode=" + resultCode); + 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: send load background message"); + LogUtils.d(TAG, "onActivityResult: 背景设置完成,发送加载背景消息"); } } - LogUtils.d(TAG, "onActivityResult: exit"); + LogUtils.d(TAG, "onActivityResult: 退出"); } - // ======================== 菜单与导航相关方法(统一归类,逻辑清晰)======================== + // ======================== 菜单与导航方法(统一归类,用户交互入口)======================== @Override public boolean onCreateOptionsMenu(Menu menu) { - LogUtils.d(TAG, "onCreateOptionsMenu: enter"); + 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: exit"); + LogUtils.d(TAG, "onCreateOptionsMenu: 退出"); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - LogUtils.d(TAG, "onOptionsItemSelected: enter, itemId=" + item.getItemId()); - // 主题切换处理 + LogUtils.d(TAG, "onOptionsItemSelected: 进入,itemId=" + item.getItemId()); + // 1. 主题切换处理(切换后重建Activity生效) if (AESThemeUtil.onAppThemeItemSelected(this, item)) { recreate(); - LogUtils.d(TAG, "onOptionsItemSelected: theme changed, recreate activity"); + LogUtils.d(TAG, "onOptionsItemSelected: 主题切换,重建Activity"); return true; } - // 开发模式菜单处理 + // 2. 开发模式菜单处理 if (DevelopUtils.onDevelopItemSelected(this, item)) { - LogUtils.d(TAG, "onOptionsItemSelected: develop menu selected"); + LogUtils.d(TAG, "onOptionsItemSelected: 开发菜单操作完成"); return true; } - // 主菜单逻辑分发 + // 3. 主菜单逻辑分发(按功能分类,清晰易维护) switch (item.getItemId()) { case R.id.action_settings: startActivity(new Intent(this, SettingsActivity.class)); @@ -260,26 +246,26 @@ public class MainActivity extends WinBoLLActivity { default: return super.onOptionsItemSelected(item); } - LogUtils.d(TAG, "onOptionsItemSelected: exit"); + LogUtils.d(TAG, "onOptionsItemSelected: 退出"); return true; } @Override public void setupToolbar() { super.setupToolbar(); - // 隐藏返回按钮(主页面无返回需求) + // 主页面隐藏返回按钮(符合用户操作习惯) if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(false); - LogUtils.d(TAG, "setupToolbar: hide back button"); + LogUtils.d(TAG, "setupToolbar: 隐藏Toolbar返回按钮"); } } - // ======================== 页面交互相关方法(统一归类,用户操作响应)======================== + // ======================== 页面交互方法(用户操作响应,统一归类)======================== @Override public void onBackPressed() { - // 退到后台,不销毁Activity(提升用户体验,适配小米手机后台保活) + // 退到后台,不销毁Activity(提升用户体验,小米手机后台保活优化) moveTaskToBack(true); - LogUtils.d(TAG, "onBackPressed: move task to background"); + LogUtils.d(TAG, "onBackPressed: 应用退到后台"); } @Override @@ -288,460 +274,237 @@ public class MainActivity extends WinBoLLActivity { return super.dispatchKeyEvent(event); } - // ======================== 核心初始化方法(按优先级排列,核心优先)======================== + // ======================== 核心初始化方法(按优先级排序,核心优先加载)======================== /** - * 初始化权限工具类(提前初始化,避免权限请求延迟) + * 初始化权限工具类(提前初始化,保障权限请求无延迟) */ private void initPermissionUtils() { - LogUtils.d(TAG, "initPermissionUtils: enter"); + LogUtils.d(TAG, "initPermissionUtils: 进入"); mPermissionUtils = PermissionUtils.getInstance(); - LogUtils.d(TAG, "initPermissionUtils: exit"); + LogUtils.d(TAG, "initPermissionUtils: 退出"); } /** - * 初始化全局Handler(统一消息分发,避免多Handler混乱,Java7 显式实现) + * 初始化全局Handler(统一消息分发,Java7显式实现,避免内存泄漏) */ private void initGlobalHandler() { - LogUtils.d(TAG, "initGlobalHandler: enter"); + LogUtils.d(TAG, "initGlobalHandler: 进入"); if (sGlobalHandler == null) { sGlobalHandler = new Handler() { @Override public void handleMessage(Message msg) { - LogUtils.d(TAG, "handleMessage: msg.what=" + msg.what); + LogUtils.d(TAG, "handleMessage: 接收消息,what=" + msg.what); + // 消息处理前先校验Activity状态(避免销毁后操作UI) + if (sMainActivity == null || sMainActivity.isFinishing()) { + LogUtils.w(TAG, "handleMessage: Activity已销毁,跳过消息处理"); + return; + } + // 按消息类型分发逻辑 switch (msg.what) { case MSG_RELOAD_APPCONFIG: - // 重新加载配置,更新视图(判空避免Activity销毁后操作) - if (sMainActivity != null && sMainActivity.mViewHolder != null && !sMainActivity.isFinishing()) { - // 兼容Java7:isDestroyed() 是API17+,但避免在Handler中调用,仅用isFinishing()防护 - sMainActivity.setViewData(); - } + sMainActivity.updateViewData(); break; case MSG_CURRENTVALUEBATTERY: - // 更新当前电量显示(判空防护) - if (sMainActivity != null && sMainActivity.mViewHolder != null && !sMainActivity.isFinishing()) { - sMainActivity.setCurrentBatteryValue(msg.arg1); - } + sMainActivity.mMainContentView.updateCurrentBattery(msg.arg1); break; case MSG_LOAD_BACKGROUND: - // 加载背景+设置主布局颜色(判空防护) - if (sMainActivity != null && !sMainActivity.isFinishing()) { - sMainActivity.reloadBackground(); - sMainActivity.setMainLayoutBackgroundColor(); - } + sMainActivity.reloadBackground(); + sMainActivity.setMainLayoutBackgroundColor(); break; case MSG_RESTART_REMIND_THREAD: - // 重启RemindThread线程(接收配置Bean参数,判空防护) - if (sMainActivity != null && sMainActivity.mAppConfigUtils != null && !sMainActivity.isFinishing()) { - AppConfigBean configBean = (AppConfigBean) msg.obj; - sMainActivity.restartRemindThread(configBean); - } + AppConfigBean configBean = (AppConfigBean) msg.obj; + sMainActivity.restartRemindThread(configBean); break; } super.handleMessage(msg); } }; + LogUtils.d(TAG, "initGlobalHandler: 全局Handler初始化完成"); } - LogUtils.d(TAG, "initGlobalHandler: exit"); + LogUtils.d(TAG, "initGlobalHandler: 退出"); } /** - * 初始化ViewHolder(缓存视图,减少findViewById调用,提升性能,Java7 显式强转) + * 初始化核心视图封装类(统一管理视图,替代原ViewHolder模式) */ - private void initViewHolder() { - LogUtils.d(TAG, "initViewHolder: enter"); - mViewHolder = new ViewHolder(); - // 绑定核心视图(按布局层级绑定,便于排查绑定失败,Java7 显式强转) - mViewHolder.mainLayout = (RelativeLayout) findViewById(R.id.activitymainRelativeLayout1); - mViewHolder.backgroundView = (BackgroundView) findViewById(R.id.fragmentmainviewBackgroundView1); - mViewHolder.llLeftSeekBar = (LinearLayout) findViewById(R.id.fragmentmainviewLinearLayout1); - mViewHolder.llRightSeekBar = (LinearLayout) findViewById(R.id.fragmentmainviewLinearLayout2); - mViewHolder.cbEnableChargeReminder = (CheckBox) findViewById(R.id.fragmentmainviewCheckBox1); - mViewHolder.cbEnableUsageReminder = (CheckBox) findViewById(R.id.fragmentmainviewCheckBox2); - mViewHolder.swEnableService = (Switch) findViewById(R.id.fragmentandroidviewSwitch1); - mViewHolder.tvTips = (TextView) findViewById(R.id.fragmentandroidviewTextView1); - mViewHolder.tvChargeReminderValue = (TextView) findViewById(R.id.fragmentandroidviewTextView2); - mViewHolder.tvUsageReminderValue = (TextView) findViewById(R.id.fragmentandroidviewTextView3); - mViewHolder.tvCurrentBatteryValue = (TextView) findViewById(R.id.fragmentandroidviewTextView4); - mViewHolder.sbChargeReminder = (VerticalSeekBar) findViewById(R.id.fragmentandroidviewVerticalSeekBar1); - mViewHolder.sbUsageReminder = (VerticalSeekBar) findViewById(R.id.fragmentandroidviewVerticalSeekBar2); - mViewHolder.ivCurrentBattery = (ImageView) findViewById(R.id.fragmentandroidviewImageView1); - mViewHolder.ivChargeReminderBattery = (ImageView) findViewById(R.id.fragmentandroidviewImageView3); - mViewHolder.ivUsageReminderBattery = (ImageView) findViewById(R.id.fragmentandroidviewImageView2); - // 非核心视图(广告) - mAdsViewStub = (ViewStub) findViewById(R.id.stub_ads_banner); - mToolbar = (Toolbar) findViewById(R.id.toolbar); - // 绑定失败日志(便于调试布局ID错误) - if (mViewHolder.mainLayout == null) LogUtils.e(TAG, "initViewHolder: mainLayout bind failed"); - if (mViewHolder.backgroundView == null) LogUtils.e(TAG, "initViewHolder: backgroundView bind failed"); - LogUtils.d(TAG, "initViewHolder: exit"); + private void initMainContentView() { + LogUtils.d(TAG, "initMainContentView: 进入"); + View rootView = findViewById(android.R.id.content); + mMainContentView = new MainContentView(this, rootView, this); + LogUtils.d(TAG, "initMainContentView: 核心视图封装类初始化完成"); + LogUtils.d(TAG, "initMainContentView: 退出"); } /** - * 初始化首屏核心视图(优先级最高,保障首屏加载速度) + * 初始化首屏核心视图(Toolbar等,保障首屏加载速度,优先渲染) */ private void initCriticalView() { - LogUtils.d(TAG, "initCriticalView: enter"); - sMainActivity = this; + LogUtils.d(TAG, "initCriticalView: 进入"); + sMainActivity = this; // 赋值全局实例(用于Handler回调) // 初始化Toolbar + mToolbar = findViewById(R.id.toolbar); setSupportActionBar(mToolbar); if (mToolbar != null) { mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText); - LogUtils.d(TAG, "initCriticalView: toolbar style set success"); + LogUtils.d(TAG, "initCriticalView: Toolbar样式设置完成"); } - LogUtils.d(TAG, "initCriticalView: exit"); + // 初始化广告ViewStub(仅初始化Stub,不立即加载广告) + mAdsViewStub = findViewById(R.id.stub_ads_banner); + LogUtils.d(TAG, "initCriticalView: 退出"); } /** - * 异步初始化核心工具类(避免主线程阻塞,提升首屏加载速度,Java7 显式Runnable) + * 异步初始化核心工具类(子线程执行,避免主线程阻塞,提升首屏加载速度) */ private void initCoreUtilsAsync() { - LogUtils.d(TAG, "initCoreUtilsAsync: enter"); + LogUtils.d(TAG, "initCoreUtilsAsync: 进入"); new Thread(new Runnable() { @Override public void run() { - LogUtils.d(TAG, "initCoreUtilsAsync: async thread start"); - // 初始化工具类(子线程执行,不阻塞主线程) + LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程启动"); + // 子线程初始化工具类(无UI操作,安全执行) mApplication = (App) getApplication(); - // 修正1:App.getAppConfigUtils() 入参改为 Context,避免静态方法调用异常 mAppConfigUtils = AppConfigUtils.getInstance(getApplicationContext()); mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity()); - // 主线程更新UI(工具类初始化完成后,Java7 显式runOnUiThread) + LogUtils.d(TAG, "initCoreUtilsAsync: 核心工具类初始化完成"); + + // 切换主线程更新UI(Java7显式runOnUiThread) runOnUiThread(new Runnable() { @Override public void run() { - LogUtils.d(TAG, "initCoreUtilsAsync: update UI on main thread"); - // 加载框架资源(判空Activity状态,避免销毁后操作) - if (!isFinishing()) { - // 修正2:API30 适配 getDrawable(),添加主题参数,兼容Java7 - 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); - } + LogUtils.d(TAG, "initCoreUtilsAsync: 切换主线程更新UI"); + if (isFinishing()) { + LogUtils.w(TAG, "initCoreUtilsAsync: Activity已销毁,跳过UI更新"); + return; } - // 更新视图数据+设置监听+检查服务状态+加载背景 - setViewData(); - setViewListeners(); - checkServiceAsync(); - if (sGlobalHandler != null) { - sGlobalHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND); + // 适配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); // 加载背景 } }); - LogUtils.d(TAG, "initCoreUtilsAsync: async thread exit"); + LogUtils.d(TAG, "initCoreUtilsAsync: 异步线程结束"); } }).start(); - LogUtils.d(TAG, "initCoreUtilsAsync: exit"); + LogUtils.d(TAG, "initCoreUtilsAsync: 退出"); } /** - * 延迟加载非核心视图(广告,避免影响首屏加载速度,适配小米手机性能管控,Java7 显式Runnable) + * 延迟加载非核心视图(广告,延迟500ms加载,不影响首屏体验) */ private void loadNonCriticalViewDelayed() { - LogUtils.d(TAG, "loadNonCriticalViewDelayed: enter"); + LogUtils.d(TAG, "loadNonCriticalViewDelayed: 进入"); new Handler().postDelayed(new Runnable() { @Override public void run() { - // 避免Activity销毁后执行(双重判空,API30 安全防护) if (isFinishing()) { - LogUtils.w(TAG, "loadNonCriticalViewDelayed: activity is finishing/destroyed, skip"); + LogUtils.w(TAG, "loadNonCriticalViewDelayed: Activity已销毁,跳过广告加载"); return; } - loadAdsView(); + loadAdsView(); // 加载广告视图 } }, DELAY_LOAD_NON_CRITICAL); - LogUtils.d(TAG, "loadNonCriticalViewDelayed: exit"); + LogUtils.d(TAG, "loadNonCriticalViewDelayed: 退出"); } - // ======================== 视图与业务逻辑方法(按功能归类,清晰易维护)======================== + // ======================== 视图操作方法(UI更新,统一归类)======================== /** - * 加载广告视图(非核心,延迟加载,容错处理) + * 加载广告视图(非核心,容错处理,避免广告加载失败影响主功能) */ private void loadAdsView() { - LogUtils.d(TAG, "loadAdsView: enter"); + LogUtils.d(TAG, "loadAdsView: 进入"); if (mAdsViewStub == null) { - LogUtils.e(TAG, "loadAdsView: adsViewStub is null, load failed"); + LogUtils.e(TAG, "loadAdsView: 广告ViewStub为空,加载失败"); return; } if (mADsBannerView == null) { View adsView = mAdsViewStub.inflate(); - mADsBannerView = (ADsBannerView) adsView.findViewById(R.id.adsbanner); - LogUtils.d(TAG, "loadAdsView: ads view inflate success"); + mADsBannerView = adsView.findViewById(R.id.adsbanner); + LogUtils.d(TAG, "loadAdsView: 广告视图加载完成"); } - LogUtils.d(TAG, "loadAdsView: exit"); + LogUtils.d(TAG, "loadAdsView: 退出"); } /** - * 初始化电池图标Drawable(复用实例,减少内存分配,适配小米手机内存敏感特性) + * 更新视图数据(调用核心视图封装类方法,统一更新UI) */ - private void initBatteryDrawables() { - LogUtils.d(TAG, "initBatteryDrawables: enter"); - if (mCurrentBatteryDrawable == null) { - // 修正3:API30 适配 getColor(),添加主题参数,兼容Java7 - int colorCurrent = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - getResources().getColor(R.color.colorCurrent, getTheme()) : - getResources().getColor(R.color.colorCurrent); - mCurrentBatteryDrawable = new BatteryDrawable(colorCurrent); - } - if (mChargeReminderBatteryDrawable == null) { - int colorCharge = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - getResources().getColor(R.color.colorCharge, getTheme()) : - getResources().getColor(R.color.colorCharge); - mChargeReminderBatteryDrawable = new BatteryDrawable(colorCharge); - } - if (mUsageReminderBatteryDrawable == null) { - int colorUsege = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - getResources().getColor(R.color.colorUsege, getTheme()) : - getResources().getColor(R.color.colorUsege); - mUsageReminderBatteryDrawable = new BatteryDrawable(colorUsege); - } - LogUtils.d(TAG, "initBatteryDrawables: exit"); - } - - /** - * 释放Drawable资源(API30 图片资源泄漏防护,避免OOM) - */ - private void releaseDrawableResources() { - LogUtils.d(TAG, "releaseDrawableResources: enter"); - if (mCurrentBatteryDrawable != null) { - mCurrentBatteryDrawable = null; - } - if (mChargeReminderBatteryDrawable != null) { - mChargeReminderBatteryDrawable = null; - } - if (mUsageReminderBatteryDrawable != null) { - mUsageReminderBatteryDrawable = null; - } - if (mFrameDrawable != null) { - mFrameDrawable.setCallback(null); - mFrameDrawable = null; - } - LogUtils.d(TAG, "releaseDrawableResources: exit"); - } - - /** - * 设置视图数据(从配置读取数据,更新UI,容错处理,Java7 显式判空) - */ - private void setViewData() { - LogUtils.d(TAG, "setViewData: enter"); - if (mViewHolder == null || mAppConfigUtils == null) { - LogUtils.e(TAG, "setViewData: viewHolder/appConfigUtils is null, skip"); + private void updateViewData() { + LogUtils.d(TAG, "updateViewData: 进入"); + if (mMainContentView == null || mFrameDrawable == null) { + LogUtils.e(TAG, "updateViewData: 核心视图/框架资源为空,跳过更新"); return; } - // 读取配置数据 - int chargeReminderValue = mAppConfigUtils.getChargeReminderValue(); - int usageReminderValue = mAppConfigUtils.getUsageReminderValue(); - int currentBatteryValue = mAppConfigUtils.getCurrentBatteryValue(); - boolean isChargeReminderEnable = mAppConfigUtils.isChargeReminderEnabled(); - boolean isUsageReminderEnable = mAppConfigUtils.isUsageReminderEnabled(); - boolean isServiceEnable = mAppConfigUtils.isServiceEnabled(); - LogUtils.d(TAG, "setViewData: charge=" + chargeReminderValue + ", usage=" + usageReminderValue + ", current=" + currentBatteryValue); - - // 设置进度条布局背景(判空防护) - if (mFrameDrawable != null) { - if (mViewHolder.llLeftSeekBar != null) mViewHolder.llLeftSeekBar.setBackground(mFrameDrawable); - if (mViewHolder.llRightSeekBar != null) mViewHolder.llRightSeekBar.setBackground(mFrameDrawable); - } - - // 初始化电池图标并设置数据 - initBatteryDrawables(); - // 当前电量(判空防护,避免空指针) - if (mViewHolder.ivCurrentBattery != null) { - mCurrentBatteryDrawable.setValue(currentBatteryValue); - mViewHolder.ivCurrentBattery.setImageDrawable(mCurrentBatteryDrawable); - } - if (mViewHolder.tvCurrentBatteryValue != null) { - // 修正4:API30 适配 getColor(),添加主题参数 - int colorCurrent = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - getResources().getColor(R.color.colorCurrent, getTheme()) : - getResources().getColor(R.color.colorCurrent); - mViewHolder.tvCurrentBatteryValue.setTextColor(colorCurrent); - mViewHolder.tvCurrentBatteryValue.setText(String.valueOf(currentBatteryValue) + "%"); - } - // 充电提醒(判空防护) - if (mViewHolder.ivChargeReminderBattery != null) { - mChargeReminderBatteryDrawable.setValue(chargeReminderValue); - mViewHolder.ivChargeReminderBattery.setImageDrawable(mChargeReminderBatteryDrawable); - } - if (mViewHolder.tvChargeReminderValue != null) { - int colorCharge = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - getResources().getColor(R.color.colorCharge, getTheme()) : - getResources().getColor(R.color.colorCharge); - mViewHolder.tvChargeReminderValue.setTextColor(colorCharge); - mViewHolder.tvChargeReminderValue.setText(String.valueOf(chargeReminderValue) + "%"); - } - if (mViewHolder.sbChargeReminder != null) { - mViewHolder.sbChargeReminder.setProgress(chargeReminderValue); - } - if (mViewHolder.cbEnableChargeReminder != null) { - mViewHolder.cbEnableChargeReminder.setChecked(isChargeReminderEnable); - } - // 耗电提醒(判空防护) - if (mViewHolder.ivUsageReminderBattery != null) { - mUsageReminderBatteryDrawable.setValue(usageReminderValue); - mViewHolder.ivUsageReminderBattery.setImageDrawable(mUsageReminderBatteryDrawable); - } - if (mViewHolder.tvUsageReminderValue != null) { - int colorUsege = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - getResources().getColor(R.color.colorUsege, getTheme()) : - getResources().getColor(R.color.colorUsege); - mViewHolder.tvUsageReminderValue.setTextColor(colorUsege); - mViewHolder.tvUsageReminderValue.setText(String.valueOf(usageReminderValue) + "%"); - } - if (mViewHolder.sbUsageReminder != null) { - mViewHolder.sbUsageReminder.setProgress(usageReminderValue); - } - if (mViewHolder.cbEnableUsageReminder != null) { - mViewHolder.cbEnableUsageReminder.setChecked(isUsageReminderEnable); - } - // 服务开关(判空防护) - if (mViewHolder.swEnableService != null) { - mViewHolder.swEnableService.setChecked(isServiceEnable); - mViewHolder.swEnableService.setText(getString(R.string.txt_aboveswitch)); - } - // 提示文本(判空防护) - if (mViewHolder.tvTips != null) { - mViewHolder.tvTips.setText(getString(R.string.txt_aboveswitchtips)); - } - LogUtils.d(TAG, "setViewData: exit"); + mMainContentView.updateViewData(mFrameDrawable); + LogUtils.d(TAG, "updateViewData: 视图数据更新完成"); + LogUtils.d(TAG, "updateViewData: 退出"); } /** - * 设置视图监听(统一绑定,避免监听泄漏,逻辑清晰,Java7 显式匿名内部类) - */ - private void setViewListeners() { - LogUtils.d(TAG, "setViewListeners: enter"); - if (mViewHolder == null || mAppConfigUtils == null) { - LogUtils.e(TAG, "setViewListeners: viewHolder/appConfigUtils is null, skip"); - return; - } - // 充电提醒进度条监听 - if (mViewHolder.sbChargeReminder != null) { - mViewHolder.sbChargeReminder.setOnSeekBarChangeListener(new ChargeReminderSeekBarListener()); - } - // 充电提醒开关监听(Java7 显式OnClickListener) - if (mViewHolder.cbEnableChargeReminder != null) { - mViewHolder.cbEnableChargeReminder.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean isChecked = mViewHolder.cbEnableChargeReminder.isChecked(); - mAppConfigUtils.setChargeReminderEnabled(MainActivity.this, isChecked); - // 配置变更后发送重启线程消息 - sendRestartRemindThreadMessage(); - LogUtils.d(TAG, "cbEnableChargeReminder: checked=" + isChecked + ", send restart thread msg"); - } - }); - } - // 耗电提醒进度条监听 - if (mViewHolder.sbUsageReminder != null) { - mViewHolder.sbUsageReminder.setOnSeekBarChangeListener(new UsageReminderSeekBarListener()); - } - // 耗电提醒开关监听(Java7 显式OnClickListener) - if (mViewHolder.cbEnableUsageReminder != null) { - mViewHolder.cbEnableUsageReminder.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean isChecked = mViewHolder.cbEnableUsageReminder.isChecked(); - mAppConfigUtils.setUsageReminderEnabled(MainActivity.this, isChecked); - // 配置变更后发送重启线程消息 - sendRestartRemindThreadMessage(); - LogUtils.d(TAG, "cbEnableUsageReminder: checked=" + isChecked + ", send restart thread msg"); - } - }); - } - // 服务开关监听(Java7 显式OnClickListener,兼容Switch) - if (mViewHolder.swEnableService != null) { - mViewHolder.swEnableService.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean isChecked = ((Switch) v).isChecked(); - mAppConfigUtils.setServiceEnabled(getActivity(), isChecked); - // 配置变更后发送重启线程消息 - sendRestartRemindThreadMessage(); - LogUtils.d(TAG, "swEnableService: checked=" + isChecked + ", send restart thread msg"); - } - }); - } - LogUtils.d(TAG, "setViewListeners: exit"); - } - - /** - * 更新当前电量显示(实时更新,复用Drawable,提升性能,判空防护) - */ - private void setCurrentBatteryValue(int value) { - LogUtils.d(TAG, "setCurrentBatteryValue: enter, value=" + value); - if (mViewHolder == null || mCurrentBatteryDrawable == null || mViewHolder.tvCurrentBatteryValue == null) { - LogUtils.e(TAG, "setCurrentBatteryValue: viewHolder/drawable is null, skip"); - return; - } - // 校准电量范围(0-100,避免异常值,Java7 显式Math调用) - value = Math.min(Math.max(value, 0), 100); - mViewHolder.tvCurrentBatteryValue.setText(String.valueOf(value) + "%"); - mCurrentBatteryDrawable.setValue(value); - mCurrentBatteryDrawable.invalidateSelf(); - LogUtils.d(TAG, "setCurrentBatteryValue: exit"); - } - - /** - * 重新加载背景(适配背景切换,容错处理,适配小米手机图片加载特性) + * 重新加载背景(适配背景切换,小米手机图片加载优化,容错处理) */ private void reloadBackground() { - LogUtils.d(TAG, "reloadBackground: enter"); - if (mViewHolder == null || mBgSourceUtils == null || mViewHolder.backgroundView == null) { - LogUtils.e(TAG, "reloadBackground: viewHolder/bgSourceUtils/backgroundView is null, skip"); + LogUtils.d(TAG, "reloadBackground: 进入"); + if (mMainContentView == null || mBgSourceUtils == null) { + LogUtils.e(TAG, "reloadBackground: 核心视图/背景工具类为空,跳过加载"); return; } BackgroundBean currentBgBean = mBgSourceUtils.getCurrentBackgroundBean(); if (currentBgBean != null) { - mViewHolder.backgroundView.loadBackgroundBean(currentBgBean); - LogUtils.d(TAG, "reloadBackground: load background bean success"); + mMainContentView.backgroundView.loadBackgroundBean(currentBgBean); + LogUtils.d(TAG, "reloadBackground: 加载自定义背景完成"); } else { - LogUtils.e(TAG, "reloadBackground: current background bean is null, use default"); - mViewHolder.backgroundView.setBackgroundResource(R.drawable.default_background); + LogUtils.e(TAG, "reloadBackground: 无自定义背景,加载默认背景"); + mMainContentView.backgroundView.setBackgroundResource(R.drawable.default_background); } - LogUtils.d(TAG, "reloadBackground: exit"); + LogUtils.d(TAG, "reloadBackground: 退出"); } /** - * 设置主布局背景颜色(适配背景配置,容错处理,API30 安全防护) + * 设置主布局背景颜色(适配背景配置,API30安全防护) */ private void setMainLayoutBackgroundColor() { - LogUtils.d(TAG, "setMainLayoutBackgroundColor: enter"); - if (isFinishing() || mViewHolder == null || mBgSourceUtils == null || mViewHolder.mainLayout == null) { - LogUtils.e(TAG, "setMainLayoutBackgroundColor: activity/viewHolder is invalid, skip"); + LogUtils.d(TAG, "setMainLayoutBackgroundColor: 进入"); + if (isFinishing() || mMainContentView == null || mBgSourceUtils == null) { + LogUtils.e(TAG, "setMainLayoutBackgroundColor: 环境无效,跳过设置"); return; } BackgroundBean currentBgBean = mBgSourceUtils.getCurrentBackgroundBean(); if (currentBgBean != null) { int bgColor = currentBgBean.getPixelColor(); - mViewHolder.mainLayout.setBackgroundColor(bgColor); - LogUtils.d(TAG, "setMainLayoutBackgroundColor: set color success"); + mMainContentView.mainLayout.setBackgroundColor(bgColor); + LogUtils.d(TAG, "setMainLayoutBackgroundColor: 主布局颜色设置完成"); } - LogUtils.d(TAG, "setMainLayoutBackgroundColor: exit"); + LogUtils.d(TAG, "setMainLayoutBackgroundColor: 退出"); } + // ======================== 服务与线程管理方法(业务核心,统一归类)======================== /** - * 异步检查服务状态(避免主线程阻塞,适配API30 前台服务规范,小米保活适配,Java7 显式Runnable) + * 异步检查服务状态(子线程执行,避免主线程阻塞,适配API30前台服务规范) */ private void checkServiceAsync() { - LogUtils.d(TAG, "checkServiceAsync: enter"); + LogUtils.d(TAG, "checkServiceAsync: 进入"); new Thread(new Runnable() { @Override public void run() { - LogUtils.d(TAG, "checkServiceAsync: async thread start"); + LogUtils.d(TAG, "checkServiceAsync: 异步线程启动"); if (mAppConfigUtils == null || isFinishing()) { - LogUtils.e(TAG, "checkServiceAsync: appConfigUtils/activity is invalid, skip"); + LogUtils.e(TAG, "checkServiceAsync: 配置工具类/Activity无效,跳过检查"); return; } - // 服务启用且未运行时,启动前台服务(API30 必须前台服务,适配小米保活) + // 服务启用且未运行时,启动前台服务(API30必须前台服务,小米保活优化) if (mAppConfigUtils.isServiceEnabled() && !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) { - LogUtils.d(TAG, "checkServiceAsync: service not alive, start foreground service"); + LogUtils.d(TAG, "checkServiceAsync: 服务未运行,启动前台服务"); runOnUiThread(new Runnable() { @Override public void run() { if (!isFinishing()) { Intent serviceIntent = new Intent(getActivity(), ControlCenterService.class); - // 修正5:API26+ 前台服务启动,添加版本判断,兼容Java7 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { getActivity().startForegroundService(serviceIntent); } else { @@ -751,77 +514,83 @@ public class MainActivity extends WinBoLLActivity { } }); } - LogUtils.d(TAG, "checkServiceAsync: async thread exit"); + LogUtils.d(TAG, "checkServiceAsync: 异步线程结束"); } }).start(); - LogUtils.d(TAG, "checkServiceAsync: exit"); + LogUtils.d(TAG, "checkServiceAsync: 退出"); } /** - * 启动关于页面(封装逻辑,减少代码冗余,Java7 显式Intent构建) + * 重启RemindThread线程(通过服务统一管理,保障线程安全,适配API30服务规范) + */ + private void restartRemindThread(AppConfigBean configBean) { + LogUtils.d(TAG, "restartRemindThread: 进入"); + if (configBean == null || !ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) { + LogUtils.e(TAG, "restartRemindThread: 配置为空/服务未运行,跳过重启"); + return; + } + // 发送重启指令到服务(通过Intent传递最新配置) + Intent restartIntent = new Intent(this, ControlCenterService.class); + restartIntent.setAction(ControlCenterService.ACTION_RESTART_REMIND_THREAD); + restartIntent.putExtra(ControlCenterService.EXTRA_APP_CONFIG_BEAN, (Serializable) configBean); + // 适配API26+前台服务启动规范 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(restartIntent); + } else { + startService(restartIntent); + } + LogUtils.d(TAG, "restartRemindThread: 发送线程重启指令到服务完成"); + LogUtils.d(TAG, "restartRemindThread: 退出"); + } + + // ======================== 页面跳转方法(封装逻辑,减少冗余)======================== + /** + * 启动关于页面(封装跳转逻辑,统一参数传递,便于维护) */ private void startAboutActivity() { - LogUtils.d(TAG, "startAboutActivity: enter"); + 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: exit"); + LogUtils.d(TAG, "startAboutActivity: 启动关于页面完成"); + LogUtils.d(TAG, "startAboutActivity: 退出"); } + // ======================== 消息发送方法(统一封装,避免冗余,保障消息安全)======================== /** - * 发送重启RemindThread线程消息(统一封装,减少冗余,Java7 显式Message构建) + * 发送重启RemindThread线程消息(携带最新配置,确保线程使用最新参数) */ private void sendRestartRemindThreadMessage() { - LogUtils.d(TAG, "sendRestartRemindThreadMessage: enter"); + LogUtils.d(TAG, "sendRestartRemindThreadMessage: 进入"); if (sGlobalHandler == null || mAppConfigUtils == null || mAppConfigUtils.mAppConfigBean == null) { - LogUtils.e(TAG, "sendRestartRemindThreadMessage: handler/configBean is null, skip"); + LogUtils.e(TAG, "sendRestartRemindThreadMessage: Handler/配置为空,跳过发送"); return; } - // 携带最新配置Bean发送消息,确保线程使用最新配置(复用Message,避免内存抖动) + // 复用Message,避免内存抖动(优化性能) Message msg = sGlobalHandler.obtainMessage(MSG_RESTART_REMIND_THREAD); msg.obj = mAppConfigUtils.mAppConfigBean; sGlobalHandler.sendMessage(msg); - LogUtils.d(TAG, "sendRestartRemindThreadMessage: send msg with configBean"); + LogUtils.d(TAG, "sendRestartRemindThreadMessage: 发送线程重启消息完成"); + LogUtils.d(TAG, "sendRestartRemindThreadMessage: 退出"); } + // ======================== 静态工具方法(全局调用,统一入口)======================== /** - * 重启RemindThread线程(通过ControlCenterService统一管理,保证线程安全) - */ - private void restartRemindThread(AppConfigBean configBean) { - LogUtils.d(TAG, "restartRemindThread: enter"); - if (configBean == null || !ServiceUtils.isServiceAlive(this, ControlCenterService.class.getName())) { - LogUtils.e(TAG, "restartRemindThread: configBean is null or service not alive, skip"); - return; - } - // 修正6:通过Intent传递配置,通知服务重启线程,添加版本判断启动服务 - Intent restartIntent = new Intent(this, ControlCenterService.class); - restartIntent.setAction(ControlCenterService.ACTION_RESTART_REMIND_THREAD); // 复用服务已定义Action - restartIntent.putExtra(ControlCenterService.EXTRA_APP_CONFIG_BEAN, (Serializable)configBean); // 传递最新配置 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(restartIntent); // API26+ 前台服务 - } else { - startService(restartIntent); // 低版本普通服务 - } - LogUtils.d(TAG, "restartRemindThread: send restart command to ControlCenterService via Intent"); - } - - // ======================== 静态工具方法(全局调用,统一入口,Java7 显式实现)======================== - /** - * 重新加载应用配置(全局调用,更新视图) + * 重新加载应用配置(全局调用,触发视图更新) */ public static void reloadAppConfig() { - LogUtils.d(TAG, "reloadAppConfig: send message"); + LogUtils.d(TAG, "reloadAppConfig: 发送重新加载配置消息"); if (sGlobalHandler != null) { sGlobalHandler.sendEmptyMessage(MSG_RELOAD_APPCONFIG); } } /** - * 发送当前电量更新消息(全局调用,实时更新UI,Java7 显式Message构建) + * 发送当前电量更新消息(全局调用,实时更新UI) */ public static void sendCurrentBatteryValueMessage(int value) { - LogUtils.d(TAG, "sendCurrentBatteryValueMessage: value=" + value); + LogUtils.d(TAG, "sendCurrentBatteryValueMessage: 电量=" + value + ",发送更新消息"); if (sGlobalHandler != null) { Message msg = sGlobalHandler.obtainMessage(MSG_CURRENTVALUEBATTERY); msg.arg1 = value; @@ -829,15 +598,17 @@ public class MainActivity extends WinBoLLActivity { } } + // ======================== 辅助工具方法(封装重复逻辑,提升复用性)======================== /** - * 从URI获取图片真实路径(适配API30 媒体存储权限,容错处理,Java7 显式Cursor操作) + * 从URI获取图片真实路径(适配API30媒体存储权限,Java7显式Cursor操作,避免资源泄漏) */ private String getRealPathFromUri(Uri contentUri) { - LogUtils.d(TAG, "getRealPathFromUri: enter"); + LogUtils.d(TAG, "getRealPathFromUri: 进入"); if (contentUri == null) { - LogUtils.e(TAG, "getRealPathFromUri: contentUri is null"); + LogUtils.e(TAG, "getRealPathFromUri: URI为空,获取失败"); return null; } + String realPath = null; String[] projection = {MediaStore.MediaColumns.DATA}; Cursor cursor = getContentResolver().query(contentUri, projection, null, null, null); if (cursor != null) { @@ -845,25 +616,27 @@ public class MainActivity extends WinBoLLActivity { if (cursor.moveToNext()) { int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA); if (columnIndex > -1) { - String realPath = cursor.getString(columnIndex); - LogUtils.d(TAG, "getRealPathFromUri: real path=" + realPath); - return realPath; + realPath = cursor.getString(columnIndex); + LogUtils.d(TAG, "getRealPathFromUri: 获取图片路径=" + realPath); } } } finally { - // 确保Cursor关闭,避免资源泄漏(Java7 必须显式关闭,无try-with-resources) + // Java7必须显式关闭Cursor,避免资源泄漏(无try-with-resources语法) cursor.close(); } } - LogUtils.e(TAG, "getRealPathFromUri: get path failed"); - return null; + if (realPath == null) { + LogUtils.e(TAG, "getRealPathFromUri: 获取图片路径失败"); + } + LogUtils.d(TAG, "getRealPathFromUri: 退出"); + return realPath; } /** - * 生成默认应用信息(关于页面使用,封装逻辑,便于维护,Java7 显式Bean赋值) + * 生成默认应用信息(关于页面使用,封装参数赋值,便于维护) */ private APPInfo genDefaultAppInfo() { - LogUtils.d(TAG, "genDefaultAppInfo: enter"); + LogUtils.d(TAG, "genDefaultAppInfo: 进入"); String branchName = "powerbell"; APPInfo appInfo = new APPInfo(); appInfo.setAppName(getString(R.string.app_name)); @@ -876,75 +649,40 @@ public class MainActivity extends WinBoLLActivity { appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=PowerBell"); appInfo.setAppAPKName("PowerBell"); appInfo.setAppAPKFolderName("PowerBell"); - LogUtils.d(TAG, "genDefaultAppInfo: exit"); + LogUtils.d(TAG, "genDefaultAppInfo: 生成应用信息完成"); + LogUtils.d(TAG, "genDefaultAppInfo: 退出"); return appInfo; } - // ======================== 内部监听类(私有内部类,避免外部依赖,减少内存泄漏,Java7 显式实现)======================== - /** - * 充电提醒进度条监听(单独封装,逻辑聚焦) - */ - private class ChargeReminderSeekBarListener implements SeekBar.OnSeekBarChangeListener { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (mViewHolder == null || mChargeReminderBatteryDrawable == null || mViewHolder.tvChargeReminderValue == null) { - return; - } - // 实时更新文本与图标 - mViewHolder.tvChargeReminderValue.setText(String.valueOf(progress) + "%"); - mChargeReminderBatteryDrawable.setValue(progress); - mChargeReminderBatteryDrawable.invalidateSelf(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - if (mAppConfigUtils == null || mViewHolder == null || mViewHolder.tvChargeReminderValue == null) { - return; - } - // 修正7:VerticalSeekBar 获取进度,直接用 getProgress()(避免调用私有字段 _mnProgress 编译报错) - int progress = seekBar.getProgress(); - mAppConfigUtils.setChargeReminderValue(MainActivity.this, progress); - mViewHolder.tvChargeReminderValue.setText(String.valueOf(progress) + "%"); - // 配置变更后发送重启线程消息 - sendRestartRemindThreadMessage(); - LogUtils.d(TAG, "ChargeReminderSeekBar: save progress=" + progress + ", send restart thread msg"); - } + // ======================== MainContentView 事件回调实现(视图事件触发业务逻辑)======================== + @Override + public void onChargeReminderSwitchChanged(boolean isChecked) { + LogUtils.d(TAG, "onChargeReminderSwitchChanged: 状态=" + isChecked); + sendRestartRemindThreadMessage(); } - /** - * 耗电提醒进度条监听(单独封装,逻辑聚焦) - */ - private class UsageReminderSeekBarListener implements SeekBar.OnSeekBarChangeListener { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (mViewHolder == null || mUsageReminderBatteryDrawable == null || mViewHolder.tvUsageReminderValue == null) { - return; - } - // 实时更新文本与图标 - mViewHolder.tvUsageReminderValue.setText(String.valueOf(progress) + "%"); - mUsageReminderBatteryDrawable.setValue(progress); - mUsageReminderBatteryDrawable.invalidateSelf(); - } + @Override + public void onUsageReminderSwitchChanged(boolean isChecked) { + LogUtils.d(TAG, "onUsageReminderSwitchChanged: 状态=" + isChecked); + sendRestartRemindThreadMessage(); + } - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} + @Override + public void onServiceSwitchChanged(boolean isChecked) { + LogUtils.d(TAG, "onServiceSwitchChanged: 状态=" + isChecked); + sendRestartRemindThreadMessage(); + } - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - if (mAppConfigUtils == null || mViewHolder == null || mViewHolder.tvUsageReminderValue == null) { - return; - } - // 修正8:VerticalSeekBar 获取进度,直接用 getProgress()(避免调用私有字段 _mnProgress 编译报错) - int progress = seekBar.getProgress(); - mAppConfigUtils.setUsageReminderValue(MainActivity.this, progress); - mViewHolder.tvUsageReminderValue.setText(String.valueOf(progress) + "%"); - // 配置变更后发送重启线程消息 - sendRestartRemindThreadMessage(); - LogUtils.d(TAG, "UsageReminderSeekBar: save progress=" + progress + ", send restart thread msg"); - } + @Override + public void onChargeReminderProgressChanged(int progress) { + LogUtils.d(TAG, "onChargeReminderProgressChanged: 进度=" + progress); + sendRestartRemindThreadMessage(); + } + + @Override + public void onUsageReminderProgressChanged(int progress) { + LogUtils.d(TAG, "onUsageReminderProgressChanged: 进度=" + progress); + sendRestartRemindThreadMessage(); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java index dae8dc1..7ccfa27 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/handlers/ControlCenterServiceHandler.java @@ -8,66 +8,83 @@ import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.models.NotificationMessage; /** - * 服务通信Handler:弱引用持有服务,避免内存泄漏,适配Java7 + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 13:41 + * @Describe 服务通信Handler:弱引用持有服务,避免内存泄漏,适配Java7+API30,统一处理服务消息 */ public class ControlCenterServiceHandler extends Handler { - // 消息类型常量(与RemindThread对应) - public static final int MSG_REMIND_TEXT = 1001; // 提醒消息 - private static final String TAG = "ControlCenterServiceHandler"; + // ================================== 静态常量(置顶统一管理,清晰区分消息类型)================================= + public static final String TAG = "ControlCenterServiceHandler"; + public static final int MSG_REMIND_TEXT = 1001; // 电量提醒消息(+充电/-耗电) - // 弱引用持有服务实例(避免内存泄漏) - private WeakReference mwrControlCenterService; + // ================================== 成员变量(弱引用服务,杜绝内存泄漏)================================= + private final WeakReference mwrControlCenterService; - // 构造方法(传入服务实例) + // ================================== 构造方法(强制传入服务,初始化弱引用)================================= public ControlCenterServiceHandler(ControlCenterService service) { + LogUtils.d(TAG, "初始化Handler,绑定服务实例:" + (service != null ? service.getClass().getSimpleName() : "null")); this.mwrControlCenterService = new WeakReference<>(service); } + // ================================== 核心消息处理(重写handleMessage,按类型分发)================================= @Override public void handleMessage(Message msg) { super.handleMessage(msg); + LogUtils.d(TAG, "接收消息,what:" + msg.what + ",obj:" + (msg.obj != null ? msg.obj : "null")); + + // 弱引用获取服务,避免持有强引用导致内存泄漏 ControlCenterService service = mwrControlCenterService.get(); if (service == null) { - LogUtils.e(TAG, "handleMessage: 服务实例为空,处理失败"); + LogUtils.e(TAG, "服务实例已回收,消息处理失败"); return; } - // 处理不同类型消息 + // 按消息类型分发处理,避免逻辑冗余 switch (msg.what) { case MSG_REMIND_TEXT: - // 接收RemindThread的提醒消息,调用通知工具类发送通知 handleRemindMessage(service, (String) msg.obj); break; default: - LogUtils.d(TAG, "handleMessage: 未知消息类型,what=" + msg.what); + LogUtils.w(TAG, "未知消息类型,what:" + msg.what); break; } } - // 处理提醒消息(发送通知) + // ================================== 业务辅助方法(单独处理提醒消息,职责单一)================================= + /** + * 处理电量提醒消息,构建通知模型并调用工具类发送 + * @param service 服务实例(非空,已前置校验) + * @param content 消息内容(+:充电提醒,-:耗电提醒) + */ private void handleRemindMessage(ControlCenterService service, String content) { - LogUtils.d(TAG, "handleRemindMessage: 处理提醒消息,内容=" + content); - // 复用服务的通知工具类和前台通知模型 + LogUtils.d(TAG, "开始处理提醒消息,内容:" + content); + + // 校验通知工具类,避免空指针 if (service.getNotificationManager() == null) { - LogUtils.e(TAG, "handleRemindMessage: 通知工具类为空,发送失败"); + LogUtils.e(TAG, "通知管理工具类为空,无法发送提醒"); return; } - // 构建提醒通知消息(可按需求调整标题/内容) + // 构建通知消息模型,区分充电/耗电场景 NotificationMessage remindMsg = new NotificationMessage(); - if ("+".equals(content)) { // 充电提醒 + if ("+".equals(content)) { remindMsg.setTitle("充电提醒"); - remindMsg.setContent("电池电量已达标,建议及时断电保护电池~"); + remindMsg.setContent("电池电量已达标,建议及时断电,保护电池寿命~"); remindMsg.setRemindMSG("charge_remind"); - } else if ("-".equals(content)) { // 耗电提醒 + LogUtils.d(TAG, "构建充电提醒通知,标识:charge_remind"); + } else if ("-".equals(content)) { remindMsg.setTitle("耗电提醒"); - remindMsg.setContent("电池电量偏低,建议及时充电~"); + remindMsg.setContent("电池电量偏低,建议及时充电,避免设备关机~"); remindMsg.setRemindMSG("usage_remind"); + LogUtils.d(TAG, "构建耗电提醒通知,标识:usage_remind"); + } else { + LogUtils.w(TAG, "无效提醒消息内容,跳过发送:" + content); + return; } - // 调用通知工具类发送提醒通知(复用现有逻辑) + // 调用服务工具类发送通知,复用现有逻辑 service.getNotificationManager().showRemindNotification(service, remindMsg); - LogUtils.d(TAG, "handleRemindMessage: 提醒通知发送完成"); + LogUtils.d(TAG, "提醒通知发送完成,标题:" + remindMsg.getTitle()); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java index 4433620..af78d63 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java @@ -8,365 +8,344 @@ import cc.winboll.studio.powerbell.models.AppConfigBean; import java.lang.ref.WeakReference; /** - * 提醒线程(单例模式):统一管理充电/耗电提醒逻辑,适配Java7+API30+ + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 13:38 + * @Describe 提醒线程(单例模式):统一管理充电/耗电提醒逻辑,适配Java7+API30+,保障线程安全与内存安全 */ public class RemindThread extends Thread { - // 常量定义(优化命名规范,补充注释) + // ================================== 静态常量(置顶,统一管理魔法值)================================= public static final String TAG = "RemindThread"; private static final long INIT_DELAY_TIME = 500L; // 初始化延迟(ms,等待服务就绪) - private static final int MIN_SLEEP_TIME = 500; // 最小检测间隔(ms,避免频繁轮询) + private static final int MIN_SLEEP_TIME = 500; // 最小检测间隔(ms,防高频轮询) private static final int DEFAULT_SLEEP_TIME = 1000; // 默认检测间隔(ms) - private static final long REMIND_INTERVAL = 30000L; // 重复提醒间隔(ms,避免轰炸用户) - private static final int CONFIG_RETRY_MAX = 3; // 配置同步重试次数(兜底配置有效性) + private static final long REMIND_INTERVAL = 30000L; // 重复提醒间隔(ms,防骚扰) + private static final int CONFIG_RETRY_MAX = 3; // 配置同步重试次数(兜底) - // 单例核心(volatile+双重校验锁,线程安全,修复复用漏洞) + // ================================== 单例核心(线程安全,避免复用旧实例)================================= private static volatile RemindThread sInstance; - // 成员变量(volatile+锁保护,确保多线程可见性,优化内存泄漏防护) + // ================================== 成员变量(按功能分类,volatile+锁保护)================================= + // 依赖资源(防内存泄漏) private Context mContext; private WeakReference mwrControlCenterServiceHandler; - private volatile boolean isExist; // 线程退出标记(true=触发退出) - private volatile boolean isReminding; // 全局提醒功能开关 - private volatile boolean isThreadStarted; // 线程启动状态标记(区分isAlive()) - private volatile boolean isEnableUsageReminder; // 耗电提醒单独开关 - private volatile boolean isEnableChargeReminder; // 充电提醒单独开关 - private volatile int sleepTime; // 电量检测间隔(ms) + + // 线程状态(核心标记,多线程可见) + private volatile boolean isExist; // 退出标记(true=退出) + private volatile boolean isThreadStarted; // 启动标记(区分isAlive()) + private volatile boolean isReminding; // 全局提醒开关 + private volatile boolean isRemindTimerRunning; // 提醒恢复计时器状态 + + // 业务配置(实时同步,范围校验) + private volatile boolean isEnableChargeReminder; // 充电提醒开关 + private volatile boolean isEnableUsageReminder; // 耗电提醒开关 + private volatile int sleepTime; // 检测间隔(ms) private volatile int chargeReminderValue; // 充电提醒阈值(0-100) private volatile int usageReminderValue; // 耗电提醒阈值(0-100) private volatile int quantityOfElectricity; // 当前电量(0-100) - private volatile boolean isCharging; // 是否处于充电状态 - private volatile boolean isRemindTimerRunning; // 提醒恢复计时器运行标记 - private volatile long lastRemindTime; // 上次提醒时间戳(ms) - private final Object mLock = new Object(); // 全局锁(保护所有状态变量,解决并发安全) + private volatile boolean isCharging; // 充电状态标记 - // 私有构造器(单例模式,强化上下文弱引用+状态重置) + // 辅助变量(并发安全+防重复提醒) + private volatile long lastRemindTime; // 上次提醒时间戳(ms) + private final Object mLock = new Object(); // 全局锁(保护所有状态变量) + + // ================================== 私有构造器(单例专用,初始化状态+防护泄漏)================================= private RemindThread(Context context, ControlCenterServiceHandler handler) { - LogUtils.d(TAG, "RemindThread: 构造方法初始化,线程名称=" + getName()); - // 应用上下文+弱引用Handler,双重防护内存泄漏 + LogUtils.d(TAG, "构造线程实例,线程名称:" + getName()); this.mContext = context.getApplicationContext(); this.mwrControlCenterServiceHandler = new WeakReference<>(handler); - resetThreadStateInternal(); // 初始化所有状态变量,避免脏数据 + resetThreadStateInternal(); // 初始化所有状态,避免脏数据 } - // 单例获取(核心修复:线程停止后自动销毁单例,杜绝复用旧线程对象) + // ================================== 单例管理(获取+销毁,彻底释放资源)================================= + /** + * 获取单例实例(旧线程停止自动销毁,强制入参校验) + */ public static RemindThread getInstance(Context context, ControlCenterServiceHandler handler) { - LogUtils.d(TAG, "getInstance: 获取线程实例,当前单例状态=" + (sInstance != null)); - // 入参校验(非空抛出异常,强制上游传参正确) + LogUtils.d(TAG, "获取单例,当前实例是否存在:" + (sInstance != null)); if (context == null || handler == null) { - LogUtils.e(TAG, "getInstance: 致命错误 - Context/Handler不能为空"); + LogUtils.e(TAG, "致命错误:Context/ControlCenterServiceHandler不能为空"); throw new IllegalArgumentException("Context and ControlCenterServiceHandler cannot be null"); } - // 关键修复:旧线程已停止(未运行),直接销毁单例,确保新实例创建 + // 旧线程停止则销毁,确保新实例有效 if (sInstance != null && !sInstance.isRunning()) { destroyInstance(); - LogUtils.d(TAG, "getInstance: 旧线程已停止,销毁单例准备创建新实例"); + LogUtils.d(TAG, "旧线程已停止,销毁单例准备创建新实例"); } - // 双重校验锁,线程安全创建单例 + // 双重校验锁创建单例 if (sInstance == null) { synchronized (RemindThread.class) { if (sInstance == null) { sInstance = new RemindThread(context, handler); - LogUtils.d(TAG, "getInstance: 新线程实例创建成功,线程ID=" + sInstance.getId()); + LogUtils.d(TAG, "新线程实例创建成功,线程ID:" + sInstance.getId()); } } } return sInstance; } - // 单例销毁(核心优化:彻底释放资源+终止线程,与Service停止逻辑联动) + /** + * 销毁单例(安全停止线程+释放资源,避免内存泄漏) + */ public static void destroyInstance() { - LogUtils.d(TAG, "destroyInstance: 开始销毁线程单例"); - synchronized (RemindThread.class) { // 类锁保护,避免并发销毁冲突 + LogUtils.d(TAG, "开始销毁线程单例"); + synchronized (RemindThread.class) { if (sInstance != null) { - // 1. 安全停止线程(触发退出+中断休眠) sInstance.stopThread(); - // 2. 强制释放所有资源(避免内存泄漏) sInstance.releaseResources(); - // 3. 置空单例,杜绝后续复用 sInstance = null; - LogUtils.d(TAG, "destroyInstance: 线程单例销毁完成"); + LogUtils.d(TAG, "线程单例销毁完成"); } else { - LogUtils.w(TAG, "destroyInstance: 单例已为空,跳过销毁"); + LogUtils.w(TAG, "单例已为空,跳过销毁"); } } } - // 线程核心逻辑(彻底修复重复启动、优化退出效率、强化异常容错) + // ================================== 线程核心逻辑(run方法,高效退出+异常容错)================================= @Override public void run() { - LogUtils.d(TAG, "run: 线程启动执行,线程ID=" + Thread.currentThread().getId() + ",当前状态=" + getState()); - // 核心修复1:杜绝重复执行run()(Thread.start()后会自动调用run(),手动调用/重复start()直接拦截) + LogUtils.d(TAG, "线程启动,ID:" + Thread.currentThread().getId() + ",状态:" + getState()); + // 防止run方法重复执行 synchronized (mLock) { if (isThreadStarted || isExist) { - LogUtils.e(TAG, "run: 线程已启动/待退出,禁止重复执行run()"); + LogUtils.e(TAG, "线程已启动/待退出,禁止重复执行run"); return; } - isThreadStarted = true; // 标记线程启动成功(仅执行一次) - lastRemindTime = 0; // 初始化上次提醒时间 + isThreadStarted = true; + lastRemindTime = 0; } - // 步骤1:配置同步重试(等待Service同步配置,避免阈值无效) + // 配置同步重试(失败则兜底默认值) boolean configValid = syncConfigRetry(); if (!configValid) { - LogUtils.e(TAG, "run: 配置同步失败,使用默认配置兜底启动"); + LogUtils.e(TAG, "配置同步失败,使用默认阈值兜底启动"); } - // 步骤2:初始化延迟(等待服务/系统资源就绪,避免启动初期异常) + // 初始化延迟(等待服务就绪) try { - LogUtils.d(TAG, "run: 初始化延迟" + INIT_DELAY_TIME + "ms"); + LogUtils.d(TAG, "初始化延迟" + INIT_DELAY_TIME + "ms"); Thread.sleep(INIT_DELAY_TIME); - // 延迟期间检测退出标记,避免线程启动后立即销毁 if (isExist) { - LogUtils.d(TAG, "run: 初始化延迟期间收到退出指令,线程终止"); + LogUtils.d(TAG, "延迟期间收到退出指令,线程终止"); cleanThreadState(); return; } } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // 保留中断标记 - LogUtils.e(TAG, "run: 初始化延迟被中断,线程终止"); + Thread.currentThread().interrupt(); + LogUtils.e(TAG, "初始化延迟被中断,线程终止", e); cleanThreadState(); return; } - // 步骤3:核心业务循环(isExist=true时立即退出,优化轮询效率) - LogUtils.d(TAG, "run: 进入电量检测核心循环,检测间隔=" + sleepTime + "ms"); + // 核心检测循环(快速退出+并发安全) + LogUtils.d(TAG, "进入电量检测循环,间隔:" + sleepTime + "ms"); while (!isExist) { try { - // 快速退出判断(循环头部+尾部双重校验,提升退出效率) if (isExist) break; - // 状态1:提醒功能关闭 → 仅休眠,不执行检测逻辑 + // 提醒关闭/电量无效,直接休眠 if (!isReminding) { safeSleep(sleepTime); continue; } - - // 状态2:电量数据无效 → 休眠重试(避免无效计算) if (quantityOfElectricity < 0 || quantityOfElectricity > 100) { - LogUtils.w(TAG, "run: 电量数据无效(" + quantityOfElectricity + "),休眠重试"); + LogUtils.w(TAG, "电量无效(" + quantityOfElectricity + "),休眠重试"); safeSleep(sleepTime); continue; } - // 状态3:正常检测 → 锁保护核心逻辑,解决并发安全(计时器+提醒判断) + // 锁保护核心提醒逻辑 synchronized (mLock) { - // 快速退出(锁内再次校验,避免持有锁期间收到退出指令) if (isExist) break; - // 防重复提醒:间隔未到/计时器运行中 → 跳过 + // 防重复提醒(间隔未到/计时器运行中) long currentTime = System.currentTimeMillis(); if ((currentTime - lastRemindTime < REMIND_INTERVAL) || isRemindTimerRunning) { safeSleep(sleepTime); continue; } - // 充电状态提醒(开关+阈值双重校验) - if (isCharging && isEnableChargeReminder) { - if (quantityOfElectricity >= chargeReminderValue) { - sendNotificationMessage("+"); // 充电提醒标识 - lastRemindTime = currentTime; - LogUtils.d(TAG, "run: 触发充电提醒 → 电量:" + quantityOfElectricity + ",阈值:" + chargeReminderValue); - startRemindRecoveryTimer(); // 启动恢复计时器,避免重复提醒 - } + // 充电提醒触发 + if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) { + LogUtils.d(TAG, "触发充电提醒:电量" + quantityOfElectricity + "≥阈值" + chargeReminderValue); + sendNotificationMessage("+"); + lastRemindTime = currentTime; + startRemindRecoveryTimer(); } - // 耗电状态提醒(同理,开关+阈值双重校验) - else if (!isCharging && isEnableUsageReminder) { - if (quantityOfElectricity <= usageReminderValue) { - sendNotificationMessage("-"); // 耗电提醒标识 - lastRemindTime = currentTime; - LogUtils.d(TAG, "run: 触发耗电提醒 → 电量:" + quantityOfElectricity + ",阈值:" + usageReminderValue); - startRemindRecoveryTimer(); // 启动恢复计时器 - } + // 耗电提醒触发 + else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { + LogUtils.d(TAG, "触发耗电提醒:电量" + quantityOfElectricity + "≤阈值" + usageReminderValue); + sendNotificationMessage("-"); + lastRemindTime = currentTime; + startRemindRecoveryTimer(); } } - // 检测间隔休眠(循环尾部休眠,避免空轮询) safeSleep(sleepTime); } catch (Exception e) { - LogUtils.e(TAG, "run: 线程运行异常,休眠后重试", e); - safeSleep(sleepTime); // 异常后强制休眠,避免死循环占用CPU + LogUtils.e(TAG, "线程运行异常,休眠重试", e); + safeSleep(sleepTime); } } - // 步骤4:线程退出清理(无论正常/异常退出,必执行) + // 退出清理 cleanThreadState(); - LogUtils.d(TAG, "run: 线程核心循环退出,线程ID=" + Thread.currentThread().getId() + ",最终状态=" + getState()); + LogUtils.d(TAG, "线程循环退出,ID:" + Thread.currentThread().getId()); } - // 新增:配置同步重试(独立封装,提升代码可读性,修复阈值校验逻辑) + // ================================== 核心业务辅助方法(配置同步+消息发送+计时器)================================= + /** + * 配置同步重试(确保阈值有效,失败兜底) + */ private boolean syncConfigRetry() { int retryCount = 0; while (retryCount < CONFIG_RETRY_MAX) { synchronized (mLock) { - // 配置有效(阈值0-100)→ 重试成功 if ((chargeReminderValue >= 0 && chargeReminderValue <= 100) - && (usageReminderValue >= 0 && usageReminderValue <= 100)) { - LogUtils.d(TAG, "syncConfigRetry: 配置同步成功,重试次数=" + retryCount); + && (usageReminderValue >= 0 && usageReminderValue <= 100)) { + LogUtils.d(TAG, "配置同步成功,重试次数:" + retryCount); return true; } } - // 配置无效 → 休眠1秒重试 - LogUtils.w(TAG, "syncConfigRetry: 配置未同步(阈值无效),重试次数=" + (retryCount + 1)); + LogUtils.w(TAG, "配置未同步,重试次数:" + (retryCount + 1)); try { Thread.sleep(1000); retryCount++; } catch (InterruptedException e) { Thread.currentThread().interrupt(); - LogUtils.e(TAG, "syncConfigRetry: 配置重试被中断", e); + LogUtils.e(TAG, "配置重试被中断", e); return false; } } - // 重试失败 → 用默认阈值兜底(确保线程能正常启动) + // 兜底默认阈值 synchronized (mLock) { - chargeReminderValue = Math.min(Math.max(chargeReminderValue, 0), 100); - if (chargeReminderValue < 0 || chargeReminderValue > 100) { - chargeReminderValue = 80; - } - usageReminderValue = Math.min(Math.max(usageReminderValue, 0), 100); - if (usageReminderValue < 0 || usageReminderValue > 100) { - usageReminderValue = 20; - } - LogUtils.e(TAG, "syncConfigRetry: 配置重试失败,使用默认阈值 → 充电:" + chargeReminderValue + ",耗电:" + usageReminderValue); + chargeReminderValue = (chargeReminderValue < 0 || chargeReminderValue > 100) ? 80 : chargeReminderValue; + usageReminderValue = (usageReminderValue < 0 || usageReminderValue > 100) ? 20 : usageReminderValue; + LogUtils.e(TAG, "配置重试失败,兜底阈值:充电" + chargeReminderValue + ",耗电" + usageReminderValue); } return false; } - // 发送提醒消息(核心修复:Message回收写法+优化容错,适配全Android版本) + /** + * 发送提醒消息(弱引用Handler+Message复用,防泄漏) + */ private void sendNotificationMessage(String content) { - LogUtils.d(TAG, "sendNotificationMessage: 准备发送提醒消息,内容=" + content); - // 前置校验:线程退出/提醒关闭 → 跳过 + LogUtils.d(TAG, "准备发送提醒消息:" + content); if (isExist || !isReminding) { - LogUtils.d(TAG, "sendNotificationMessage: 线程退出/提醒关闭,跳过发送"); + LogUtils.d(TAG, "线程退出/提醒关闭,跳过发送"); return; } - // 弱引用获取Handler(避免持有Service引用导致内存泄漏) - ControlCenterServiceHandler handler = null; - if (mwrControlCenterServiceHandler != null) { - handler = mwrControlCenterServiceHandler.get(); - } + ControlCenterServiceHandler handler = mwrControlCenterServiceHandler != null ? mwrControlCenterServiceHandler.get() : null; if (handler == null) { - LogUtils.w(TAG, "sendNotificationMessage: Handler已回收,发送失败"); + LogUtils.w(TAG, "Handler已回收,发送失败"); return; } - // 复用Message(减少内存分配),发送提醒指令 Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT, content); try { handler.sendMessage(message); - LogUtils.d(TAG, "sendNotificationMessage: 提醒消息发送成功"); + LogUtils.d(TAG, "提醒消息发送成功"); } catch (Exception e) { - LogUtils.e(TAG, "sendNotificationMessage: 消息发送异常", e); - // 核心修复:Message回收改为实例方法(兼容所有Android版本),且仅在发送失败时回收 - if (message != null) { - message.recycle(); // 正确写法:实例方法回收,避免内存泄漏 - } + LogUtils.e(TAG, "消息发送异常", e); + if (message != null) message.recycle(); } } - // 提醒恢复计时器(核心优化:弱引用线程实例+双重锁保护,彻底解决并发安全) + /** + * 启动提醒恢复计时器(防重复提醒,弱引用线程+锁保护) + */ private void startRemindRecoveryTimer() { - LogUtils.d(TAG, "startRemindRecoveryTimer: 启动提醒恢复计时器,间隔=" + REMIND_INTERVAL + "ms"); + LogUtils.d(TAG, "启动提醒恢复计时器,间隔:" + REMIND_INTERVAL + "ms"); synchronized (mLock) { - // 前置校验:线程退出/计时器已运行/单例已销毁 → 跳过 if (isExist || isRemindTimerRunning || sInstance != this) { - LogUtils.w(TAG, "startRemindRecoveryTimer: 计时器启动条件不满足,跳过"); + LogUtils.w(TAG, "计时器启动条件不满足,跳过"); return; } - // 临时关闭提醒(避免恢复前重复触发),标记计时器运行中 isReminding = false; isRemindTimerRunning = true; } - // 计时器线程:弱引用当前RemindThread实例(避免计时器持有线程导致内存泄漏) - final WeakReference threadWeakRef = new WeakReference<>(this); + final WeakReference threadRef = new WeakReference<>(this); new Thread(new Runnable() { @Override public void run() { try { - // 计时器休眠(间隔时间到后恢复提醒) Thread.sleep(REMIND_INTERVAL); - - // 弱引用获取线程实例(避免内存泄漏) - RemindThread thread = threadWeakRef.get(); + RemindThread thread = threadRef.get(); if (thread == null || thread.isExist || sInstance != thread) { - LogUtils.d(TAG, "startRemindRecoveryTimer: 线程已销毁/退出,不恢复提醒"); + LogUtils.d(TAG, "线程已销毁,不恢复提醒"); return; } - // 锁保护恢复逻辑(确保状态一致性) synchronized (thread.mLock) { - // 再次校验:线程未退出+单例未切换+有任一提醒开关开启 → 恢复提醒 if (!thread.isExist && sInstance == thread - && (thread.isEnableChargeReminder || thread.isEnableUsageReminder)) { + && (thread.isEnableChargeReminder || thread.isEnableUsageReminder)) { thread.isReminding = true; - LogUtils.d(TAG, "startRemindRecoveryTimer: 提醒功能恢复开启"); + LogUtils.d(TAG, "提醒功能恢复开启"); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); - LogUtils.e(TAG, "startRemindRecoveryTimer: 计时器被中断", e); + LogUtils.e(TAG, "计时器被中断", e); } finally { - // 无论是否异常,都重置计时器标记(锁保护,避免并发冲突) - RemindThread thread = threadWeakRef.get(); + RemindThread thread = threadRef.get(); if (thread != null) { synchronized (thread.mLock) { - if (sInstance == thread) { - thread.isRemindTimerRunning = false; - } + if (sInstance == thread) thread.isRemindTimerRunning = false; } } - LogUtils.d(TAG, "startRemindRecoveryTimer: 提醒恢复计时器执行完成"); + LogUtils.d(TAG, "提醒恢复计时器执行完成"); } } - }, "RemindRecoveryTimer").start(); // 给计时器线程命名,便于调试 + }, "RemindRecoveryTimer").start(); } - // 安全停止线程(核心修复:触发退出+中断休眠+重置状态,确保快速退出) + // ================================== 线程控制方法(停止+休眠+状态清理)================================= + /** + * 安全停止线程(快速退出+中断休眠) + */ public void stopThread() { - LogUtils.d(TAG, "stopThread: 开始安全停止线程,当前线程状态=" + getState()); + LogUtils.d(TAG, "开始停止线程,当前状态:" + getState()); synchronized (mLock) { - // 标记退出+关闭所有开关+停止计时器(原子操作,避免状态混乱) isExist = true; isReminding = false; isRemindTimerRunning = false; - LogUtils.d(TAG, "stopThread: 线程退出标记已置位,所有开关已关闭"); } - - // 中断线程(唤醒sleep/wait状态,加速线程退出核心循环,避免阻塞) if (isAlive()) { interrupt(); - LogUtils.d(TAG, "stopThread: 线程已中断,加速退出"); - } - - // 兜底:单例置空(避免Service销毁后单例残留) - if (sInstance == this) { - sInstance = null; + LogUtils.d(TAG, "线程已中断,加速退出"); } + if (sInstance == this) sInstance = null; } - // 废弃原强制停止方法(统一用stopThread(),避免方法冗余导致调用混淆) + /** + * 废弃方法(统一用stopThread) + */ @Deprecated public void forceStopThread() { - LogUtils.w(TAG, "forceStopThread: 该方法已废弃,请调用stopThread()"); + LogUtils.w(TAG, "forceStopThread已废弃,请调用stopThread"); stopThread(); } - // 安全休眠(修复:保留中断标记,避免线程退出逻辑失效) + /** + * 安全休眠(保留中断标记,确保退出逻辑生效) + */ private void safeSleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // 关键:保留中断标记,确保线程能快速退出 - LogUtils.w(TAG, "safeSleep: 休眠被中断,线程准备退出"); + Thread.currentThread().interrupt(); + LogUtils.w(TAG, "休眠被中断,线程准备退出"); } } - // 清理线程运行状态(原子操作,确保退出后状态干净,无残留) + /** + * 清理线程运行状态(退出后重置所有标记) + */ private void cleanThreadState() { - LogUtils.d(TAG, "cleanThreadState: 清理线程运行状态"); + LogUtils.d(TAG, "清理线程运行状态"); synchronized (mLock) { isReminding = false; isExist = true; @@ -374,74 +353,76 @@ public class RemindThread extends Thread { isRemindTimerRunning = false; lastRemindTime = 0; } - // 辅助:中断线程(兜底加速退出) - if (isAlive()) { - interrupt(); - } - // 辅助:单例置空(终极兜底) - if (sInstance == this) { - sInstance = null; - } + if (isAlive()) interrupt(); + if (sInstance == this) sInstance = null; } - // 重置线程初始状态(构造方法专用,初始化所有变量,避免脏数据) + // ================================== 状态重置方法(初始化+运行时重置)================================= + /** + * 初始状态重置(构造器专用) + */ private void resetThreadStateInternal() { - LogUtils.d(TAG, "resetThreadStateInternal: 重置线程初始状态"); + LogUtils.d(TAG, "重置线程初始状态"); synchronized (mLock) { + // 线程状态 isExist = false; isReminding = false; isThreadStarted = false; - isEnableUsageReminder = false; + isRemindTimerRunning = false; + lastRemindTime = 0; + // 业务配置 isEnableChargeReminder = false; + isEnableUsageReminder = false; sleepTime = DEFAULT_SLEEP_TIME; chargeReminderValue = -1; usageReminderValue = -1; quantityOfElectricity = -1; isCharging = false; - isRemindTimerRunning = false; - lastRemindTime = 0; } } - // 外部调用:重置线程运行状态(不重置配置,仅重置运行时标记) + /** + * 运行时状态重置(外部调用,不重置配置) + */ public void resetThreadState() { - LogUtils.d(TAG, "resetThreadState: 重置线程运行状态"); + LogUtils.d(TAG, "重置线程运行状态"); synchronized (mLock) { isExist = false; - isReminding = (isEnableChargeReminder || isEnableUsageReminder); // 按开关自动恢复提醒状态 isRemindTimerRunning = false; lastRemindTime = 0; + isReminding = (isEnableChargeReminder || isEnableUsageReminder); } + LogUtils.d(TAG, "运行状态重置完成,全局提醒:" + isReminding); } - // 核心:同步完整配置(优化参数校验+原子操作,确保配置一致性) + // ================================== 配置同步方法(完整同步配置,原子操作)================================= + /** + * 同步应用配置(参数校验+范围限制,立即生效) + */ public void setAppConfigBean(AppConfigBean config) { if (config == null) { - LogUtils.e(TAG, "setAppConfigBean: 配置Bean为空,同步失败"); + LogUtils.e(TAG, "配置Bean为空,同步失败"); return; } - LogUtils.d(TAG, "setAppConfigBean: 开始同步配置 → 充电阈值:" + config.getChargeReminderValue() + ",耗电阈值:" + config.getUsageReminderValue()); + LogUtils.d(TAG, "开始同步配置:充电阈值" + config.getChargeReminderValue() + ",耗电阈值" + config.getUsageReminderValue()); synchronized (mLock) { - // 1. 同步提醒开关(单独开关+全局开关联动) - this.isEnableChargeReminder = config.isEnableChargeReminder(); - this.isEnableUsageReminder = config.isEnableUsageReminder(); - this.isReminding = (this.isEnableChargeReminder || this.isEnableUsageReminder); - - // 2. 同步阈值(强制0-100范围,避免无效值) - this.chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), 0), 100); - this.usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), 0), 100); - - // 3. 同步检测间隔(强制≥最小间隔,避免频繁轮询耗电) - this.sleepTime = Math.max(config.getBatteryDetectInterval(), MIN_SLEEP_TIME); - - // 4. 同步实时电池状态(配置中携带最新状态,立即生效) - this.quantityOfElectricity = Math.min(Math.max(config.getCurrentBatteryValue(), 0), 100); - this.isCharging = config.isCharging(); + // 同步开关 + isEnableChargeReminder = config.isEnableChargeReminder(); + isEnableUsageReminder = config.isEnableUsageReminder(); + isReminding = (isEnableChargeReminder || isEnableUsageReminder); + // 同步阈值(0-100限制) + chargeReminderValue = Math.min(Math.max(config.getChargeReminderValue(), 0), 100); + usageReminderValue = Math.min(Math.max(config.getUsageReminderValue(), 0), 100); + // 同步检测间隔(≥最小间隔) + sleepTime = Math.max(config.getBatteryDetectInterval(), MIN_SLEEP_TIME); + // 同步电池状态 + quantityOfElectricity = Math.min(Math.max(config.getCurrentBatteryValue(), 0), 100); + isCharging = config.isCharging(); } - LogUtils.d(TAG, "setAppConfigBean: 配置同步完成 → 检测间隔:" + sleepTime + "ms,提醒开启:" + isReminding); + LogUtils.d(TAG, "配置同步完成:检测间隔" + sleepTime + "ms,全局提醒" + isReminding); } - // ====================== Setter/Getter 方法(全量锁保护,确保线程安全,优化参数校验) ====================== + // ================================== Setter/Getter(锁保护,线程安全+精准日志)================================= public boolean isExist() { synchronized (mLock) { return isExist; @@ -451,7 +432,7 @@ public class RemindThread extends Thread { public void setIsExist(boolean isExist) { synchronized (mLock) { this.isExist = isExist; - LogUtils.d(TAG, "setIsExist: 线程退出标记设置为" + isExist); + LogUtils.d(TAG, "设置线程退出标记:" + isExist); } } @@ -464,7 +445,7 @@ public class RemindThread extends Thread { public void setIsReminding(boolean isReminding) { synchronized (mLock) { this.isReminding = isReminding; - LogUtils.d(TAG, "setIsReminding: 全局提醒开关设置为" + isReminding); + LogUtils.d(TAG, "设置全局提醒开关:" + isReminding); } } @@ -483,8 +464,8 @@ public class RemindThread extends Thread { public void setIsEnableUsageReminder(boolean isEnableUsageReminder) { synchronized (mLock) { this.isEnableUsageReminder = isEnableUsageReminder; - this.isReminding = (this.isEnableChargeReminder || this.isEnableUsageReminder); // 联动全局开关 - LogUtils.d(TAG, "setIsEnableUsageReminder: 耗电提醒开关设置为" + isEnableUsageReminder + ",全局提醒:" + this.isReminding); + isReminding = (isEnableChargeReminder || isEnableUsageReminder); + LogUtils.d(TAG, "设置耗电提醒开关:" + isEnableUsageReminder + ",全局提醒:" + isReminding); } } @@ -497,8 +478,8 @@ public class RemindThread extends Thread { public void setIsEnableChargeReminder(boolean isEnableChargeReminder) { synchronized (mLock) { this.isEnableChargeReminder = isEnableChargeReminder; - this.isReminding = (this.isEnableChargeReminder || this.isEnableUsageReminder); // 联动全局开关 - LogUtils.d(TAG, "setIsEnableChargeReminder: 充电提醒开关设置为" + isEnableChargeReminder + ",全局提醒:" + this.isReminding); + isReminding = (isEnableChargeReminder || isEnableUsageReminder); + LogUtils.d(TAG, "设置充电提醒开关:" + isEnableChargeReminder + ",全局提醒:" + isReminding); } } @@ -510,8 +491,8 @@ public class RemindThread extends Thread { public void setSleepTime(int sleepTime) { synchronized (mLock) { - this.sleepTime = Math.max(sleepTime, MIN_SLEEP_TIME); // 强制≥最小间隔 - LogUtils.d(TAG, "setSleepTime: 检测间隔设置为" + this.sleepTime + "ms"); + this.sleepTime = Math.max(sleepTime, MIN_SLEEP_TIME); + LogUtils.d(TAG, "设置检测间隔:" + this.sleepTime + "ms"); } } @@ -523,8 +504,8 @@ public class RemindThread extends Thread { public void setChargeReminderValue(int chargeReminderValue) { synchronized (mLock) { - this.chargeReminderValue = Math.min(Math.max(chargeReminderValue, 0), 100); // 强制0-100 - LogUtils.d(TAG, "setChargeReminderValue: 充电提醒阈值设置为" + this.chargeReminderValue); + this.chargeReminderValue = Math.min(Math.max(chargeReminderValue, 0), 100); + LogUtils.d(TAG, "设置充电提醒阈值:" + this.chargeReminderValue); } } @@ -536,8 +517,8 @@ public class RemindThread extends Thread { public void setUsageReminderValue(int usageReminderValue) { synchronized (mLock) { - this.usageReminderValue = Math.min(Math.max(usageReminderValue, 0), 100); // 强制0-100 - LogUtils.d(TAG, "setUsageReminderValue: 耗电提醒阈值设置为" + this.usageReminderValue); + this.usageReminderValue = Math.min(Math.max(usageReminderValue, 0), 100); + LogUtils.d(TAG, "设置耗电提醒阈值:" + this.usageReminderValue); } } @@ -549,8 +530,8 @@ public class RemindThread extends Thread { public void setQuantityOfElectricity(int quantityOfElectricity) { synchronized (mLock) { - this.quantityOfElectricity = Math.min(Math.max(quantityOfElectricity, 0), 100); // 强制0-100 - LogUtils.d(TAG, "setQuantityOfElectricity: 当前电量更新为" + this.quantityOfElectricity); + this.quantityOfElectricity = Math.min(Math.max(quantityOfElectricity, 0), 100); + LogUtils.d(TAG, "更新当前电量:" + this.quantityOfElectricity); } } @@ -563,36 +544,34 @@ public class RemindThread extends Thread { public void setIsCharging(boolean isCharging) { synchronized (mLock) { this.isCharging = isCharging; - LogUtils.d(TAG, "setIsCharging: 充电状态更新为" + isCharging); + LogUtils.d(TAG, "更新充电状态:" + isCharging); } } - // 核心:判断线程是否正在运行(Service依赖此方法,精准判断运行状态) + // ================================== 核心状态判断(Service依赖,精准校验)================================= public boolean isRunning() { synchronized (mLock) { - // 条件:未退出 + 已启动 + 线程存活 → 视为正在运行 - return !isExist && isThreadStarted && isAlive(); + boolean running = !isExist && isThreadStarted && isAlive(); + LogUtils.d(TAG, "线程运行状态:" + running + "(退出:" + isExist + ",已启动:" + isThreadStarted + ",存活:" + isAlive() + ")"); + return running; } } - // 彻底释放线程资源(Service销毁时调用,避免内存泄漏) + // ================================== 资源释放(彻底断开引用,防内存泄漏)================================= public void releaseResources() { - LogUtils.d(TAG, "releaseResources: 开始释放线程资源"); + LogUtils.d(TAG, "开始释放线程资源"); synchronized (mLock) { - // 1. 释放上下文(避免持有应用上下文导致内存泄漏) mContext = null; - // 2. 清空Handler弱引用(加速回收) if (mwrControlCenterServiceHandler != null) { mwrControlCenterServiceHandler.clear(); mwrControlCenterServiceHandler = null; } - // 3. 清理运行状态(确保所有标记干净) cleanThreadState(); } - LogUtils.d(TAG, "releaseResources: 线程资源释放完成"); + LogUtils.d(TAG, "线程资源释放完成"); } - // 重写toString(),便于调试(新增:打印线程核心状态) + // ================================== 调试辅助(toString,打印核心状态)================================= @Override public String toString() { return "RemindThread{" + @@ -604,6 +583,7 @@ public class RemindThread extends Thread { ", usageThreshold=" + getUsageReminderValue() + ", currentBattery=" + getQuantityOfElectricity() + ", isCharging=" + isCharging() + + ", sleepTime=" + getSleepTime() + "ms" + '}'; } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java index 2979580..32668fa 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java @@ -7,6 +7,10 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.Build; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.MainActivity; @@ -14,342 +18,393 @@ import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.models.NotificationMessage; /** - * 通知工具类:统一管理前台服务通知、提醒通知,适配API19-34,强化容错与兼容性 + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 13:42 + * @Describe 通知工具类:统一管理前台服务通知、电池提醒通知,适配API19-30,强化兼容性与容错性(取消所有振动) */ public class NotificationManagerUtils { - private static final String TAG = "NotificationManagerUtils"; - // 通知常量(渠道ID、通知ID,避免魔法值,渠道名/描述优化用户感知) + // ================================== 静态常量(置顶统一管理,杜绝魔法值)================================= + public static final String TAG = "NotificationManagerUtils"; + // 通知渠道ID(区分前台服务/提醒通知,适配API26+) public static final String CHANNEL_ID_FOREGROUND = "cc.winboll.studio.powerbell.channel.foreground"; public static final String CHANNEL_ID_REMIND = "cc.winboll.studio.powerbell.channel.remind"; + // 通知ID(唯一标识,避免重复/混淆) public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001; public static final int NOTIFY_ID_REMIND = 1002; - - // 成员变量(封装属性,提升安全性) - private Notification mForegroundServiceNotify; - private NotificationManager mNotificationManager; - private Context mContext; - // 低版本通知兼容配置(API<21 必须,避免通知图标显示异常) + // 低版本兼容配置(API<21 通知图标默认值,避免显示异常) private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher; - // 构造方法(强化判空,避免初始化失败+内存泄漏) + // ================================== 成员变量(按功能分类,封装保护,避免外部篡改)================================= + // 核心依赖资源 + private Context mContext; + private NotificationManager mNotificationManager; + // 前台服务通知实例(单独持有,便于更新/取消) + private Notification mForegroundServiceNotify; + + // ================================== 构造方法(初始化核心资源,前置校验防崩溃)================================= public NotificationManagerUtils(Context context) { - LogUtils.d(TAG, "NotificationManagerUtils: 初始化通知工具类"); + LogUtils.d(TAG, "初始化通知工具类"); + // 前置校验:Context非空,避免后续空指针 if (context == null) { - LogUtils.e(TAG, "NotificationManagerUtils: Context is null,初始化失败"); + LogUtils.e(TAG, "初始化失败:Context为空"); return; } + // 持有应用上下文,杜绝内存泄漏 this.mContext = context.getApplicationContext(); + // 获取系统通知服务,初始化核心依赖 this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + // 初始化通知渠道(API26+ 必需) initNotificationChannels(); - LogUtils.d(TAG, "NotificationManagerUtils: 工具类初始化完成"); + LogUtils.d(TAG, "通知工具类初始化完成"); } - // 通知渠道初始化(适配API26+,保持稳定) + // ================================== 核心初始化(通知渠道,适配API26+,取消振动)================================= + /** + * 初始化通知渠道:前台服务渠道(低打扰)、电池提醒渠道(正常感知),均关闭振动 + */ private void initNotificationChannels() { - LogUtils.d(TAG, "initNotificationChannels: 初始化通知渠道"); + LogUtils.d(TAG, "开始初始化通知渠道"); + // API<26 无渠道机制,直接跳过;通知服务为空也不执行 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || mNotificationManager == null) { - LogUtils.d(TAG, "initNotificationChannels: API<26 或 NotificationManager为空,跳过渠道初始化"); + LogUtils.d(TAG, "无需初始化渠道:API<26 或 NotificationManager为空"); return; } - // 前台服务渠道(低优先级,无打扰) + // 1. 前台服务渠道(低优先级,后台运行无打扰,关闭振动) NotificationChannel foregroundChannel = new NotificationChannel( CHANNEL_ID_FOREGROUND, "电池服务保活", NotificationManager.IMPORTANCE_LOW ); - foregroundChannel.setDescription("电池提醒服务后台稳定运行,无弹窗、无震动、无声音"); + foregroundChannel.setDescription("电池监测服务后台稳定运行,无弹窗、无震动、无声音"); foregroundChannel.enableLights(false); - foregroundChannel.enableVibration(false); + foregroundChannel.enableVibration(false); // 关闭振动 foregroundChannel.setSound(null, null); foregroundChannel.setShowBadge(false); foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); - // 电池提醒渠道(中优先级,确保感知) + // 2. 电池提醒渠道(中优先级,确保用户感知,关闭振动) NotificationChannel remindChannel = new NotificationChannel( CHANNEL_ID_REMIND, "电池状态提醒", NotificationManager.IMPORTANCE_DEFAULT ); - remindChannel.setDescription("电池充电满/低电量提醒,及时保护电池健康"); + remindChannel.setDescription("电池满电/低电量提醒,及时保护电池健康(无振动)"); remindChannel.enableLights(true); - remindChannel.enableVibration(true); - remindChannel.setVibrationPattern(new long[]{100, 200, 100}); + remindChannel.enableVibration(false); // 关闭振动(删除振动模式配置) remindChannel.setSound(null, null); remindChannel.setShowBadge(false); remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + // 注册渠道到系统 mNotificationManager.createNotificationChannel(foregroundChannel); mNotificationManager.createNotificationChannel(remindChannel); - LogUtils.d(TAG, "initNotificationChannels: 渠道初始化完成"); + LogUtils.d(TAG, "通知渠道初始化完成(前台+提醒各1个,均关闭振动)"); } - // ====================== 核心修复:适配低API,移除 FOREGROUND_SERVICE_TYPE ====================== - // 启动前台服务通知(全版本兼容,API19-34通用) + // ================================== 对外核心方法(前台服务通知:启动+更新+取消)================================= + /** + * 启动前台服务通知(适配API19-30,无FOREGROUND_SERVICE_TYPE,全版本通用) + */ public void startForegroundServiceNotify(Service service, NotificationMessage message) { - LogUtils.d(TAG, "startForegroundServiceNotify: 启动前台服务通知"); + LogUtils.d(TAG, "启动前台服务通知,通知ID:" + NOTIFY_ID_FOREGROUND_SERVICE); + // 前置校验:依赖参数非空,避免崩溃 if (service == null || message == null || mNotificationManager == null) { - LogUtils.e(TAG, "startForegroundServiceNotify: 依赖参数为空,启动失败"); + LogUtils.e(TAG, "启动失败:Service/消息/NotificationManager为空"); return; } + // 构建前台通知实例 mForegroundServiceNotify = buildForegroundNotification(message); if (mForegroundServiceNotify == null) { - LogUtils.e(TAG, "startForegroundServiceNotify: 通知构建失败,启动失败"); + LogUtils.e(TAG, "启动失败:前台通知构建失败"); return; } - // 修复:移除 API30+ 专属的 FOREGROUND_SERVICE_TYPE,用全版本通用写法 + // 启动前台服务(全版本通用写法,适配API30无类型限制) try { - // 所有API版本统一调用:startForeground(通知ID, 通知实例) service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); - LogUtils.d(TAG, "startForegroundServiceNotify: 启动成功,通知ID=" + NOTIFY_ID_FOREGROUND_SERVICE); + LogUtils.d(TAG, "前台服务通知启动成功"); } catch (Exception e) { - LogUtils.e(TAG, "startForegroundServiceNotify: 启动异常(可能是权限或5秒内未调用)", e); + LogUtils.e(TAG, "启动异常(可能是5秒内未调用/权限缺失)", e); } } - // 发送电池提醒通知(保持稳定) - public void showRemindNotification(Context context, NotificationMessage message) { - LogUtils.d(TAG, "showRemindNotification: 发送提醒通知,标题=" + (message.getTitle() != null ? message.getTitle() : "默认提醒")); - if (context == null || message == null || mNotificationManager == null) { - LogUtils.e(TAG, "showRemindNotification: 依赖参数为空,发送失败"); + /** + * 更新前台服务通知内容(复用通知ID,实时刷新显示) + */ + public void updateForegroundServiceNotify(NotificationMessage message) { + LogUtils.d(TAG, "更新前台服务通知,通知ID:" + NOTIFY_ID_FOREGROUND_SERVICE); + if (message == null || mNotificationManager == null) { + LogUtils.e(TAG, "更新失败:消息/NotificationManager为空"); return; } + // 重新构建通知,覆盖旧实例 + mForegroundServiceNotify = buildForegroundNotification(message); + if (mForegroundServiceNotify == null) { + LogUtils.e(TAG, "更新失败:前台通知构建失败"); + return; + } + + // 刷新通知显示 + try { + mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); + LogUtils.d(TAG, "前台服务通知更新成功"); + } catch (Exception e) { + LogUtils.e(TAG, "更新异常", e); + } + } + + /** + * 取消前台服务通知(销毁Service时调用,避免通知残留) + */ + public void cancelForegroundServiceNotify() { + LogUtils.d(TAG, "取消前台服务通知,通知ID:" + NOTIFY_ID_FOREGROUND_SERVICE); + // 先取消系统通知 + cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE); + // 置空实例,加速GC回收 + mForegroundServiceNotify = null; + LogUtils.d(TAG, "前台服务通知取消完成,实例已置空"); + } + + // ================================== 对外核心方法(电池提醒通知:发送)================================= + /** + * 发送电池提醒通知(满电/低电量,适配全版本提醒效果,无振动) + */ + public void showRemindNotification(Context context, NotificationMessage message) { + LogUtils.d(TAG, "发送电池提醒通知,标题:" + (message.getTitle() != null ? message.getTitle() : "默认提醒")); + // 前置校验:依赖参数非空 + if (context == null || message == null || mNotificationManager == null) { + LogUtils.e(TAG, "发送失败:Context/消息/NotificationManager为空"); + return; + } + + // 构建提醒通知实例 Notification remindNotify = buildRemindNotification(context, message); if (remindNotify == null) { - LogUtils.e(TAG, "showRemindNotification: 通知构建失败,发送失败"); + LogUtils.e(TAG, "发送失败:提醒通知构建失败"); + return; + } + + // 发送通知到系统 + try { + mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify); + LogUtils.d(TAG, "电池提醒通知发送成功,通知ID:" + NOTIFY_ID_REMIND); + } catch (Exception e) { + LogUtils.e(TAG, "发送异常", e); + } + } + + // ================================== 对外核心方法(通知取消:单个+全部)================================= + /** + * 取消指定ID的通知 + */ + public void cancelNotification(int notifyId) { + LogUtils.d(TAG, "取消指定通知,通知ID:" + notifyId); + if (mNotificationManager == null) { + LogUtils.e(TAG, "取消失败:NotificationManager为空"); return; } try { - mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify); - LogUtils.d(TAG, "showRemindNotification: 发送成功,通知ID=" + NOTIFY_ID_REMIND); + mNotificationManager.cancel(notifyId); + LogUtils.d(TAG, "通知取消成功"); } catch (Exception e) { - LogUtils.e(TAG, "showRemindNotification: 发送异常", e); + LogUtils.e(TAG, "取消异常", e); } } - // 构建前台服务通知(低版本API<21 兼容配置) + /** + * 取消所有通知(兜底场景使用) + */ + public void cancelAllNotifications() { + LogUtils.d(TAG, "取消所有通知"); + if (mNotificationManager == null) { + LogUtils.e(TAG, "取消失败:NotificationManager为空"); + return; + } + + try { + mNotificationManager.cancelAll(); + LogUtils.d(TAG, "所有通知取消成功"); + } catch (Exception e) { + LogUtils.e(TAG, "取消异常", e); + } + } + + // ================================== 内部辅助方法(通知构建:前台服务通知)================================= + /** + * 构建前台服务通知(API分级适配,低版本无打扰,高版本渠道管控,无振动) + */ private Notification buildForegroundNotification(NotificationMessage message) { if (message == null || mContext == null) { - LogUtils.e(TAG, "buildForegroundNotification: 参数为空,构建失败"); + LogUtils.e(TAG, "前台通知构建失败:参数/Context为空"); return null; } - String notifyTitle = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中"; - String notifyContent = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台持续监测电池状态,保护电池健康"; + // 内容兜底:避免消息标题/内容为空 + String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中"; + String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台持续监测电池状态,保护电池健康"; + Notification.Builder builder; // API分级构建,确保全版本兼容 - Notification notification; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND) + // API26+:绑定前台服务渠道,低优先级无打扰(渠道已关闭振动) + builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND) .setSmallIcon(NOTIFICATION_DEFAULT_ICON) - .setContentTitle(notifyTitle) - .setContentText(notifyContent) + .setContentTitle(title) + .setContentText(content) .setAutoCancel(false) - .setOngoing(true) + .setOngoing(true) // 前台服务通知不可手动关闭 .setWhen(System.currentTimeMillis()) - .setTicker(notifyTitle); - builder.setContentIntent(createJumpPendingIntent(mContext, 0)); - notification = builder.build(); + .setTicker(title); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Notification.Builder builder = new Notification.Builder(mContext) + // API21-25:添加大图标+主题色,手动关闭声音震动 + builder = new Notification.Builder(mContext) .setSmallIcon(NOTIFICATION_DEFAULT_ICON) .setLargeIcon(getAppIcon(mContext)) .setColor(mContext.getResources().getColor(R.color.colorPrimary)) - .setContentTitle(notifyTitle) - .setContentText(notifyContent) + .setContentTitle(title) + .setContentText(content) .setAutoCancel(false) .setOngoing(true) .setWhen(System.currentTimeMillis()) - .setTicker(notifyTitle) - .setPriority(Notification.PRIORITY_LOW); - builder.setContentIntent(createJumpPendingIntent(mContext, 0)); - notification = builder.build(); + .setTicker(title) + .setPriority(Notification.PRIORITY_LOW); // 低优先级,减少打扰 } else { - Notification.Builder builder = new Notification.Builder(mContext) + // API<21:基础配置,适配旧机型 + builder = new Notification.Builder(mContext) .setSmallIcon(NOTIFICATION_DEFAULT_ICON) - .setContentTitle(notifyTitle) - .setContentText(notifyContent) + .setContentTitle(title) + .setContentText(content) .setAutoCancel(false) .setOngoing(true) .setWhen(System.currentTimeMillis()) - .setTicker(notifyTitle) + .setTicker(title) .setPriority(Notification.PRIORITY_LOW); - builder.setContentIntent(createJumpPendingIntent(mContext, 0)); - notification = builder.build(); } - // 低版本通知默认效果屏蔽(无渠道时手动关闭声音/震动) + // 绑定跳转意图:点击通知打开主页面 + builder.setContentIntent(createJumpPendingIntent(mContext, 0)); + Notification notification = builder.build(); + + // API<26:无渠道,手动屏蔽声音/震动,确保无打扰(彻底关闭振动) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { notification.defaults = 0; - notification.vibrate = new long[]{0}; + notification.vibrate = new long[]{0}; // 振动时长设为0,彻底关闭 notification.sound = null; } return notification; } - // 构建电池提醒通知(低版本API<21 兼容配置) + // ================================== 内部辅助方法(通知构建:电池提醒通知)================================= + /** + * 构建电池提醒通知(API分级适配,确保提醒效果,无振动,支持手动关闭) + */ private Notification buildRemindNotification(Context context, NotificationMessage message) { if (context == null || message == null) { - LogUtils.e(TAG, "buildRemindNotification: 参数为空,构建失败"); + LogUtils.e(TAG, "提醒通知构建失败:参数/Context为空"); return null; } - String notifyTitle = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒"; - String notifyContent = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理(充电满/低电量)"; + // 内容兜底:避免消息标题/内容为空 + String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒"; + String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理"; - // API分级构建,确保提醒效果 - Notification notification; + Notification.Builder builder; + // API分级构建,确保提醒效果一致(全版本关闭振动) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID_REMIND) + // API26+:绑定提醒渠道,渠道已关闭振动,无需额外配置 + builder = new Notification.Builder(context, CHANNEL_ID_REMIND) .setSmallIcon(NOTIFICATION_DEFAULT_ICON) - .setContentTitle(notifyTitle) - .setContentText(notifyContent) - .setAutoCancel(true) + .setContentTitle(title) + .setContentText(content) + .setAutoCancel(true) // 点击后自动关闭 .setOngoing(false) .setWhen(System.currentTimeMillis()) - .setTicker(notifyTitle) - .setVibrate(new long[]{100, 200, 100}); - builder.setContentIntent(createJumpPendingIntent(context, 1)); - notification = builder.build(); + .setTicker(title); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Notification.Builder builder = new Notification.Builder(context) + // API21-25:添加大图标+主题色,关闭振动(删除setVibrationPattern和振动默认值) + builder = new Notification.Builder(context) .setSmallIcon(NOTIFICATION_DEFAULT_ICON) .setLargeIcon(getAppIcon(context)) .setColor(context.getResources().getColor(R.color.colorPrimary)) - .setContentTitle(notifyTitle) - .setContentText(notifyContent) + .setContentTitle(title) + .setContentText(content) .setAutoCancel(true) .setOngoing(false) .setWhen(System.currentTimeMillis()) - .setTicker(notifyTitle) - .setPriority(Notification.PRIORITY_DEFAULT) - .setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS) - .setVibrate(new long[]{100, 200, 100}); - builder.setContentIntent(createJumpPendingIntent(context, 1)); - notification = builder.build(); + .setTicker(title) + .setPriority(Notification.PRIORITY_DEFAULT) // 中优先级,确保感知 + .setDefaults(Notification.DEFAULT_LIGHTS); // 仅保留灯光提醒,关闭振动 } else { - Notification.Builder builder = new Notification.Builder(context) + // API<21:基础配置,仅保留灯光,关闭振动 + builder = new Notification.Builder(context) .setSmallIcon(NOTIFICATION_DEFAULT_ICON) - .setContentTitle(notifyTitle) - .setContentText(notifyContent) + .setContentTitle(title) + .setContentText(content) .setAutoCancel(true) .setOngoing(false) .setWhen(System.currentTimeMillis()) - .setTicker(notifyTitle) + .setTicker(title) .setPriority(Notification.PRIORITY_DEFAULT) - .setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS) - .setVibrate(new long[]{100, 200, 100}); - builder.setContentIntent(createJumpPendingIntent(context, 1)); - notification = builder.build(); + .setDefaults(Notification.DEFAULT_LIGHTS); // 仅保留灯光,关闭振动 } - return notification; + // 绑定跳转意图:点击通知打开主页面 + builder.setContentIntent(createJumpPendingIntent(context, 1)); + return builder.build(); } - // 内部辅助:创建跳转主页面的PendingIntent(适配API31+安全要求) + // ================================== 内部辅助方法(通用:创建跳转PendingIntent)================================= + /** + * 创建跳转主页面的PendingIntent(适配API30+安全要求,添加IMMUTABLE标记) + */ private PendingIntent createJumpPendingIntent(Context context, int requestCode) { + // 跳转意图:打开MainActivity,清除栈顶重复页面 Intent jumpIntent = new Intent(context, MainActivity.class); jumpIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - // API31+ 必须加FLAG_IMMUTABLE,避免安全异常 + + // 适配API30+安全要求:API23+ 必须添加FLAG_IMMUTABLE,避免安全异常 int flags = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { flags |= PendingIntent.FLAG_IMMUTABLE; } + return PendingIntent.getActivity(context, requestCode, jumpIntent, flags); } - // 内部辅助:获取APP图标(API21+ 大图标显示) - private android.graphics.Bitmap getAppIcon(Context context) { + // ================================== 内部辅助方法(通用:获取APP图标)================================= + /** + * 获取APP图标(API21+ 大图标显示使用,失败则返回默认图标) + */ + private Bitmap getAppIcon(Context context) { try { - android.content.pm.PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - return android.graphics.BitmapFactory.decodeResource(context.getResources(), packageInfo.applicationInfo.icon); - } catch (Exception e) { - LogUtils.e(TAG, "getAppIcon: 获取图标异常", e); - return android.graphics.BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON); + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + return BitmapFactory.decodeResource(context.getResources(), packageInfo.applicationInfo.icon); + } catch (PackageManager.NameNotFoundException e) { + LogUtils.e(TAG, "获取APP图标失败", e); + // 异常兜底:返回默认图标,避免显示空白 + return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON); } } - // 取消前台服务通知(适配Service销毁逻辑,统一管理前台通知生命周期) - public void cancelForegroundServiceNotify() { - LogUtils.d(TAG, "cancelForegroundServiceNotify: 取消前台服务通知,ID=" + NOTIFY_ID_FOREGROUND_SERVICE); - // 1. 先取消通知实例(避免通知残留) - cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE); - // 2. 置空前台通知引用,帮助GC回收 - if (mForegroundServiceNotify != null) { - mForegroundServiceNotify = null; - LogUtils.d(TAG, "cancelForegroundServiceNotify: 前台通知实例置空完成"); - } - } - - // 取消指定ID通知(保持稳定) - public void cancelNotification(int notifyId) { - LogUtils.d(TAG, "cancelNotification: 取消通知,ID=" + notifyId); - if (mNotificationManager != null) { - try { - mNotificationManager.cancel(notifyId); - LogUtils.d(TAG, "cancelNotification: 取消成功"); - } catch (Exception e) { - LogUtils.e(TAG, "cancelNotification: 取消异常", e); - } - } else { - LogUtils.e(TAG, "cancelNotification: NotificationManager为空,取消失败"); - } - } - - // 取消所有通知(保持稳定) - public void cancelAllNotifications() { - LogUtils.d(TAG, "cancelAllNotifications: 取消所有通知"); - if (mNotificationManager != null) { - try { - mNotificationManager.cancelAll(); - LogUtils.d(TAG, "cancelAllNotifications: 取消成功"); - } catch (Exception e) { - LogUtils.e(TAG, "cancelAllNotifications: 取消异常", e); - } - } else { - LogUtils.e(TAG, "cancelAllNotifications: NotificationManager为空,取消失败"); - } - } - - // 更新前台服务通知(保持稳定) - public void updateForegroundServiceNotify(NotificationMessage message) { - LogUtils.d(TAG, "updateForegroundServiceNotify: 更新前台通知"); - if (message == null || mNotificationManager == null) { - LogUtils.e(TAG, "updateForegroundServiceNotify: 参数为空,更新失败"); - return; - } - - mForegroundServiceNotify = buildForegroundNotification(message); - if (mForegroundServiceNotify != null) { - try { - mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); - LogUtils.d(TAG, "updateForegroundServiceNotify: 更新成功"); - } catch (Exception e) { - LogUtils.e(TAG, "updateForegroundServiceNotify: 更新异常", e); - } - } else { - LogUtils.e(TAG, "updateForegroundServiceNotify: 通知构建失败,更新失败"); - } - } - - // 获取前台服务通知实例(封装属性,外部仅可读) + // ================================== 对外工具方法(获取前台通知实例,仅可读)================================= public Notification getForegroundServiceNotify() { return mForegroundServiceNotify; } - // 释放资源(保持稳定) + // ================================== 资源释放(彻底释放依赖,避免内存泄漏)================================= public void release() { - LogUtils.d(TAG, "release: 释放资源"); - // 释放前先取消前台通知(兜底,避免资源泄漏) + LogUtils.d(TAG, "开始释放通知工具类资源"); + // 释放前先取消前台通知(兜底,避免残留) cancelForegroundServiceNotify(); + // 置空核心依赖,加速GC回收 mNotificationManager = null; mContext = null; - LogUtils.d(TAG, "release: 资源释放完成"); + LogUtils.d(TAG, "通知工具类资源释放完成"); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils6.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils6.java new file mode 100644 index 0000000..b4c3f44 --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils6.java @@ -0,0 +1,14 @@ +package cc.winboll.studio.powerbell.utils; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 13:42 + * @Describe NotificationManagerUtils + */ +public class NotificationManagerUtils6 { + + public static final String TAG = "NotificationManagerUtils6"; + + + +} \ No newline at end of file diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BatteryDrawable.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BatteryDrawable.java index a7bad9f..2096456 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BatteryDrawable.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BatteryDrawable.java @@ -1,80 +1,179 @@ package cc.winboll.studio.powerbell.views; -import android.graphics.Color; -import android.graphics.Rect; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import cc.winboll.studio.libappbase.LogUtils; +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 12:55 + * @Describe 电池电量Drawable:适配API30,兼容小米机型,支持能量/条纹两种绘制风格切换 + */ public class BatteryDrawable extends Drawable { - public static final String TAG = BatteryDrawable.class.getSimpleName(); + // ====================== 静态常量(置顶,按重要性排序) ====================== + public static final String TAG = "BatteryDrawable"; + // 小米机型绘制偏移校准(适配MIUI渲染特性,避免绘制错位) + private static final int MIUI_DRAW_OFFSET = 1; + // 默认电量透明度(兼顾显示效果与API30渲染性能) + private static final int DEFAULT_BATTERY_ALPHA = 210; - // 电量颜色画笔 - final Paint mPaint; - // 电量值 - int mnValue = -1; + // ====================== 核心成员变量(按功能归类,final优先) ====================== + // 绘制画笔(final修饰,避免重复创建,提升性能) + private final Paint mBatteryPaint; + // 业务控制变量 + private int mBatteryValue = -1; // 当前电量(0-100,-1=未初始化) + private boolean mIsEnergyStyle = true; // 绘制风格(true=能量,false=条纹) - // @int color : 电量颜色 - // - public BatteryDrawable(int color) { - mPaint = new Paint(); - mPaint.setColor(color); - mPaint.setAlpha(210); + // ====================== 构造方法(重载适配,优先暴露常用构造) ====================== + /** + * 构造方法(默认能量风格,常用场景) + * @param batteryColor 电量显示颜色 + */ + public BatteryDrawable(int batteryColor) { + LogUtils.d(TAG, "constructor: 初始化(能量风格),颜色=" + Integer.toHexString(batteryColor)); + mBatteryPaint = new Paint(); + initPaintConfig(batteryColor); } - // 设置电量值 - // - public void setValue(int value) { - mnValue = value; + /** + * 构造方法(支持指定绘制风格,扩展场景) + * @param batteryColor 电量显示颜色 + * @param isEnergyStyle 是否启用能量风格 + */ + public BatteryDrawable(int batteryColor, boolean isEnergyStyle) { + LogUtils.d(TAG, "constructor: 初始化,颜色=" + Integer.toHexString(batteryColor) + ",风格=" + (isEnergyStyle ? "能量" : "条纹")); + mBatteryPaint = new Paint(); + mIsEnergyStyle = isEnergyStyle; + initPaintConfig(batteryColor); } + // ====================== 私有初始化方法(封装复用,隐藏内部逻辑) ====================== + /** + * 初始化画笔配置(适配API30渲染特性,优化小米机型兼容性) + */ + private void initPaintConfig(int color) { + mBatteryPaint.setColor(color); + mBatteryPaint.setAlpha(DEFAULT_BATTERY_ALPHA); + mBatteryPaint.setAntiAlias(true); // 抗锯齿,解决小米低分辨率锯齿问题 + mBatteryPaint.setStyle(Paint.Style.FILL); // 固定填充模式,避免混乱 + mBatteryPaint.setDither(false); // 禁用抖动,提升API30颜色显示一致性 + LogUtils.d(TAG, "initPaintConfig: 画笔配置完成"); + } + + // ====================== 核心绘制方法(Drawable抽象方法,优先级最高) ====================== @Override public void draw(Canvas canvas) { - int nWidth = getBounds().width(); - int nHeight = getBounds().height(); - int mnDx = nHeight / 203; + // 未初始化/异常电量,直接跳过,避免无效绘制 + if (mBatteryValue < 0) { + LogUtils.w(TAG, "draw: 电量未初始化,跳过绘制"); + return; + } + // 强制校准电量范围(0-100),防止异常值导致绘制错误 + int validBattery = Math.max(0, Math.min(mBatteryValue, 100)); + Rect drawBounds = getBounds(); + int drawHeight = drawBounds.height(); - // 绘制耗电电量提醒值电量 - // 能量绘图风格 - int nTop; - int nLeft = 0; - int nBottom; - int nRight = nWidth; + // 小米机型绘制偏移校准(解决MIUI系统渲染偏移问题) + int offset = MIUI_DRAW_OFFSET; + int left = drawBounds.left + offset; + int right = drawBounds.right - offset; - //for (int i = 0; i < mnValue; i ++) { - nBottom = nHeight; - nTop = nHeight - (nHeight * mnValue / 100); - canvas.drawRect(new Rect(nLeft, nTop, nRight, nBottom), mPaint); - - // 绘制耗电电量提醒值电量 - // 意兴阑珊绘图风格 - /*int nTop; - int nLeft = 0; - int nBottom; - int nRight = nWidth; + // 按风格执行绘制(精简日志,仅保留核心绘制参数) + LogUtils.d(TAG, "draw: 开始绘制,电量=" + validBattery + ",风格=" + (mIsEnergyStyle ? "能量" : "条纹")); + if (mIsEnergyStyle) { + drawEnergyStyle(canvas, validBattery, left, right, drawHeight); + } else { + drawStripeStyle(canvas, validBattery, left, right, drawHeight); + } + } - for (int i = 0; i < mnValue; i ++) { - nBottom = (nHeight * (100-i)/100) - mnDx; - nTop = nBottom + mnDx; - canvas.drawRect(new Rect(nLeft, nTop, nRight, nBottom), mPaint); - }*/ + // ====================== 绘制风格实现(私有封装,按风格拆分) ====================== + /** + * 能量风格绘制(整块填充,高效简洁,默认风格) + */ + private void drawEnergyStyle(Canvas canvas, int battery, int left, int right, int height) { + int top = height - (height * battery / 100); // 计算电量对应顶部坐标 + canvas.drawRect(new Rect(left, top, right, height), mBatteryPaint); + LogUtils.d(TAG, "drawEnergyStyle: 绘制完成,顶部坐标=" + top); + } + + /** + * 条纹风格绘制(分段条纹,扩展风格) + */ + private void drawStripeStyle(Canvas canvas, int battery, int left, int right, int height) { + int stripeHeight = height / 100; // 单条条纹高度(均匀拆分) + // 从底部向上绘制对应电量条纹 + for (int i = 0; i < battery; i++) { + int bottom = height - (stripeHeight * i); + int top = bottom - stripeHeight; + canvas.drawRect(new Rect(left, top, right, bottom), mBatteryPaint); + } + LogUtils.d(TAG, "drawStripeStyle: 绘制完成,条纹数量=" + battery); + } + + // ====================== 对外暴露方法(业务控制入口,按功能排序) ====================== + /** + * 设置当前电量(外部核心调用入口) + * @param value 电量值(0-100) + */ + public void setBatteryValue(int value) { + LogUtils.d(TAG, "setBatteryValue: 电量更新,旧值=" + mBatteryValue + ",新值=" + value); + mBatteryValue = value; + invalidateSelf(); // 触发重绘,确保UI实时更新 + } + + /** + * 切换绘制风格 + * @param isEnergyStyle true=能量风格,false=条纹风格 + */ + public void switchDrawStyle(boolean isEnergyStyle) { + LogUtils.d(TAG, "switchDrawStyle: 风格切换,旧=" + (mIsEnergyStyle ? "能量" : "条纹") + ",新=" + (isEnergyStyle ? "能量" : "条纹")); + mIsEnergyStyle = isEnergyStyle; + invalidateSelf(); + } + + /** + * 更新电量显示颜色 + * @param color 新颜色值 + */ + public void updateBatteryColor(int color) { + LogUtils.d(TAG, "updateBatteryColor: 颜色更新,旧=" + Integer.toHexString(mBatteryPaint.getColor()) + ",新=" + Integer.toHexString(color)); + mBatteryPaint.setColor(color); + invalidateSelf(); + } + + // ====================== Getter方法(按需暴露,简洁无冗余) ====================== + public int getBatteryValue() { + return mBatteryValue; + } + + public boolean isEnergyStyle() { + return mIsEnergyStyle; + } + + // ====================== Drawable抽象方法(必须实现,精简逻辑) ====================== + @Override + public void setAlpha(int alpha) { + LogUtils.d(TAG, "setAlpha: 透明度更新,旧=" + mBatteryPaint.getAlpha() + ",新=" + alpha); + mBatteryPaint.setAlpha(alpha); + invalidateSelf(); } @Override public void setColorFilter(ColorFilter colorFilter) { - // This method is required - } - - @Override - public void setAlpha(int p1) { - + LogUtils.d(TAG, "setColorFilter: 设置颜色过滤,filter=" + colorFilter); + mBatteryPaint.setColorFilter(colorFilter); + invalidateSelf(); } @Override public int getOpacity() { - return PixelFormat.UNKNOWN; + // 固定返回半透明,适配API30透明度渲染机制,兼容小米机型 + return PixelFormat.TRANSLUCENT; } - } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java new file mode 100644 index 0000000..8d79393 --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java @@ -0,0 +1,420 @@ +package cc.winboll.studio.powerbell.views; + +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.Switch; +import android.widget.TextView; +import cc.winboll.studio.powerbell.R; +import cc.winboll.studio.powerbell.utils.AppConfigUtils; +import cc.winboll.studio.libappbase.LogUtils; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 13:14 + * @Describe 主页面核心视图封装类:统一管理视图绑定、数据更新、事件监听,解耦 Activity 逻辑 + * 适配:Java7 | API30 | 小米手机,优化性能与资源回收,杜绝内存泄漏 + */ +public class MainContentView { + // ======================== 静态常量(置顶,唯一标识)======================== + public static final String TAG = "MainContentView"; + + // ======================== 核心成员变量(按「依赖→视图→内部资源」排序)======================== + // 外部依赖实例(生命周期关联,优先声明) + private Context mContext; + private AppConfigUtils mAppConfigUtils; + private OnViewActionListener mActionListener; + + // 视图控件(按「布局→开关→文本→进度条→图标」功能归类) + // 基础布局控件 + public RelativeLayout mainLayout; + public BackgroundView backgroundView; + // 容器布局控件 + public LinearLayout llLeftSeekBar; + public LinearLayout llRightSeekBar; + // 开关控件 + public CheckBox cbEnableChargeReminder; + public CheckBox cbEnableUsageReminder; + public Switch swEnableService; + // 文本显示控件 + public TextView tvTips; + public TextView tvChargeReminderValue; + public TextView tvUsageReminderValue; + public TextView tvCurrentBatteryValue; + // 进度条控件 + public VerticalSeekBar sbChargeReminder; + public VerticalSeekBar sbUsageReminder; + // 图标显示控件 + public ImageView ivCurrentBattery; + public ImageView ivChargeReminderBattery; + public ImageView ivUsageReminderBattery; + + // 内部复用资源(避免重复创建,优化性能) + private BatteryDrawable mCurrentBatteryDrawable; + private BatteryDrawable mChargeReminderBatteryDrawable; + private BatteryDrawable mUsageReminderBatteryDrawable; + + // ======================== 构造方法(初始化入口,逻辑闭环)======================== + public MainContentView(Context context, View rootView, OnViewActionListener actionListener) { + LogUtils.d(TAG, "constructor: 开始初始化"); + // 初始化外部依赖 + this.mContext = context; + this.mActionListener = actionListener; + this.mAppConfigUtils = AppConfigUtils.getInstance(context.getApplicationContext()); + LogUtils.d(TAG, "constructor: 外部依赖初始化完成"); + + // 执行核心初始化流程(按顺序执行,避免依赖空指针) + bindViews(rootView); + initBatteryDrawables(); + bindViewListeners(); + + LogUtils.d(TAG, "constructor: 整体初始化完成"); + } + + // ======================== 私有初始化方法(封装内部逻辑,仅暴露入口)======================== + /** + * 绑定视图控件(显式强转适配 Java7,适配 API30 视图加载机制) + */ + private void bindViews(View rootView) { + LogUtils.d(TAG, "bindViews: 开始绑定视图"); + // 基础布局绑定 + mainLayout = (RelativeLayout) rootView.findViewById(R.id.activitymainRelativeLayout1); + backgroundView = (BackgroundView) rootView.findViewById(R.id.fragmentmainviewBackgroundView1); + // 容器布局绑定 + llLeftSeekBar = (LinearLayout) rootView.findViewById(R.id.fragmentmainviewLinearLayout1); + llRightSeekBar = (LinearLayout) rootView.findViewById(R.id.fragmentmainviewLinearLayout2); + // 开关控件绑定 + cbEnableChargeReminder = (CheckBox) rootView.findViewById(R.id.fragmentmainviewCheckBox1); + cbEnableUsageReminder = (CheckBox) rootView.findViewById(R.id.fragmentmainviewCheckBox2); + swEnableService = (Switch) rootView.findViewById(R.id.fragmentandroidviewSwitch1); + // 文本控件绑定 + tvTips = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView1); + tvChargeReminderValue = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView2); + tvUsageReminderValue = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView3); + tvCurrentBatteryValue = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView4); + // 进度条控件绑定 + sbChargeReminder = (VerticalSeekBar) rootView.findViewById(R.id.fragmentandroidviewVerticalSeekBar1); + sbUsageReminder = (VerticalSeekBar) rootView.findViewById(R.id.fragmentandroidviewVerticalSeekBar2); + // 图标控件绑定 + ivCurrentBattery = (ImageView) rootView.findViewById(R.id.fragmentandroidviewImageView1); + ivChargeReminderBattery = (ImageView) rootView.findViewById(R.id.fragmentandroidviewImageView3); + ivUsageReminderBattery = (ImageView) rootView.findViewById(R.id.fragmentandroidviewImageView2); + + // 关键视图绑定校验(仅保留核心控件错误日志,精简冗余) + if (mainLayout == null) LogUtils.e(TAG, "bindViews: mainLayout 绑定失败"); + if (backgroundView == null) LogUtils.e(TAG, "bindViews: backgroundView 绑定失败"); + LogUtils.d(TAG, "bindViews: 视图绑定完成"); + } + + /** + * 初始化电池 Drawable(集成 BatteryDrawable,默认能量风格,适配小米机型渲染) + */ + private void initBatteryDrawables() { + LogUtils.d(TAG, "initBatteryDrawables: 开始初始化电池 Drawable"); + // 当前电量 Drawable(颜色从资源读取,适配 API30 主题) + int colorCurrent = getResourceColor(R.color.colorCurrent); + mCurrentBatteryDrawable = new BatteryDrawable(colorCurrent); + // 充电提醒 Drawable + int colorCharge = getResourceColor(R.color.colorCharge); + mChargeReminderBatteryDrawable = new BatteryDrawable(colorCharge); + // 耗电提醒 Drawable + int colorUsage = getResourceColor(R.color.colorUsege); + mUsageReminderBatteryDrawable = new BatteryDrawable(colorUsage); + + LogUtils.d(TAG, "initBatteryDrawables: 电池 Drawable 初始化完成"); + } + + /** + * 绑定视图事件监听(Java7 显式实现接口,避免 Lambda,适配 API30 事件分发) + */ + private void bindViewListeners() { + LogUtils.d(TAG, "bindViewListeners: 开始绑定事件监听"); + // 依赖校验,避免空指针 + if (mAppConfigUtils == null || mActionListener == null) { + LogUtils.e(TAG, "bindViewListeners: 依赖实例为空,跳过监听绑定"); + return; + } + + // 充电提醒进度条监听 + if (sbChargeReminder != null) { + sbChargeReminder.setOnSeekBarChangeListener(new ChargeReminderSeekBarListener()); + LogUtils.d(TAG, "bindViewListeners: 充电提醒进度条监听绑定完成"); + } + // 充电提醒开关监听 + if (cbEnableChargeReminder != null) { + cbEnableChargeReminder.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean isChecked = cbEnableChargeReminder.isChecked(); + mAppConfigUtils.setChargeReminderEnabled((Activity) mContext, isChecked); + mActionListener.onChargeReminderSwitchChanged(isChecked); + LogUtils.d(TAG, "cbEnableChargeReminder: 状态切换为 " + isChecked); + } + }); + LogUtils.d(TAG, "bindViewListeners: 充电提醒开关监听绑定完成"); + } + + // 耗电提醒进度条监听 + if (sbUsageReminder != null) { + sbUsageReminder.setOnSeekBarChangeListener(new UsageReminderSeekBarListener()); + LogUtils.d(TAG, "bindViewListeners: 耗电提醒进度条监听绑定完成"); + } + // 耗电提醒开关监听 + if (cbEnableUsageReminder != null) { + cbEnableUsageReminder.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean isChecked = cbEnableUsageReminder.isChecked(); + mAppConfigUtils.setUsageReminderEnabled((Activity) mContext, isChecked); + mActionListener.onUsageReminderSwitchChanged(isChecked); + LogUtils.d(TAG, "cbEnableUsageReminder: 状态切换为 " + isChecked); + } + }); + LogUtils.d(TAG, "bindViewListeners: 耗电提醒开关监听绑定完成"); + } + + // 服务总开关监听 + if (swEnableService != null) { + swEnableService.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean isChecked = ((Switch) v).isChecked(); + mAppConfigUtils.setServiceEnabled((Activity) mContext, isChecked); + mActionListener.onServiceSwitchChanged(isChecked); + LogUtils.d(TAG, "swEnableService: 状态切换为 " + isChecked); + } + }); + LogUtils.d(TAG, "bindViewListeners: 服务总开关监听绑定完成"); + } + + LogUtils.d(TAG, "bindViewListeners: 所有事件监听绑定完成"); + } + + // ======================== 对外暴露核心方法(业务入口,精简参数,明确职责)======================== + /** + * 更新所有视图数据(从配置读取数据,统一刷新 UI,适配 API30 视图更新规范) + * @param frameDrawable 进度条背景 Drawable(外部传入,适配主题切换) + */ + public void updateViewData(Drawable frameDrawable) { + LogUtils.d(TAG, "updateViewData: 开始更新视图数据"); + if (mAppConfigUtils == null) { + LogUtils.e(TAG, "updateViewData: AppConfigUtils 为空,跳过更新"); + return; + } + + // 一次读取所有配置参数,减少工具类调用,提升性能 + int chargeVal = mAppConfigUtils.getChargeReminderValue(); + int usageVal = mAppConfigUtils.getUsageReminderValue(); + int currentVal = mAppConfigUtils.getCurrentBatteryValue(); + boolean chargeEnable = mAppConfigUtils.isChargeReminderEnabled(); + boolean usageEnable = mAppConfigUtils.isUsageReminderEnabled(); + boolean serviceEnable = mAppConfigUtils.isServiceEnabled(); + LogUtils.d(TAG, "updateViewData: 配置数据读取完成,charge=" + chargeVal + ", usage=" + usageVal + ", current=" + currentVal); + + // 进度条背景更新 + if (frameDrawable != null) { + if (llLeftSeekBar != null) llLeftSeekBar.setBackground(frameDrawable); + if (llRightSeekBar != null) llRightSeekBar.setBackground(frameDrawable); + LogUtils.d(TAG, "updateViewData: 进度条背景更新完成"); + } + + // 当前电量更新(联动 BatteryDrawable,实时刷新图标) + if (ivCurrentBattery != null && mCurrentBatteryDrawable != null) { + mCurrentBatteryDrawable.setBatteryValue(currentVal); + ivCurrentBattery.setImageDrawable(mCurrentBatteryDrawable); + } + if (tvCurrentBatteryValue != null) { + tvCurrentBatteryValue.setTextColor(getResourceColor(R.color.colorCurrent)); + tvCurrentBatteryValue.setText(currentVal + "%"); + } + LogUtils.d(TAG, "updateViewData: 当前电量视图更新完成"); + + // 充电提醒视图更新 + if (ivChargeReminderBattery != null && mChargeReminderBatteryDrawable != null) { + mChargeReminderBatteryDrawable.setBatteryValue(chargeVal); + ivChargeReminderBattery.setImageDrawable(mChargeReminderBatteryDrawable); + } + if (tvChargeReminderValue != null) { + tvChargeReminderValue.setTextColor(getResourceColor(R.color.colorCharge)); + tvChargeReminderValue.setText(chargeVal + "%"); + } + if (sbChargeReminder != null) sbChargeReminder.setProgress(chargeVal); + if (cbEnableChargeReminder != null) cbEnableChargeReminder.setChecked(chargeEnable); + LogUtils.d(TAG, "updateViewData: 充电提醒视图更新完成"); + + // 耗电提醒视图更新 + if (ivUsageReminderBattery != null && mUsageReminderBatteryDrawable != null) { + mUsageReminderBatteryDrawable.setBatteryValue(usageVal); + ivUsageReminderBattery.setImageDrawable(mUsageReminderBatteryDrawable); + } + if (tvUsageReminderValue != null) { + tvUsageReminderValue.setTextColor(getResourceColor(R.color.colorUsege)); + tvUsageReminderValue.setText(usageVal + "%"); + } + if (sbUsageReminder != null) sbUsageReminder.setProgress(usageVal); + if (cbEnableUsageReminder != null) cbEnableUsageReminder.setChecked(usageEnable); + LogUtils.d(TAG, "updateViewData: 耗电提醒视图更新完成"); + + // 服务开关+提示文本更新 + if (swEnableService != null) { + swEnableService.setChecked(serviceEnable); + swEnableService.setText(mContext.getString(R.string.txt_aboveswitch)); + } + if (tvTips != null) tvTips.setText(mContext.getString(R.string.txt_aboveswitchtips)); + LogUtils.d(TAG, "updateViewData: 服务开关及提示文本更新完成"); + + LogUtils.d(TAG, "updateViewData: 所有视图数据更新完成"); + } + + /** + * 实时更新当前电量(单独抽离,适配电池实时监控场景,优化 API30 UI 响应速度) + * @param value 电量值(自动校准 0-100,避免异常值) + */ + public void updateCurrentBattery(int value) { + LogUtils.d(TAG, "updateCurrentBattery: 开始更新,原始值=" + value); + // 核心依赖校验 + if (tvCurrentBatteryValue == null || mCurrentBatteryDrawable == null || ivCurrentBattery == null) { + LogUtils.e(TAG, "updateCurrentBattery: 视图/Drawable 为空,跳过更新"); + return; + } + + // 校准电量范围(强制 0-100,防止 API30 视图显示异常) + int validValue = Math.max(0, Math.min(value, 100)); + // 联动 BatteryDrawable 更新图标,同步文本显示 + mCurrentBatteryDrawable.setBatteryValue(validValue); + ivCurrentBattery.setImageDrawable(mCurrentBatteryDrawable); + tvCurrentBatteryValue.setText(validValue + "%"); + + LogUtils.d(TAG, "updateCurrentBattery: 更新完成,校准后值=" + validValue); + } + + /** + * 释放资源(主动回收,适配 API30 资源管控机制,优化小米手机内存占用) + */ + public void releaseResources() { + LogUtils.d(TAG, "releaseResources: 开始释放资源"); + // 释放 BatteryDrawable 资源(重点回收绘制资源,避免 OOM) + mCurrentBatteryDrawable = null; + mChargeReminderBatteryDrawable = null; + mUsageReminderBatteryDrawable = null; + + // 置空视图实例(断开视图引用,辅助 GC 回收) + mainLayout = null; + backgroundView = null; + llLeftSeekBar = null; + llRightSeekBar = null; + cbEnableChargeReminder = null; + cbEnableUsageReminder = null; + swEnableService = null; + tvTips = null; + tvChargeReminderValue = null; + tvUsageReminderValue = null; + tvCurrentBatteryValue = null; + sbChargeReminder = null; + sbUsageReminder = null; + ivCurrentBattery = null; + ivChargeReminderBattery = null; + ivUsageReminderBattery = null; + + // 置空外部依赖(断开生命周期关联,杜绝内存泄漏) + mContext = null; + mAppConfigUtils = null; + mActionListener = null; + + LogUtils.d(TAG, "releaseResources: 所有资源释放完成"); + } + + // ======================== 内部工具方法(封装重复逻辑,提升复用性)======================== + /** + * 获取资源颜色(适配 API30 主题颜色读取机制,兼容低版本,优化小米机型颜色显示) + * @param colorResId 颜色资源 ID + * @return 校准后的颜色值 + */ + private int getResourceColor(int colorResId) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // API23+ 支持主题颜色,适配 API30 主题机制 + return mContext.getResources().getColor(colorResId, mContext.getTheme()); + } else { + // 低版本兼容 + return mContext.getResources().getColor(colorResId); + } + } + + // ======================== 内部事件监听类(私有封装,职责单一,避免外部依赖)======================== + /** + * 充电提醒进度条监听(仅处理充电提醒进度相关逻辑) + */ + private class ChargeReminderSeekBarListener implements SeekBar.OnSeekBarChangeListener { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + // 实时更新视图(联动 BatteryDrawable,适配 API30 实时渲染) + if (tvChargeReminderValue != null && mChargeReminderBatteryDrawable != null && ivChargeReminderBattery != null) { + mChargeReminderBatteryDrawable.setBatteryValue(progress); + ivChargeReminderBattery.setImageDrawable(mChargeReminderBatteryDrawable); + tvChargeReminderValue.setText(progress + "%"); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // 触摸开始,无额外逻辑,留空保持接口完整 + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // 触摸结束,保存配置并回调 + if (mAppConfigUtils == null || mActionListener == null) return; + int progress = seekBar.getProgress(); + mAppConfigUtils.setChargeReminderValue((Activity) mContext, progress); + mActionListener.onChargeReminderProgressChanged(progress); + LogUtils.d(TAG, "ChargeReminderSeekBar: 进度确认,保存值=" + progress); + } + } + + /** + * 耗电提醒进度条监听(仅处理耗电提醒进度相关逻辑) + */ + private class UsageReminderSeekBarListener implements SeekBar.OnSeekBarChangeListener { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + // 实时更新视图(联动 BatteryDrawable,适配 API30 实时渲染) + if (tvUsageReminderValue != null && mUsageReminderBatteryDrawable != null && ivUsageReminderBattery != null) { + mUsageReminderBatteryDrawable.setBatteryValue(progress); + ivUsageReminderBattery.setImageDrawable(mUsageReminderBatteryDrawable); + tvUsageReminderValue.setText(progress + "%"); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // 触摸开始,无额外逻辑,留空保持接口完整 + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // 触摸结束,保存配置并回调 + if (mAppConfigUtils == null || mActionListener == null) return; + int progress = seekBar.getProgress(); + mAppConfigUtils.setUsageReminderValue((Activity) mContext, progress); + mActionListener.onUsageReminderProgressChanged(progress); + LogUtils.d(TAG, "UsageReminderSeekBar: 进度确认,保存值=" + progress); + } + } + + // ======================== 事件回调接口(解耦视图与业务,提升扩展性)======================== + public interface OnViewActionListener { + void onChargeReminderSwitchChanged(boolean isChecked); + void onUsageReminderSwitchChanged(boolean isChecked); + void onServiceSwitchChanged(boolean isChecked); + void onChargeReminderProgressChanged(int progress); + void onUsageReminderProgressChanged(int progress); + } +} +