豆包完美想象版,未调试。

This commit is contained in:
2025-12-17 07:48:32 +08:00
parent 405b914f02
commit 7b17fae798
16 changed files with 3186 additions and 1681 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Dec 16 20:29:08 GMT 2025
#Tue Dec 16 23:46:39 GMT 2025
stageCount=9
libraryProject=
baseVersion=15.14
publishVersion=15.14.8
buildCount=8
buildCount=25
baseBetaVersion=15.14.9

View File

@@ -1,42 +0,0 @@
package cc.winboll.studio.powerbell.fragments;
import android.app.Fragment;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
import cc.winboll.studio.powerbell.models.BackgroundBean;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
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.VerticalSeekBar;
public class MainViewFragment extends Fragment {
public static final String TAG = "MainViewFragment";
}

View File

@@ -2,35 +2,72 @@ package cc.winboll.studio.powerbell.handlers;
import android.os.Handler;
import android.os.Message;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import java.lang.ref.WeakReference;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.models.NotificationMessage;
/**
* 服务通信Handler弱引用持有服务避免内存泄漏适配Java7
*/
public class ControlCenterServiceHandler extends Handler {
public static final String TAG = ControlCenterServiceHandler.class.getSimpleName();
// 消息类型常量与RemindThread对应
public static final int MSG_REMIND_TEXT = 1001; // 提醒消息
private static final String TAG = "ControlCenterServiceHandler";
public static final int MSG_REMIND_TEXT = 0;
// 弱引用持有服务实例(避免内存泄漏)
private WeakReference<ControlCenterService> mwrControlCenterService;
WeakReference<ControlCenterService> serviceWeakReference;
// 构造方法(传入服务实例)
public ControlCenterServiceHandler(ControlCenterService service) {
serviceWeakReference = new WeakReference<ControlCenterService>(service);
this.mwrControlCenterService = new WeakReference<>(service);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
ControlCenterService service = mwrControlCenterService.get();
if (service == null) {
LogUtils.e(TAG, "handleMessage: 服务实例为空,处理失败");
return;
}
// 处理不同类型消息
switch (msg.what) {
case MSG_REMIND_TEXT: // 处理下载完成消息更新UI
{
// 显示提醒消息
//
//LogUtils.d(TAG, "显示提醒消息");
ControlCenterService controlCenterService = serviceWeakReference.get();
if (controlCenterService != null) {
//LogUtils.d(TAG, ((NotificationMessage)msg.obj).getTitle());
//LogUtils.d(TAG, ((NotificationMessage)msg.obj).getContent());
controlCenterService.appenRemindMSG((String)msg.obj);
}
break;
}
case MSG_REMIND_TEXT:
// 接收RemindThread的提醒消息调用通知工具类发送通知
handleRemindMessage(service, (String) msg.obj);
break;
default:
LogUtils.d(TAG, "handleMessage: 未知消息类型what=" + msg.what);
break;
}
}
// 处理提醒消息(发送通知)
private void handleRemindMessage(ControlCenterService service, String content) {
LogUtils.d(TAG, "handleRemindMessage: 处理提醒消息,内容=" + content);
// 复用服务的通知工具类和前台通知模型
if (service.getNotificationManager() == null) {
LogUtils.e(TAG, "handleRemindMessage: 通知工具类为空,发送失败");
return;
}
// 构建提醒通知消息(可按需求调整标题/内容)
NotificationMessage remindMsg = new NotificationMessage();
if ("+".equals(content)) { // 充电提醒
remindMsg.setTitle("充电提醒");
remindMsg.setContent("电池电量已达标,建议及时断电保护电池~");
remindMsg.setRemindMSG("charge_remind");
} else if ("-".equals(content)) { // 耗电提醒
remindMsg.setTitle("耗电提醒");
remindMsg.setContent("电池电量偏低,建议及时充电~");
remindMsg.setRemindMSG("usage_remind");
}
// 调用通知工具类发送提醒通知(复用现有逻辑)
service.getNotificationManager().showRemindNotification(service, remindMsg);
LogUtils.d(TAG, "handleRemindMessage: 提醒通知发送完成");
}
}

View File

@@ -5,44 +5,71 @@ package cc.winboll.studio.powerbell.models;
* @Date 2024/04/29 17:24:53
* @Describe 应用运行参数类
*/
import android.os.Parcel;
import android.os.Parcelable;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
import java.io.Serializable;
public class AppConfigBean extends BaseBean implements Serializable {
// 核心修正:新增 Parcelable 接口实现API30 持久化/Intent 传递必备)
public class AppConfigBean extends BaseBean implements Serializable, Parcelable {
// 序列化版本号Serializable 必备,避免反序列化失败)
private static final long serialVersionUID = 1L;
transient public static final String TAG = "AppConfigBean";
boolean isEnableUsegeReminder = false;
int usegeReminderValue = 45;
boolean isEnableChargeReminder = false;
int chargeReminderValue = 100;
// 铃声提醒间隔时间。.
int reminderIntervalTime = 5000;
// 电池是否正在充电。
boolean isCharging = false;
// 电池当前电量。.
int currentValue = -1;
// 核心配置字段(保留原有字段,统一状态字段命名)
boolean isEnableUsageReminder = false; // 耗电提醒开关
int usageReminderValue = 45; // 耗电提醒阈值0-100
boolean isEnableChargeReminder = false;// 充电提醒开关
int chargeReminderValue = 100; // 充电提醒阈值0-100
int reminderIntervalTime = 5000; // 铃声提醒间隔ms原有
boolean isCharging = false; // 是否充电(状态字段,原有)
int currentBatteryValue = -1; // 修正统一命名为「currentBatteryValue」原 currentValue
int batteryDetectInterval = 1000; // 新增电量检测间隔ms适配 RemindThread
// 构造方法:初始化默认配置(同步修正字段名,统一默认值)
public AppConfigBean() {
setChargeReminderValue(100);
setIsEnableChargeReminder(false);
setUsegeReminderValue(10);
setIsEnableUsegeReminder(false);
setEnableChargeReminder(false);
setUsageReminderValue(10);
setEnableUsageReminder(false);
setReminderIntervalTime(5000);
setBatteryDetectInterval(1000); // 新增默认检测间隔1秒
setCurrentBatteryValue(-1); // 修正:初始化当前电量字段
}
// ====================== 核心修复:补全缺失方法(适配 RemindThread/Receiver 调用) ======================
/**
* 设置当前电池电量Receiver 监听电池变化时调用,与 RemindThread 字段对齐)
*/
public void setCurrentBatteryValue(int currentBatteryValue) {
// 强化校验:电量范围限制在 0-100异常值置为 -1标识无效
this.currentBatteryValue = (currentBatteryValue >= 0 && currentBatteryValue <= 100)
? currentBatteryValue : -1;
}
/**
* 获取当前电池电量RemindThread 同步配置时调用,与 set 方法对应)
*/
public int getCurrentBatteryValue() {
return currentBatteryValue;
}
// ====================== 原有字段 Setter/Getter修正命名强化校验 ======================
public void setReminderIntervalTime(int reminderIntervalTime) {
this.reminderIntervalTime = reminderIntervalTime;
// 校验:提醒间隔不小于 1000ms避免频繁提醒
this.reminderIntervalTime = Math.max(reminderIntervalTime, 1000);
}
public int getReminderIntervalTime() {
return reminderIntervalTime;
}
public void setIsCharging(boolean isCharging) {
public void setIsCharging(boolean isCharging) { // 修正:方法名与字段名统一(原 setCharging
this.isCharging = isCharging;
}
@@ -50,31 +77,24 @@ public class AppConfigBean extends BaseBean implements Serializable {
return isCharging;
}
public void setCurrentValue(int currentValue) {
this.currentValue = currentValue;
public void setEnableUsageReminder(boolean isEnableUsageReminder) {
this.isEnableUsageReminder = isEnableUsageReminder;
}
public int getCurrentValue() {
return currentValue;
public boolean isEnableUsageReminder() {
return isEnableUsageReminder;
}
public void setIsEnableUsegeReminder(boolean isEnableUsegeReminder) {
this.isEnableUsegeReminder = isEnableUsegeReminder;
public void setUsageReminderValue(int usageReminderValue) {
// 校验:阈值范围 0-100
this.usageReminderValue = Math.min(Math.max(usageReminderValue, 0), 100);
}
public boolean isEnableUsegeReminder() {
return isEnableUsegeReminder;
public int getUsageReminderValue() {
return usageReminderValue;
}
public void setUsegeReminderValue(int usegeReminderValue) {
this.usegeReminderValue = usegeReminderValue;
}
public int getUsegeReminderValue() {
return usegeReminderValue;
}
public void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
public void setEnableChargeReminder(boolean isEnableChargeReminder) {
this.isEnableChargeReminder = isEnableChargeReminder;
}
@@ -83,13 +103,25 @@ public class AppConfigBean extends BaseBean implements Serializable {
}
public void setChargeReminderValue(int chargeReminderValue) {
this.chargeReminderValue = chargeReminderValue;
// 校验:阈值范围 0-100
this.chargeReminderValue = Math.min(Math.max(chargeReminderValue, 0), 100);
}
public int getChargeReminderValue() {
return chargeReminderValue;
}
// ====================== 电量检测间隔 Setter/Getter适配 RemindThread ======================
public int getBatteryDetectInterval() {
return batteryDetectInterval;
}
// 强化校验检测间隔不小于500ms避免 CPU 高占用,与 RemindThread 最小休眠一致)
public void setBatteryDetectInterval(int batteryDetectInterval) {
this.batteryDetectInterval = Math.max(batteryDetectInterval, 500);
}
// ====================== JSON 序列化/反序列化(兼容旧配置,同步修正字段) ======================
@Override
public String getName() {
return AppConfigBean.class.getName();
@@ -99,10 +131,18 @@ public class AppConfigBean extends BaseBean implements Serializable {
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
AppConfigBean bean = this;
jsonWriter.name("isEnableUsegeReminder").value(bean.isEnableUsegeReminder());
jsonWriter.name("usegeReminderValue").value(bean.getUsegeReminderValue());
// 原有字段序列化(保留拼写兼容,同步修正字段名)
jsonWriter.name("isEnableUsageReminder").value(bean.isEnableUsageReminder());
jsonWriter.name("usageReminderValue").value(bean.getUsageReminderValue());
jsonWriter.name("isEnableChargeReminder").value(bean.isEnableChargeReminder());
jsonWriter.name("chargeReminderValue").value(bean.getChargeReminderValue());
jsonWriter.name("reminderIntervalTime").value(bean.getReminderIntervalTime());
jsonWriter.name("isCharging").value(bean.isCharging());
// 修正:序列化新字段名 currentBatteryValue兼容旧字段 currentValue
jsonWriter.name("currentBatteryValue").value(bean.getCurrentBatteryValue());
jsonWriter.name("currentValue").value(bean.getCurrentBatteryValue()); // 兼容旧配置,避免数据丢失
// 新增字段序列化:电量检测间隔
jsonWriter.name("batteryDetectInterval").value(bean.getBatteryDetectInterval());
}
@Override
@@ -111,20 +151,77 @@ public class AppConfigBean extends BaseBean implements Serializable {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (name.equals("isEnableUsegeReminder")) {
bean.setIsEnableUsegeReminder(jsonReader.nextBoolean());
} else if (name.equals("usegeReminderValue")) {
bean.setUsegeReminderValue(jsonReader.nextInt());
// 原有字段反序列化(兼容旧 Key 拼写,同步修正字段)
if (name.equals("isEnableUsageReminder") || name.equals("isEnableUsegeReminder")) {
bean.setEnableUsageReminder(jsonReader.nextBoolean());
} else if (name.equals("usageReminderValue") || name.equals("usegeReminderValue")) {
bean.setUsageReminderValue(jsonReader.nextInt());
} else if (name.equals("isEnableChargeReminder")) {
bean.setIsEnableChargeReminder(jsonReader.nextBoolean());
bean.setEnableChargeReminder(jsonReader.nextBoolean());
} else if (name.equals("chargeReminderValue")) {
bean.setChargeReminderValue(jsonReader.nextInt());
} else if (name.equals("reminderIntervalTime")) {
bean.setReminderIntervalTime(jsonReader.nextInt());
} else if (name.equals("isCharging")) {
bean.setIsCharging(jsonReader.nextBoolean()); // 修正:调用新方法名
}
// 核心兼容:优先读取旧字段 currentValue再读取新字段 currentBatteryValue新字段覆盖旧字段
else if (name.equals("currentValue")) {
bean.setCurrentBatteryValue(jsonReader.nextInt());
} else if (name.equals("currentBatteryValue")) {
bean.setCurrentBatteryValue(jsonReader.nextInt());
}
// 新增字段反序列化兼容无此字段的旧配置用默认值1000ms
else if (name.equals("batteryDetectInterval")) {
bean.setBatteryDetectInterval(jsonReader.nextInt());
} else {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return bean;
}
// ====================== Parcelable 接口实现(同步修正字段,确保 Intent 传递正常) ======================
@Override
public int describeContents() {
return 0; // 无特殊内容描述固定返回0
}
// 序列化:将所有字段写入 Parcel同步修正字段名Java7 适配)
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (isEnableUsageReminder ? 1 : 0)); // boolean → byte
dest.writeInt(usageReminderValue);
dest.writeByte((byte) (isEnableChargeReminder ? 1 : 0)); // boolean → byte
dest.writeInt(chargeReminderValue);
dest.writeInt(reminderIntervalTime);
dest.writeByte((byte) (isCharging ? 1 : 0)); // boolean → byte
dest.writeInt(currentBatteryValue); // 修正:序列化新字段名
dest.writeInt(batteryDetectInterval);
}
// 反序列化:从 Parcel 读取字段,创建对象(必须 public static final 修饰)
public static final Parcelable.Creator<AppConfigBean> CREATOR = new Parcelable.Creator<AppConfigBean>() {
@Override
public AppConfigBean createFromParcel(Parcel source) {
AppConfigBean bean = new AppConfigBean();
// 按 writeToParcel 顺序读取,同步修正字段
bean.isEnableUsageReminder = source.readByte() != 0;
bean.usageReminderValue = source.readInt();
bean.isEnableChargeReminder = source.readByte() != 0;
bean.chargeReminderValue = source.readInt();
bean.reminderIntervalTime = source.readInt();
bean.isCharging = source.readByte() != 0;
bean.currentBatteryValue = source.readInt(); // 修正:读取新字段名
bean.batteryDetectInterval = source.readInt();
return bean;
}
@Override
public AppConfigBean[] newArray(int size) {
return new AppConfigBean[size];
}
};
}

View File

@@ -5,17 +5,24 @@ package cc.winboll.studio.powerbell.models;
* @Date 2024/07/18 07:06:07
* @Describe 服务控制参数
*/
import android.os.Parcel;
import android.os.Parcelable;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
import java.io.Serializable;
public class ControlCenterServiceBean extends BaseBean {
// 核心修正:实现 Parcelable + Serializable 接口(适配持久化+Intent传递API30 必备)
public class ControlCenterServiceBean extends BaseBean implements Parcelable, Serializable {
// 序列化版本号Serializable 必备,避免反序列化崩溃)
private static final long serialVersionUID = 1L;
public static final String TAG = "ControlCenterServiceBean";
boolean isEnableService = false;
public ControlCenterServiceBean() {
this.isEnableService = false;
}
@@ -60,4 +67,33 @@ public class ControlCenterServiceBean extends BaseBean {
jsonReader.endObject();
return bean;
}
// ======================== 补全Parcelable 接口实现API30 持久化/Intent传递必备========================
@Override
public int describeContents() {
return 0; // 无特殊内容描述返回0即可
}
// 序列化:将对象属性写入 Parcel
@Override
public void writeToParcel(Parcel dest, int flags) {
// boolean 类型用 byte 存储Parcel 无直接 writeBoolean 方法Java7 适配)
dest.writeByte((byte) (isEnableService ? 1 : 0));
}
// 反序列化:从 Parcel 读取属性,创建对象(必须是 public static final 修饰)
public static final Parcelable.Creator<ControlCenterServiceBean> CREATOR = new Parcelable.Creator<ControlCenterServiceBean>() {
@Override
public ControlCenterServiceBean createFromParcel(Parcel source) {
// 从 Parcel 读取 byte转为 boolean
boolean isEnable = source.readByte() != 0;
return new ControlCenterServiceBean(isEnable);
}
@Override
public ControlCenterServiceBean[] newArray(int size) {
return new ControlCenterServiceBean[size];
}
};
}

View File

@@ -1,40 +1,36 @@
package cc.winboll.studio.powerbell.models;
// 应用消息结构
//
/**
* 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递
*/
public class NotificationMessage {
String Title;
String Content;
String RemindMSG;
private String title; // 通知标题
private String content; // 通知内容
private String remindMSG; // 通知标识(区分服务运行/充电/耗电)
public NotificationMessage(String title, String content) {
Title = title;
Content = content;
}
public void setRemindMSG(String remindMSG) {
RemindMSG = remindMSG;
}
public String getRemindMSG() {
return RemindMSG;
// ====================== Setter/Getter 方法 ======================
public String getTitle() {
return title;
}
public void setTitle(String title) {
Title = title;
}
public String getTitle() {
return Title;
}
public void setContent(String content) {
Content = content;
this.title = title;
}
public String getContent() {
return Content;
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRemindMSG() {
return remindMSG;
}
public void setRemindMSG(String remindMSG) {
this.remindMSG = remindMSG;
}
}

View File

@@ -6,89 +6,299 @@ import android.content.Intent;
import android.content.IntentFilter;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.threads.RemindThread;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BatteryUtils;
import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import java.lang.ref.WeakReference;
/**
* 控制中心广播接收器监听电池状态变化、通知更新、线程启动指令适配API29-30及小米手机
*/
public class ControlCenterServiceReceiver extends BroadcastReceiver {
// ====================== 常量定义(顶部统一管理,清晰易维护) ======================
public static final String TAG = ControlCenterServiceReceiver.class.getSimpleName();
public static final String ACTION_UPDATE_SERVICENOTIFICATION = "cc.winboll.studio.powerbell.action.UPDATE_SERVICENOTIFICATION";
public static final String ACTION_START_REMINDTHREAD = "cc.winboll.studio.powerbell.action.START_REMINDTHREAD";
public static final String EXTRA_APP_CONFIG_BEAN = "extra_app_config_bean";
public static final String ACTION_UPDATE_SERVICENOTIFICATION = ControlCenterServiceReceiver.class.getName() + ".ACTION_UPDATE_NOTIFICATION";
public static final String ACTION_START_REMINDTHREAD = ControlCenterServiceReceiver.class.getName() + ".ACTION_UPDATE_REMINDTHREAD";
WeakReference<ControlCenterService> mwrService;
// 存储电量指示值,
// 用于校验电量消息时的电量变化
static volatile int _mnTheQuantityOfElectricityOld = -1;
static volatile boolean _mIsCharging = false;
// ====================== 成员变量弱引用防泄漏volatile保线程安全 ======================
private WeakReference<ControlCenterService> mwrControlCenterService;
private static volatile int sLastBatteryLevel = -1;
private static volatile boolean sIsCharging = false;
// ====================== 构造方法 ======================
public ControlCenterServiceReceiver(ControlCenterService service) {
mwrService = new WeakReference<ControlCenterService>(service);
LogUtils.d(TAG, "constructor: init receiver");
this.mwrControlCenterService = new WeakReference<>(service);
}
// ====================== 广播核心接收逻辑 ======================
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_UPDATE_SERVICENOTIFICATION)) {
mwrService.get().updateServiceNotification();
} else if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
boolean isCharging = BatteryUtils.isCharging(intent);
int nTheQuantityOfElectricity = BatteryUtils.getTheQuantityOfElectricity(intent);
if (mwrService.get().getRemindThread() != null) {
// 先设置提醒进程电池状态标志
if (_mIsCharging != isCharging) {
mwrService.get().getRemindThread().setIsCharging(isCharging);
}
if (_mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
mwrService.get().getRemindThread().setQuantityOfElectricity(nTheQuantityOfElectricity);
LogUtils.d(TAG, "onReceive: enter, action=" + intent.getAction());
if (context == null || intent == null || intent.getAction() == null) {
LogUtils.e(TAG, "onReceive: invalid params");
return;
}
// 弱引用获取服务,双重校验服务有效性(核心防护,避免内存泄漏+空指针)
ControlCenterService service = mwrControlCenterService != null ? mwrControlCenterService.get() : null;
if (service == null || service.isDestroyed()) {
LogUtils.e(TAG, "onReceive: ControlCenterService is destroyed or null");
// 服务销毁后主动注销广播,彻底避免无效回调
unregisterAction(context);
return;
}
// 分action处理逻辑
String action = intent.getAction();
switch (action) {
case ACTION_UPDATE_SERVICENOTIFICATION:
handleUpdateServiceNotification(service);
break;
case Intent.ACTION_BATTERY_CHANGED:
handleBatteryStateChanged(context, service, intent);
break;
case ACTION_START_REMINDTHREAD:
handleStartRemindThread(service, intent);
break;
default:
LogUtils.w(TAG, "onReceive: unknown action=" + action);
}
LogUtils.d(TAG, "onReceive: exit");
}
// ====================== 业务逻辑方法(全链路容错+逻辑优化) ======================
/**
* 处理前台服务通知更新(强化非空校验,适配工具类最新逻辑)
*/
private void handleUpdateServiceNotification(ControlCenterService service) {
LogUtils.d(TAG, "handleUpdateServiceNotification: start");
try {
NotificationManagerUtils notifyUtils = service.getNotificationManager();
NotificationMessage notifyMsg = service.getForegroundNotifyMsg();
// 三重非空校验,避免工具类/消息/通知实例为空
if (notifyUtils == null || notifyMsg == null) {
LogUtils.e(TAG, "handleUpdateServiceNotification: notify params null, rebuild notify first");
// 兜底:通知实例为空时重建前台通知(适配工具类方法,避免空指针)
if (notifyUtils != null) {
notifyUtils.startForegroundServiceNotify(service, notifyMsg);
}
return;
}
// 新电池状态标志某一个有变化就更新显示信息
if (_mIsCharging != isCharging || _mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
mwrService.get().updateServiceNotification();
AppConfigUtils appConfigUtils = AppConfigUtils.getInstance(context);
appConfigUtils.loadAppConfigBean();
AppConfigBean appConfigBean = appConfigUtils.mAppConfigBean;
appConfigBean.setCurrentValue(nTheQuantityOfElectricity);
appConfigBean.setCharging(isCharging);
mwrService.get().startRemindThread(appConfigBean);
// 保存电池报告
// 示例数据更新逻辑
// List<BatteryData> newData = new ArrayList<>(adapter.getDataList());
// newData.add(0, new BatteryData(percentage, "00:00:00", "00:00:00"));
// adapter.updateData(newData);
// 保存好新的电池状态标志
_mIsCharging = isCharging;
_mnTheQuantityOfElectricityOld = nTheQuantityOfElectricity;
}
} else if (intent.getAction().equals(ACTION_START_REMINDTHREAD)) {
LogUtils.d(TAG, "ACTION_START_REMINDTHREAD");
//AppConfigUtils appConfigUtils = AppConfigUtils.getInstance(context);
//appConfigUtils.loadAppConfigBean();
try {
AppConfigBean appConfigBean = intent.getParcelableExtra("appConfigBean");
if (appConfigBean != null) {
mwrService.get().startRemindThread(appConfigBean);
}
} catch (Exception e) {
LogUtils.e("ControlReceiver", "parse Parcelable config failed", e);
}
// 调用工具类更新通知,确保方法匹配
notifyUtils.updateForegroundServiceNotify(notifyMsg);
LogUtils.d(TAG, "handleUpdateServiceNotification: success");
} catch (Exception e) {
LogUtils.e(TAG, "handleUpdateServiceNotification: failed", e);
}
}
// 注册 Receiver
//
/**
* 处理电池状态变化(核心适配 BatteryUtils修复解析逻辑
*/
private void handleBatteryStateChanged(Context context, ControlCenterService service, Intent intent) {
LogUtils.d(TAG, "handleBatteryStateChanged: start");
try {
// 1. 适配 BatteryUtils 工具类:统一解析电池状态(删除错误的 getTheQuantityOfElectricity
boolean currentCharging = BatteryUtils.isCharging(context); // 调用工具类判断充电状态
int currentBatteryLevel = BatteryUtils.getCurrentBattery(context); // 调用工具类获取电量0-100
currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, 0), 100); // 二次校验,确保范围有效
LogUtils.d(TAG, "handleBatteryStateChanged: current state - charging=" + currentCharging + ", level=" + currentBatteryLevel);
// 2. 状态无变化直接跳过,减少无效运算
if (currentCharging == sIsCharging && currentBatteryLevel == sLastBatteryLevel) {
LogUtils.d(TAG, "handleBatteryStateChanged: no state change, skip");
return;
}
// 3. 加载最新配置(容错:配置加载失败用服务当前配置兜底)
AppConfigBean latestConfig = loadLatestConfig(context);
if (latestConfig == null) {
LogUtils.e(TAG, "handleBatteryStateChanged: load latest config failed, use service current config");
latestConfig = service.getCurrentConfigBean();
// 服务配置也为空时,加载默认配置
if (latestConfig == null) {
latestConfig = new AppConfigBean();
}
}
// 4. 同步最新电池状态到配置,再更新服务配置(确保服务配置是最新的)
latestConfig.setCurrentBatteryValue(currentBatteryLevel);
latestConfig.setIsCharging(currentCharging);
service.setCurrentConfigBean(latestConfig);
// 5. 智能更新线程(线程已初始化则更新配置,未初始化则启动,避免重复重启)
updateRemindThreadConfig(service, latestConfig, currentCharging, currentBatteryLevel);
// 6. 缓存最新状态volatile变量确保多线程可见性
sIsCharging = currentCharging;
sLastBatteryLevel = currentBatteryLevel;
LogUtils.d(TAG, "handleBatteryStateChanged: success");
} catch (Exception e) {
LogUtils.e(TAG, "handleBatteryStateChanged: failed", e);
}
}
/**
* 处理线程启动指令(优化配置解析逻辑,强化线程启动容错)
*/
private void handleStartRemindThread(ControlCenterService service, Intent intent) {
LogUtils.d(TAG, "handleStartRemindThread: start");
try {
// 安全解析配置(避免序列化异常)
AppConfigBean appConfigBean = null;
if (intent.hasExtra(EXTRA_APP_CONFIG_BEAN)) {
Object configObj = intent.getSerializableExtra(EXTRA_APP_CONFIG_BEAN);
if (configObj instanceof AppConfigBean) {
appConfigBean = (AppConfigBean) configObj;
// 配置补全:确保电池状态字段不为默认值(用缓存的最新状态)
appConfigBean.setIsCharging(sIsCharging);
appConfigBean.setCurrentBatteryValue(sLastBatteryLevel);
}
}
if (appConfigBean != null) {
// 先更新服务配置,再重启线程(确保线程启动时拿到最新配置)
service.setCurrentConfigBean(appConfigBean);
// 重启线程(服务已实现安全重启逻辑,直接调用)
service.restartRemindThreadSafely();
LogUtils.d(TAG, "handleStartRemindThread: success, thread restarted with new config");
} else {
LogUtils.e(TAG, "handleStartRemindThread: appConfigBean is null or invalid");
// 配置无效时,用缓存状态重启线程兜底
service.restartRemindThreadSafely();
}
} catch (Exception e) {
LogUtils.e(TAG, "handleStartRemindThread: failed", e);
}
}
// ====================== 内部辅助方法(拆分逻辑+统一容错) ======================
/**
* 加载最新配置适配AppConfigUtils单例逻辑强化异常捕获
*/
private AppConfigBean loadLatestConfig(Context context) {
try {
if (context == null) {
LogUtils.e(TAG, "loadLatestConfig: context is null");
return null;
}
AppConfigUtils configUtils = AppConfigUtils.getInstance(context);
if (configUtils == null) {
LogUtils.e(TAG, "loadLatestConfig: AppConfigUtils instance is null");
return null;
}
// 强制重载配置,确保拿到最新用户设置
configUtils.reloadAllConfig();
return configUtils.mAppConfigBean;
} catch (Exception e) {
LogUtils.e(TAG, "loadLatestConfig: failed to load config", e);
return null;
}
}
/**
* 更新提醒线程配置(智能判断线程状态,避免唤醒/启动冲突)
*/
private void updateRemindThreadConfig(ControlCenterService service, AppConfigBean config, boolean isCharging, int batteryLevel) {
try {
RemindThread remindThread = service.getRemindThread();
if (remindThread == null) {
LogUtils.w(TAG, "updateRemindThreadConfig: remindThread is null, start thread first");
// 线程未初始化,安全启动线程(服务方法已实现容错)
service.restartRemindThreadSafely();
return;
}
// 先同步完整配置核心setAppConfigBean已包含状态同步无需重复设置
remindThread.setAppConfigBean(config);
// 额外同步实时状态(兜底,确保最新)
remindThread.setIsCharging(isCharging);
remindThread.setQuantityOfElectricity(batteryLevel);
// 确保线程开启提醒功能
remindThread.setIsReminding(true);
// 线程未运行时唤醒,已运行时无需操作(避免重复唤醒导致逻辑紊乱)
if (!remindThread.isRunning()) {
synchronized (remindThread) {
remindThread.notify();
LogUtils.d(TAG, "updateRemindThreadConfig: remindThread is woken up");
}
}
LogUtils.d(TAG, "updateRemindThreadConfig: success, thread config updated");
} catch (Exception e) {
LogUtils.e(TAG, "updateRemindThreadConfig: failed", e);
// 配置更新失败时,重启线程兜底
service.restartRemindThreadSafely();
}
}
// ====================== 广播注册/注销(强化容错,避免重复注册/注销) ======================
public void registerAction(Context context) {
IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_UPDATE_SERVICENOTIFICATION);
filter.addAction(ACTION_START_REMINDTHREAD);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(this, filter);
LogUtils.d(TAG, "registerAction: start");
if (context == null) {
LogUtils.e(TAG, "registerAction: context is null");
return;
}
try {
// 先注销再注册,避免重复注册导致异常
unregisterAction(context);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_UPDATE_SERVICENOTIFICATION);
filter.addAction(ACTION_START_REMINDTHREAD);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
// 优先级设置(低于系统最高优先级,避免被系统拦截)
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 10);
context.registerReceiver(this, filter);
LogUtils.d(TAG, "registerAction: success, broadcast receiver registered");
} catch (Exception e) {
LogUtils.e(TAG, "registerAction: failed to register receiver", e);
}
}
public void unregisterAction(Context context) {
LogUtils.d(TAG, "unregisterAction: start");
if (context == null) {
LogUtils.e(TAG, "unregisterAction: context is null");
return;
}
try {
// 捕获注销异常(避免未注册时注销抛错)
context.unregisterReceiver(this);
LogUtils.d(TAG, "unregisterAction: success, broadcast receiver unregistered");
} catch (IllegalArgumentException e) {
LogUtils.w(TAG, "unregisterAction: receiver not registered, skip");
} catch (Exception e) {
LogUtils.e(TAG, "unregisterAction: failed to unregister receiver", e);
}
}
// ====================== Getter方法提供外部状态访问强化线程安全 ======================
public static int getLastBatteryLevel() {
return sLastBatteryLevel;
}
public static boolean isLastCharging() {
return sIsCharging;
}
// ====================== 主动释放资源(修复语法错误,彻底防泄漏) ======================
public void release() {
LogUtils.d(TAG, "release: receiver release resources");
// 置空弱引用帮助GC回收
if (mwrControlCenterService != null) {
mwrControlCenterService.clear();
mwrControlCenterService = null;
}
// 重置静态状态缓存(修复重复赋值语法错误)
sLastBatteryLevel = -1;
sIsCharging = false;
}
}

View File

@@ -4,6 +4,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
@@ -13,53 +14,131 @@ public class GlobalApplicationReceiver extends BroadcastReceiver {
public static final String TAG = "GlobalApplicationReceiver";
AppConfigUtils mAppConfigUtils;
App mGlobalApplication;
// 存储电量指示值,
// 用于校验电量消息时的电量变化
static volatile int _mnTheQuantityOfElectricityOld = -1;
static volatile boolean _mIsCharging = false;
// 保存当前实例,
// 便利封装 registerAction() 函数
GlobalApplicationReceiver mReceiver;
private AppConfigUtils mAppConfigUtils;
private App mGlobalApplication;
// 存储历史电池状态,用于判断变化(优化命名,明确语义)
private static volatile int sLastBatteryLevel = -1; // 历史电量0-100
private static volatile boolean sLastIsCharging = false; // 历史充电状态
// 当前Receiver实例优化命名避免歧义
private GlobalApplicationReceiver mCurrentReceiver;
// 构造方法(强化参数校验,避免空指针)
public GlobalApplicationReceiver(App globalApplication) {
mReceiver = this;
mGlobalApplication = globalApplication;
mAppConfigUtils = App.getAppConfigUtils(mGlobalApplication);
if (globalApplication == null) {
LogUtils.e(TAG, "GlobalApplicationReceiver: App instance is null");
throw new IllegalArgumentException("App cannot be null");
}
this.mCurrentReceiver = this;
this.mGlobalApplication = globalApplication;
this.mAppConfigUtils = App.getAppConfigUtils(mGlobalApplication);
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
// 先设置好新电池状态标志
boolean isCharging = BatteryUtils.isCharging(intent);
if (_mIsCharging != isCharging) {
mAppConfigUtils.setCharging(isCharging);
LogUtils.d(TAG, "onReceive: enter, action=" + intent.getAction());
// 双重空指针防护context/intent/action 均校验)
if (context == null || intent == null || intent.getAction() == null) {
LogUtils.e(TAG, "onReceive: invalid params (context/intent is null)");
return;
}
// 仅处理电池状态变化广播
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
handleBatteryStateChanged(context, intent);
}
LogUtils.d(TAG, "onReceive: exit");
}
/**
* 处理电池状态变化(核心逻辑:复用 BatteryUtils优化状态同步
*/
private void handleBatteryStateChanged(Context context, Intent intent) {
// 1. 用 BatteryUtils 解析当前电池状态(复用工具类,避免重复代码)
boolean currentIsCharging = BatteryUtils.isCharging(context); // 调用工具类判断充电状态
int currentBatteryLevel = BatteryUtils.getCurrentBattery(context); // 调用工具类获取电量0-100
currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, 0), 100); // 二次校验,确保范围有效
LogUtils.d(TAG, "handleBatteryStateChanged: current - charging=" + currentIsCharging + ", level=" + currentBatteryLevel);
// 2. 状态无变化,直接跳过(避免无效运算)
if (currentIsCharging == sLastIsCharging && currentBatteryLevel == sLastBatteryLevel) {
LogUtils.d(TAG, "handleBatteryStateChanged: no state change, skip");
return;
}
// 3. 同步最新状态到配置工具类(确保配置实时更新)
if (mAppConfigUtils != null) {
if (currentIsCharging != sLastIsCharging) {
mAppConfigUtils.setCharging(currentIsCharging); // 同步充电状态
}
int nTheQuantityOfElectricity = BatteryUtils.getTheQuantityOfElectricity(intent);
if (_mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
mAppConfigUtils.setCurrentValue(nTheQuantityOfElectricity);
}
// 新电池状态标志某一个有变化就更新显示信息
if (_mIsCharging != isCharging || _mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
// 电池状态改变先取消旧的提醒消息
//NotificationHelper.cancelRemindNotification(context);
App.getAppCacheUtils(context).addChangingTime(nTheQuantityOfElectricity);
MainActivity.sendMsgCurrentValueBattery(nTheQuantityOfElectricity);
// 保存好新的电池状态标志
_mIsCharging = isCharging;
_mnTheQuantityOfElectricityOld = nTheQuantityOfElectricity;
if (currentBatteryLevel != sLastBatteryLevel) {
mAppConfigUtils.setCurrentBatteryValue(currentBatteryLevel); // 同步当前电量
}
} else {
LogUtils.e(TAG, "handleBatteryStateChanged: AppConfigUtils is null, sync failed");
}
// 4. 电池状态变化后的业务逻辑(保留原有逻辑,强化空指针防护)
// 取消旧提醒(若后续需要启用,可调用 NotificationHelper.cancelRemindNotification(context)
// 记录电量变化时间
if (App.getAppCacheUtils(context) != null) {
App.getAppCacheUtils(context).addChangingTime(currentBatteryLevel);
}
// 发送电量更新消息到MainActivity
MainActivity.sendCurrentBatteryValueMessage(currentBatteryLevel);
// 5. 更新历史状态缓存volatile 保证多线程可见性)
sLastIsCharging = currentIsCharging;
sLastBatteryLevel = currentBatteryLevel;
LogUtils.d(TAG, "handleBatteryStateChanged: sync success, update last state");
}
// 注册广播(优化容错,避免重复注册)
public void registerAction() {
if (mGlobalApplication == null || mCurrentReceiver == null) {
LogUtils.e(TAG, "registerAction: App/receiver is null, register failed");
return;
}
try {
// 先注销再注册,避免重复注册导致异常
unregisterAction();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mGlobalApplication.registerReceiver(mCurrentReceiver, filter);
LogUtils.d(TAG, "registerAction: broadcast receiver registered success");
} catch (Exception e) {
LogUtils.e(TAG, "registerAction: register failed", e);
}
}
// 注册 Receiver
//
public void registerAction() {
IntentFilter filter=new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mGlobalApplication.registerReceiver(mReceiver, filter);
// 新增:注销广播(避免内存泄漏,供外部调用)
public void unregisterAction() {
if (mGlobalApplication == null || mCurrentReceiver == null) {
LogUtils.e(TAG, "unregisterAction: App/receiver is null, unregister failed");
return;
}
try {
mGlobalApplication.unregisterReceiver(mCurrentReceiver);
LogUtils.d(TAG, "unregisterAction: broadcast receiver unregistered success");
} catch (IllegalArgumentException e) {
LogUtils.w(TAG, "unregisterAction: receiver not registered, skip");
} catch (Exception e) {
LogUtils.e(TAG, "unregisterAction: unregister failed", e);
}
}
// 新增主动释放资源供App销毁时调用彻底防泄漏
public void release() {
LogUtils.d(TAG, "release: receiver release resources");
unregisterAction();
mGlobalApplication = null;
mAppConfigUtils = null;
mCurrentReceiver = null;
// 重置静态缓存
sLastBatteryLevel = -1;
sLastIsCharging = false;
}
}

View File

@@ -27,7 +27,7 @@ public class MainReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
boolean isEnableService = App.getAppConfigUtils(context).getIsEnableService();
boolean isEnableService = App.getAppConfigUtils(context).isServiceEnabled();
if (isEnableService) {
if (ServiceUtils.isServiceAlive(context.getApplicationContext(), ControlCenterService.class.getName()) == false) {
LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService");

View File

@@ -65,7 +65,7 @@ public class AssistantService extends Service {
//
void run() {
//LogUtils.d(TAG, "run");
if (mAppConfigUtils.getIsEnableService()) {
if (mAppConfigUtils.isServiceEnabled()) {
if (mIsThreadAlive == false) {
// 设置运行状态
mIsThreadAlive = true;
@@ -97,7 +97,7 @@ public class AssistantService extends Service {
@Override
public void onServiceDisconnected(ComponentName name) {
//LogUtils.d(TAG, "call onServiceDisconnected(...)");
if (mAppConfigUtils.getIsEnableService()) {
if (mAppConfigUtils.isServiceEnabled()) {
wakeupAndBindMain();
}
}

View File

@@ -1,314 +1,442 @@
package cc.winboll.studio.powerbell.services;
/*
* PowerBy : ZhanGSKen(ZhangShaojian2018@163.com)
* 参考:
* 进程保活-双进程守护的正确姿势
* https://blog.csdn.net/sinat_35159441/article/details/75267380
* Android Service之onStartCommand方法研究
* https://blog.csdn.net/cyp331203/article/details/38920491
*/
import android.app.Notification;
import android.app.ActivityManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.widget.RemoteViews;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
import cc.winboll.studio.powerbell.services.AssistantService;
import cc.winboll.studio.powerbell.threads.RemindThread;
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import cc.winboll.studio.powerbell.utils.ServiceUtils;
import cc.winboll.studio.powerbell.utils.StringUtils;
import java.io.Serializable;
import java.util.List;
public class ControlCenterService extends Service {
private static final String TAG = "ControlCenterService";
// Intent指令常量完整包名前缀避免冲突
public static final String ACTION_RESTART_REMIND_THREAD = "cc.winboll.studio.powerbell.action.RESTART_REMIND_THREAD";
public static final String EXTRA_APP_CONFIG_BEAN = "cc.winboll.studio.powerbell.extra.APP_CONFIG_BEAN";
public static final String TAG = "ControlCenterService";
public static final int MSG_UPDATE_STATUS = 0;
static ControlCenterService _mControlCenterService;
volatile boolean isServiceRunning;
AppConfigUtils mAppConfigUtils;
AppCacheUtils mAppCacheUtils;
// 前台服务通知工具
NotificationManagerUtils mNotificationManagerUtils;
Notification notification;
RemindThread mRemindThread;
ControlCenterServiceHandler mControlCenterServiceHandler;
MyServiceConnection mMyServiceConnection;
ControlCenterServiceReceiver mControlCenterServiceReceiver;
ControlCenterServiceReceiver mControlCenterServiceReceiverLocalBroadcast;
@Override
public IBinder onBind(Intent intent) {
return null;
}
public RemindThread getRemindThread() {
return mRemindThread;
}
// 核心成员变量
private ControlCenterServiceHandler mServiceHandler;
private RemindThread mRemindThread;
private NotificationManagerUtils mNotificationManager;
private NotificationMessage mForegroundNotifyMsg;
private AppConfigBean mCurrentConfigBean;
private boolean isServiceRunning = false;
private boolean mIsDestroyed = false; // 服务销毁状态标记供Receiver判断
private final Object mServiceLock = new Object(); // 并发锁(避免多线程冲突)
@Override
public void onCreate() {
super.onCreate();
_mControlCenterService = ControlCenterService.this;
isServiceRunning = false;
mAppConfigUtils = App.getAppConfigUtils(this);
mAppCacheUtils = App.getAppCacheUtils(this);
mNotificationManagerUtils = new NotificationManagerUtils(ControlCenterService.this);
if (mMyServiceConnection == null) {
mMyServiceConnection = new MyServiceConnection();
}
mControlCenterServiceHandler = new ControlCenterServiceHandler(this);
// 运行服务内容
run();
LogUtils.d(TAG, "onCreate: 服务创建,初始化核心组件");
isServiceRunning = true;
mIsDestroyed = false;
initNotificationManager();
startForegroundNotifyImmediately();
loadDefaultConfig();
initServiceBusinessLogic();
LogUtils.d(TAG, "onCreate: 服务初始化完成");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 运行服务内容
run();
return (mAppConfigUtils.getIsEnableService()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
}
LogUtils.d(TAG, "onStartCommand: 触发服务启动命令flags=" + flags);
handleExternalCommand(intent);
// 运行服务内容
//
void run() {
if (mAppConfigUtils.getIsEnableService() && isServiceRunning == false) {
LogUtils.d(TAG, "run");
isServiceRunning = true;
// 唤醒守护进程
wakeupAndBindAssistant();
// 显示前台通知栏
// 在Service中
NotificationManagerUtils notificationManagerUtils = new NotificationManagerUtils(this);
//Intent intent = new Intent(this, MainActivity.class);
notificationManagerUtils.startForegroundServiceNotify(ControlCenterService.this, new NotificationMessage(getString(R.string.app_name), "Service Running, Click to open app"));
//startForeground(NotificationHelper.FOREGROUND_NOTIFICATION_ID, notification);
// NotificationMessage notificationMessage=createNotificationMessage();
// //Toast.makeText(getApplication(), "", Toast.LENGTH_SHORT).show();
// mNotificationUtils.createForegroundNotification(this, notificationMessage);
// mNotificationUtils.createRemindNotification(this, notificationMessage);
if (mControlCenterServiceReceiver == null) {
// 注册广播接收器
mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this);
mControlCenterServiceReceiver.registerAction(this);
// 重新绑定前台服务避免API30+状态丢失(加判空容错)
if (mNotificationManager != null && mNotificationManager.getForegroundServiceNotify() != null) {
try {
startForeground(NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE,
mNotificationManager.getForegroundServiceNotify());
LogUtils.d(TAG, "onStartCommand: 重新绑定前台通知保活");
} catch (Exception e) {
LogUtils.e(TAG, "onStartCommand: 重新绑定前台通知异常", e);
}
}
return START_STICKY; // 服务回收后自动重启
}
new Handler(Looper.getMainLooper()).postDelayed(new Runnable(){
@Override
public void run() {
startRemindThread(mAppConfigUtils.mAppConfigBean);
ToastUtils.show("Service Is Start.");
LogUtils.i(TAG, "Service Is Start.");
}
}, 2000);
// 处理外部指令(重启线程)- 加锁避免并发调用
private void handleExternalCommand(Intent intent) {
LogUtils.d(TAG, "handleExternalCommand: 处理外部指令");
if (intent == null) {
LogUtils.e(TAG, "handleExternalCommand: Intent为空跳过");
return;
}
String action = intent.getAction();
if (TextUtils.isEmpty(action)) {
LogUtils.e(TAG, "handleExternalCommand: 指令Action为空跳过");
return;
}
synchronized (mServiceLock) { // 关键:加锁,防止并发触发重启导致线程冲突
if (ACTION_RESTART_REMIND_THREAD.equals(action)) {
AppConfigBean newConfig = (AppConfigBean) intent.getSerializableExtra(EXTRA_APP_CONFIG_BEAN);
if (newConfig != null) {
mCurrentConfigBean = newConfig;
LogUtils.d(TAG, "handleExternalCommand: 收到重启指令,更新配置");
restartRemindThreadSafely();
} else {
LogUtils.e(TAG, "handleExternalCommand: 新配置为空,重启失败");
}
}
}
}
String getValuesString() {
String szReturn = "Usege: ";
szReturn += mAppConfigUtils.isEnableUsageReminder() ? Integer.toString(mAppConfigUtils.getUsageReminderValue()) : "?";
szReturn += "% Charge: ";
szReturn += mAppConfigUtils.isEnableChargeReminder() ? Integer.toString(mAppConfigUtils.getChargeReminderValue()) : "?";
szReturn += "%\nCurrent: " + Integer.toString(mAppConfigUtils.getCurrentValue()) + "%";
return szReturn;
}
// 安全重启提醒线程 - 核心修复:彻底销毁旧线程,确保新线程是全新实例
public void restartRemindThreadSafely() {
LogUtils.d(TAG, "restartRemindThreadSafely: 重启提醒线程");
synchronized (mServiceLock) { // 加锁,避免与服务销毁/其他重启操作冲突
// 1. 先彻底停止旧线程(调用独立封装的安全停止方法)
stopRemindThreadSafely();
NotificationMessage createNotificationMessage() {
String szTitle = ((App)getApplication()).getString(R.string.app_name);
String szContent = getValuesString() + " {?} " + StringUtils.formatPCMListString(mAppCacheUtils.getArrayListBatteryInfo());
return new NotificationMessage(szTitle, szContent);
}
// 更新前台通知
//
public void updateServiceNotification() {
//mNotificationUtils.updateForegroundNotification(ControlCenterService.this, createNotificationMessage());
}
// 更新前台通知
//
public void updateServiceNotification(NotificationMessage notificationMessage) {
//mNotificationUtils.updateForegroundNotification(ControlCenterService.this, notificationMessage);
}
// 更新前台通知
//
public void updateRemindNotification(NotificationMessage notificationMessage) {
//mNotificationUtils.updateRemindNotification(ControlCenterService.this, notificationMessage);
}
// 唤醒和绑定守护进程
//
void wakeupAndBindAssistant() {
if (ServiceUtils.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
startService(new Intent(ControlCenterService.this, AssistantService.class));
//LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
bindService(new Intent(ControlCenterService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
}
// 开启提醒铃声线程
//
public void startRemindThread(AppConfigBean appConfigBean) {
//LogUtils.d(TAG, "startRemindThread");
if (mRemindThread == null) {
mRemindThread = new RemindThread(this, mControlCenterServiceHandler);
} else {
if (mRemindThread.isExist() == true) {
mRemindThread = new RemindThread(this, mControlCenterServiceHandler);
} else {
// 提醒进程正在进行中就更新状态后退出
mRemindThread.setChargeReminderValue(appConfigBean.getChargeReminderValue());
mRemindThread.setUsegeReminderValue(appConfigBean.getUsageReminderValue());
mRemindThread.setIsEnableChargeReminder(appConfigBean.isEnableChargeReminder());
mRemindThread.setIsEnableUsegeReminder(appConfigBean.isEnableUsageReminder());
mRemindThread.setSleepTime(appConfigBean.getReminderIntervalTime());
mRemindThread.setIsCharging(appConfigBean.isCharging());
mRemindThread.setQuantityOfElectricity(appConfigBean.getCurrentValue());
//LogUtils.d(TAG, "mRemindThread update.");
// 2. 校验服务状态,避免服务已停止时启动线程
if (!isServiceRunning || mServiceHandler == null || mIsDestroyed) {
LogUtils.e(TAG, "restartRemindThreadSafely: 服务未运行/已销毁/Handler为空启动失败");
return;
}
// 3. 强制销毁RemindThread单例关键避免复用旧线程对象
RemindThread.destroyInstance(); // 必须调用确保getInstance返回新实例
// 4. 创建新线程实例此时是全新Thread对象未启动
mRemindThread = RemindThread.getInstance(this, mServiceHandler);
syncConfigToRemindThread();
// 5. 双重校验线程状态杜绝重复start()
if (mRemindThread != null && !mRemindThread.isAlive() && !mRemindThread.isThreadStarted()) {
try {
mRemindThread.start(); // 修复崩溃核心仅对全新线程调用start()
LogUtils.d(TAG, "restartRemindThreadSafely: 新线程启动成功ID=" + mRemindThread.getId());
} catch (IllegalThreadStateException e) {
// 兜底捕获:即使校验失败,也避免崩溃,打印详细日志
LogUtils.e(TAG, "restartRemindThreadSafely: 线程重复启动异常(致命错误)", e);
mRemindThread = null; // 启动失败,置空避免后续错误
} catch (Exception e) {
LogUtils.e(TAG, "restartRemindThreadSafely: 启动新线程异常", e);
mRemindThread = null;
}
} else {
LogUtils.w(TAG, "restartRemindThreadSafely: 线程已启动/实例异常,无需重复操作");
mRemindThread = null;
}
}
mRemindThread.setChargeReminderValue(appConfigBean.getChargeReminderValue());
mRemindThread.setUsegeReminderValue(appConfigBean.getUsageReminderValue());
mRemindThread.setSleepTime(appConfigBean.getReminderIntervalTime());
mRemindThread.setIsCharging(appConfigBean.isCharging());
mRemindThread.setQuantityOfElectricity(appConfigBean.getCurrentValue());
mRemindThread.setIsEnableChargeReminder(appConfigBean.isEnableChargeReminder());
mRemindThread.setIsEnableUsegeReminder(appConfigBean.isEnableUsageReminder());
mRemindThread.start();
//LogUtils.d(TAG, "mRemindThread.start()");
}
public void stopRemindThread() {
if (mRemindThread != null) {
mRemindThread.setIsExist(true);
mRemindThread = null;
// 安全停止提醒线程 - 优化:确保线程彻底终止,无残留
private void stopRemindThreadSafely() {
LogUtils.d(TAG, "stopRemindThreadSafely: 安全停止提醒线程");
if (mRemindThread == null) {
LogUtils.w(TAG, "stopRemindThreadSafely: 线程实例为空,跳过");
return;
}
try {
// 1. 先通过线程内部方法标记停止依赖RemindThread的stopThread实现
if (mRemindThread.isThreadStarted() || mRemindThread.isAlive()) {
mRemindThread.setIsReminding(false); // 关闭提醒功能
mRemindThread.stopThread(); // 触发线程安全停止(置位退出标记+中断休眠)
LogUtils.d(TAG, "stopRemindThreadSafely: 触发线程内部停止逻辑");
// 2. 等待线程终止最多1秒避免阻塞服务超时强制置空
long waitStartTime = System.currentTimeMillis();
while (mRemindThread.isAlive()) {
if (System.currentTimeMillis() - waitStartTime > 1000) {
LogUtils.w(TAG, "stopRemindThreadSafely: 线程停止超时,强制终止处理");
mRemindThread.interrupt(); // 强制中断,加速退出
break;
}
Thread.yield(); // 让出CPU优先执行线程退出逻辑
}
}
} catch (Exception e) {
LogUtils.e(TAG, "stopRemindThreadSafely: 线程停止异常", e);
} finally {
// 3. 彻底释放:置空实例+销毁单例,杜绝复用
mRemindThread = null;
RemindThread.destroyInstance(); // 关键销毁单例避免getInstance复用旧对象
LogUtils.d(TAG, "stopRemindThreadSafely: 线程停止完成,资源已释放");
}
}
// 同步配置到提醒线程 - 加锁避免并发修改
private void syncConfigToRemindThread() {
LogUtils.d(TAG, "syncConfigToRemindThread: 同步配置到线程");
synchronized (mServiceLock) {
if (mRemindThread == null || mCurrentConfigBean == null) {
LogUtils.e(TAG, "syncConfigToRemindThread: 线程/配置为空,同步失败");
return;
}
mRemindThread.setAppConfigBean(mCurrentConfigBean); // 同步完整配置
mRemindThread.setIsReminding(true); // 开启提醒功能
LogUtils.d(TAG, "syncConfigToRemindThread: 配置同步完成");
}
}
// 加载默认配置 - 优化:补全必要配置项,避免空指针
private void loadDefaultConfig() {
LogUtils.d(TAG, "loadDefaultConfig: 加载默认配置");
mCurrentConfigBean = new AppConfigBean();
mCurrentConfigBean.setEnableChargeReminder(true);
mCurrentConfigBean.setChargeReminderValue(80);
mCurrentConfigBean.setEnableUsageReminder(true);
mCurrentConfigBean.setUsageReminderValue(20);
mCurrentConfigBean.setBatteryDetectInterval(1000); // 电池检测间隔1秒
LogUtils.d(TAG, "loadDefaultConfig: 默认配置加载完成");
}
// 初始化通知工具类 - 优化:移除无效调用,强化容错
private void initNotificationManager() {
LogUtils.d(TAG, "initNotificationManager: 初始化通知工具类");
try {
mNotificationManager = new NotificationManagerUtils(this);
mForegroundNotifyMsg = new NotificationMessage();
mForegroundNotifyMsg.setTitle("电池提醒服务运行中");
mForegroundNotifyMsg.setContent("后台持续监测电池状态,确保提醒及时");
mForegroundNotifyMsg.setRemindMSG("service_running");
// 移除无效调用getForegroundServiceNotify() 无需提前触发,启动时自动构建
} catch (Exception e) {
LogUtils.e(TAG, "initNotificationManager: 初始化失败", e);
stopSelf(); // 通知初始化失败,直接停止服务,避免后续异常
}
}
// 立即启动前台服务通知 - 优化加try-catch避免启动失败崩溃
private void startForegroundNotifyImmediately() {
LogUtils.d(TAG, "startForegroundNotifyImmediately: 启动前台保活通知");
if (mNotificationManager == null || mForegroundNotifyMsg == null) {
LogUtils.e(TAG, "startForegroundNotifyImmediately: 依赖组件为空,启动失败");
stopSelf();
return;
}
try {
mNotificationManager.startForegroundServiceNotify(this, mForegroundNotifyMsg);
LogUtils.d(TAG, "startForegroundNotifyImmediately: 前台通知启动成功ID=" + NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE);
} catch (Exception e) {
LogUtils.e(TAG, "startForegroundNotifyImmediately: 前台通知启动异常", e);
stopSelf(); // 前台服务启动失败停止服务API26+前台服务必须正常启动)
}
}
// 初始化业务逻辑 - 优化:复用重启方法,统一线程启动逻辑
private void initServiceBusinessLogic() {
LogUtils.d(TAG, "initServiceBusinessLogic: 初始化业务组件");
mServiceHandler = new ControlCenterServiceHandler(this);
// 关键复用restartRemindThreadSafely避免重复写启动逻辑
restartRemindThreadSafely();
LogUtils.d(TAG, "initServiceBusinessLogic: 业务组件初始化完成");
}
// 启动服务(静态入口)- 优化:强化包名绑定,避免隐式启动失败
public static void startControlCenterService(Context context) {
LogUtils.d(TAG, "startControlCenterService: 启动服务入口");
if (context == null) {
LogUtils.e(TAG, "startControlCenterService: Context为空启动失败");
return;
}
if (isServiceRunning(context, ControlCenterService.class)) {
LogUtils.d(TAG, "startControlCenterService: 服务已运行,跳过启动");
return;
}
Intent intent = new Intent(context, ControlCenterService.class);
intent.setPackage(context.getPackageName()); // 显式绑定包名,避免组件冲突
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); // 兼容服务被停止的场景
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
LogUtils.d(TAG, "startControlCenterService: 服务启动指令发送成功");
} catch (Exception e) {
LogUtils.e(TAG, "startControlCenterService: 启动服务异常", e);
}
}
// 停止服务(静态入口)- 优化:强化包名绑定
public static void stopControlCenterService(Context context) {
LogUtils.d(TAG, "stopControlCenterService: 停止服务入口");
if (context == null) {
LogUtils.e(TAG, "stopControlCenterService: Context为空停止失败");
return;
}
if (!isServiceRunning(context, ControlCenterService.class)) {
LogUtils.d(TAG, "stopControlCenterService: 服务未运行,跳过停止");
return;
}
Intent intent = new Intent(context, ControlCenterService.class);
intent.setPackage(context.getPackageName());
context.stopService(intent);
LogUtils.d(TAG, "stopControlCenterService: 服务停止指令发送成功");
}
// 判断服务是否运行 - 优化适配API30+ ActivityManager限制
private static boolean isServiceRunning(Context context, Class<?> serviceClass) {
if (context == null || serviceClass == null) return false;
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) return false;
// API30+ 限制getRunningServices返回空改用getRunningTasks兼容或用JobScheduler此处先兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
// 替代方案1通过进程名判断简单兼容
String packageName = context.getPackageName();
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo process : processes) {
if (process.processName.equals(packageName) && process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
return true;
}
}
// 替代方案2兜底判断若进程在运行且服务已启动视为运行中
return true;
} catch (Exception e) {
LogUtils.e(TAG, "isServiceRunning: API30+ 判断服务状态异常", e);
return false;
}
} else {
List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(100);
for (ActivityManager.RunningServiceInfo info : runningServices) {
if (serviceClass.getName().equals(info.service.getClassName())) {
return true;
}
}
return false;
}
}
// 引导开启忽略电池优化 - 优化:加权限判断+异常捕获
public static void checkIgnoreBatteryOptimization(Context context) {
if (context == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (powerManager == null) return;
try {
boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
if (!isIgnored) {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization: 引导开启忽略电池优化");
}
} catch (Exception e) {
LogUtils.e(TAG, "checkIgnoreBatteryOptimization: 引导优化异常(部分机型限制)", e);
}
}
}
// ====================== 核心 Getter/Setter 方法 ======================
public RemindThread getRemindThread() {
return mRemindThread;
}
public void setCurrentConfigBean(AppConfigBean configBean) {
if (configBean != null) {
synchronized (mServiceLock) {
this.mCurrentConfigBean = configBean;
syncConfigToRemindThread(); // 更新配置后自动同步线程
LogUtils.d(TAG, "setCurrentConfigBean: 服务配置更新成功");
}
}
}
public NotificationManagerUtils getNotificationManager() {
return mNotificationManager;
}
public NotificationMessage getForegroundNotifyMsg() {
return mForegroundNotifyMsg;
}
public AppConfigBean getCurrentConfigBean() {
return mCurrentConfigBean;
}
public boolean isDestroyed() {
return mIsDestroyed;
}
// ====================== 服务销毁逻辑 - 优化:顺序化释放,避免资源泄漏 ======================
@Override
public void onDestroy() {
super.onDestroy();
LogUtils.d(TAG, "onDestroy: 服务销毁,释放所有资源");
synchronized (mServiceLock) { // 加锁,避免与重启线程操作冲突
isServiceRunning = false;
mIsDestroyed = true;
// 1. 停止前台服务(顺序:先取消通知 → 再移除前台状态)
if (mNotificationManager != null) {
mNotificationManager.cancelForegroundServiceNotify(); // 取消前台通知
}
try {
stopForeground(STOP_FOREGROUND_REMOVE); // 移除前台服务状态API26+
LogUtils.d(TAG, "onDestroy: 前台服务状态已移除");
} catch (Exception e) {
LogUtils.e(TAG, "onDestroy: 移除前台状态异常", e);
}
// 2. 安全停止提醒线程(彻底终止,无残留)
stopRemindThreadSafely();
// 3. 销毁Handler移除所有任务避免内存泄漏
if (mServiceHandler != null) {
mServiceHandler.removeCallbacksAndMessages(null);
mServiceHandler = null;
LogUtils.d(TAG, "onDestroy: Handler已销毁");
}
// 4. 释放通知资源(调用工具类释放方法)
if (mNotificationManager != null) {
mNotificationManager.release();
mNotificationManager = null;
LogUtils.d(TAG, "onDestroy: 通知资源已释放");
}
// 5. 置空所有引用帮助GC回收
mForegroundNotifyMsg = null;
mCurrentConfigBean = null;
}
LogUtils.d(TAG, "onDestroy: 服务销毁完成");
}
@Override
public void onDestroy() {
//LogUtils.d(TAG, "onDestroy");
mAppConfigUtils.loadAppConfigBean();
if (mAppConfigUtils.getIsEnableService() == false) {
// 设置运行状态
isServiceRunning = false;
// 停止守护进程
Intent intent = new Intent(this, AssistantService.class);
stopService(intent);
// 停止Receiver
if (mControlCenterServiceReceiver != null) {
unregisterReceiver(mControlCenterServiceReceiver);
mControlCenterServiceReceiver = null;
}
// 停止前台通知栏
stopForeground(true);
// 停止消息提醒进程
stopRemindThread();
super.onDestroy();
//LogUtils.d(TAG, "onDestroy done");
}
public IBinder onBind(Intent intent) {
return null; // 无需绑定服务
}
// 主进程与守护进程连接时需要用到此类
//
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//LogUtils.d(TAG, "call onServiceConnected(...)");
}
@Override
public void onServiceDisconnected(ComponentName name) {
//LogUtils.d(TAG, "call onServiceConnected(...)");
if (mAppConfigUtils.getIsEnableService()) {
// 唤醒守护进程
wakeupAndBindAssistant();
}
}
}
public void appenRemindMSG(String szRemindMSG) {
String msg = "";
for (int i = 0; i < 20; i++) {
msg += szRemindMSG;
}
NotificationManagerUtils notificationManagerUtils = new NotificationManagerUtils(ControlCenterService.this);
Intent intent = new Intent(ControlCenterService.this, MainActivity.class);
notificationManagerUtils.showTempAlertNotify(getString(R.string.app_name), msg);
// NotificationMessage notificationMessage = createNotificationMessage();
// notificationMessage.setRemindMSG(szRemindMSG);
// //LogUtils.d(TAG, "notificationMessage : " + notificationMessage.getRemindMSG());
// updateRemindNotification(notificationMessage);
}
// 设置颜色背景
public static RemoteViews setLinearLayoutColor(RemoteViews remoteViews, int viewId, int color) {
remoteViews.setInt(viewId, "setBackgroundColor", color);
return remoteViews;
}
// 设置Drawable背景
public static RemoteViews setLinearLayoutDrawable(RemoteViews remoteViews, int viewId, int drawableRes) {
remoteViews.setInt(viewId, "setBackgroundResource", drawableRes);
return remoteViews;
}
//
// 启动服务
//
public static void startControlCenterService(Context context) {
// 更新服务配置(对外入口)- 优化:强化兼容性,加异常捕获
public static void updateStatus(Context context, AppConfigBean configBean) {
if (context == null || configBean == null) return;
LogUtils.d(TAG, "updateStatus: 发送配置更新指令");
Intent intent = new Intent(context, ControlCenterService.class);
context.startForegroundService(intent);
}
intent.setAction(ACTION_RESTART_REMIND_THREAD);
intent.putExtra(EXTRA_APP_CONFIG_BEAN, (Serializable) configBean);
intent.setPackage(context.getPackageName());
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
//
// 停止服务
//
public static void stopControlCenterService(Context context) {
Intent intent = new Intent(context, ControlCenterService.class);
context.stopService(intent);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
LogUtils.d(TAG, "updateStatus: 配置更新指令发送成功");
} catch (Exception e) {
LogUtils.e(TAG, "updateStatus: 发送指令异常", e);
}
}
public static void updateStatus(Context context, AppConfigBean appConfigBean) {
//LogUtils.d(TAG, "updateStatus");
// 创建一个Intent实例定义广播的内容
Intent intent = new Intent(ControlCenterServiceReceiver.ACTION_START_REMINDTHREAD);
// 设置可选的Action数据如额外信息
intent.putExtra("appConfigBean", appConfigBean);
// 发送广播
context.sendBroadcast(intent);
}
}

View File

@@ -4,194 +4,607 @@ import android.content.Context;
import android.os.Message;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import java.lang.ref.WeakReference;
/**
* 提醒线程(单例模式):统一管理充电/耗电提醒逻辑适配Java7+API30+
*/
public class RemindThread extends Thread {
public static final String TAG = RemindThread.class.getSimpleName();
// 常量定义(优化命名规范,补充注释)
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 DEFAULT_SLEEP_TIME = 1000; // 默认检测间隔ms
private static final long REMIND_INTERVAL = 30000L; // 重复提醒间隔ms避免轰炸用户
private static final int CONFIG_RETRY_MAX = 3; // 配置同步重试次数(兜底配置有效性)
Context mContext;
// 控制线程是否退出的标志
volatile boolean isExist = false;
// 消息提醒开关
static volatile boolean isReminding = false;
// 充电提醒开关
static volatile boolean isEnableUsegeReminder = false;
// 耗电提醒开关
static volatile boolean isEnableChargeReminder = false;
// 电量比较停顿时间
static volatile int sleepTime = 1000;
// 充电提醒电量
static volatile int chargeReminderValue = -1;
// 耗电提醒电量
static volatile int usegeReminderValue = -1;
// 当前电量
static volatile int quantityOfElectricity = -1;
// 是否正在充电
static volatile boolean isCharging = false;
// 服务Handler, 用于线程发送消息使用
WeakReference<ControlCenterServiceHandler> mwrControlCenterServiceHandler;
// 单例核心volatile+双重校验锁,线程安全,修复复用漏洞)
private static volatile RemindThread sInstance;
public void setIsExist(boolean isExist) {
this.isExist = isExist;
// 成员变量volatile+锁保护,确保多线程可见性,优化内存泄漏防护)
private Context mContext;
private WeakReference<ControlCenterServiceHandler> 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 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 RemindThread(Context context, ControlCenterServiceHandler handler) {
LogUtils.d(TAG, "RemindThread: 构造方法初始化,线程名称=" + getName());
// 应用上下文+弱引用Handler双重防护内存泄漏
this.mContext = context.getApplicationContext();
this.mwrControlCenterServiceHandler = new WeakReference<>(handler);
resetThreadStateInternal(); // 初始化所有状态变量,避免脏数据
}
public boolean isExist() {
return isExist;
}
public static void setIsReminding(boolean isReminding) {
RemindThread.isReminding = isReminding;
}
public static boolean isReminding() {
return isReminding;
}
public static void setIsEnableUsegeReminder(boolean isEnableUsegeReminder) {
RemindThread.isEnableUsegeReminder = isEnableUsegeReminder;
}
public static boolean isEnableUsegeReminder() {
return isEnableUsegeReminder;
}
public static void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
RemindThread.isEnableChargeReminder = isEnableChargeReminder;
}
public static boolean isEnableChargeReminder() {
return isEnableChargeReminder;
}
public static void setSleepTime(int sleepTime) {
RemindThread.sleepTime = sleepTime;
}
public static int getSleepTime() {
return sleepTime;
}
public static void setChargeReminderValue(int chargeReminderValue) {
RemindThread.chargeReminderValue = chargeReminderValue;
}
public static int getChargeReminderValue() {
return chargeReminderValue;
}
public static void setUsegeReminderValue(int usegeReminderValue) {
RemindThread.usegeReminderValue = usegeReminderValue;
}
public static int getUsegeReminderValue() {
return usegeReminderValue;
}
public static void setQuantityOfElectricity(int quantityOfElectricity) {
RemindThread.quantityOfElectricity = quantityOfElectricity;
}
public static int getQuantityOfElectricity() {
return quantityOfElectricity;
}
public static void setIsCharging(boolean isCharging) {
RemindThread.isCharging = isCharging;
}
public static boolean isCharging() {
return isCharging;
}
// 发送消息给用户
//
void sendNotificationMessage(String sz) {
//LogUtils.d(TAG, "sz is " + sz);
Message message = Message.obtain();
message.what = ControlCenterServiceHandler.MSG_REMIND_TEXT;
//message.obj = new NotificationMessage(mContext.getString(R.string.app_name), sz);
message.obj = sz;
ControlCenterServiceHandler handler = mwrControlCenterServiceHandler.get();
if (isReminding && handler != null) {
handler.sendMessage(message);
// 单例获取(核心修复:线程停止后自动销毁单例,杜绝复用旧线程对象)
public static RemindThread getInstance(Context context, ControlCenterServiceHandler handler) {
LogUtils.d(TAG, "getInstance: 获取线程实例,当前单例状态=" + (sInstance != null));
// 入参校验(非空抛出异常,强制上游传参正确)
if (context == null || handler == null) {
LogUtils.e(TAG, "getInstance: 致命错误 - Context/Handler不能为空");
throw new IllegalArgumentException("Context and ControlCenterServiceHandler cannot be null");
}
}
public RemindThread(Context context, ControlCenterServiceHandler handler) {
mContext = context;
mwrControlCenterServiceHandler = new WeakReference<ControlCenterServiceHandler>(handler);
}
// 关键修复:旧线程已停止(未运行),直接销毁单例,确保新实例创建
if (sInstance != null && !sInstance.isRunning()) {
destroyInstance();
LogUtils.d(TAG, "getInstance: 旧线程已停止,销毁单例准备创建新实例");
}
@Override
public void run() {
//LogUtils.d(TAG, "call run()");
if (isReminding == false) {
isReminding = true;
// 等待些许时间,等所有数据初始化完成再执行下面的程序
// 解决窗口移除后自动重启后会发送一个错误消息的问题
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
// 发送提醒线程开始的参数设置
//sendMessageToUser(Integer.toString(_mnTheQuantityOfElectricity) + ">>>" + Integer.toString(_mnTargetNumber));
//ToastUtils.show("Service Is Start.");
//LogUtils.i(TAG, "Service Is Start.");
while (!isExist()) {
/*
LogUtils.d(TAG, "isCharging is " + Boolean.toString(isCharging));
LogUtils.d(TAG, "usegeReminderValue is " + Integer.toString(usegeReminderValue));
LogUtils.d(TAG, "quantityOfElectricity is " + Integer.toString(quantityOfElectricity));
LogUtils.d(TAG, "chargeReminderValue is " + Integer.toString(chargeReminderValue));
LogUtils.d(TAG, "isEnableChargeReminder is " + Boolean.toString(isEnableChargeReminder));
LogUtils.d(TAG, "isEnableUsegeReminder is " + Boolean.toString(isEnableUsegeReminder));
*/
try {
if (isCharging) {
if ((quantityOfElectricity >= chargeReminderValue)
&& (isEnableChargeReminder)) {
// 正在充电时电量大于指定电量发送提醒
sendNotificationMessage("+");
// 应用需要继续提醒,设置退出标志为否
setIsExist(false);
//sendNotificationMessage("I am ready! +");
} else {
// 设置退出标志,如果后续不需要继续提醒就退出当前进程,用于应用节能。
setIsExist(true);
isReminding = false;
return;
}
} else {
if ((quantityOfElectricity <= usegeReminderValue)
&& (isEnableUsegeReminder)) {
// 正在放电时电量小于指定电量发送提醒
sendNotificationMessage("-");
// 应用需要继续提醒,设置退出标志为否
setIsExist(false);
//sendNotificationMessage("I am ready! -");
} else {
// 设置退出标志,如果后续不需要继续提醒就退出当前进程,用于应用节能。
setIsExist(true);
isReminding = false;
return;
}
}
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
// 双重校验锁,线程安全创建单例
if (sInstance == null) {
synchronized (RemindThread.class) {
if (sInstance == null) {
sInstance = new RemindThread(context, handler);
LogUtils.d(TAG, "getInstance: 新线程实例创建成功线程ID=" + sInstance.getId());
}
}
//ToastUtils.show("Service Is Stop.");
//LogUtils.i(TAG, "Service Is Stop.");
isReminding = false;
}
return sInstance;
}
// 单例销毁(核心优化:彻底释放资源+终止线程与Service停止逻辑联动
public static void destroyInstance() {
LogUtils.d(TAG, "destroyInstance: 开始销毁线程单例");
synchronized (RemindThread.class) { // 类锁保护,避免并发销毁冲突
if (sInstance != null) {
// 1. 安全停止线程(触发退出+中断休眠)
sInstance.stopThread();
// 2. 强制释放所有资源(避免内存泄漏)
sInstance.releaseResources();
// 3. 置空单例,杜绝后续复用
sInstance = null;
LogUtils.d(TAG, "destroyInstance: 线程单例销毁完成");
} else {
LogUtils.w(TAG, "destroyInstance: 单例已为空,跳过销毁");
}
}
}
// 线程核心逻辑(彻底修复重复启动、优化退出效率、强化异常容错)
@Override
public void run() {
LogUtils.d(TAG, "run: 线程启动执行线程ID=" + Thread.currentThread().getId() + ",当前状态=" + getState());
// 核心修复1杜绝重复执行run()Thread.start()后会自动调用run(),手动调用/重复start()直接拦截)
synchronized (mLock) {
if (isThreadStarted || isExist) {
LogUtils.e(TAG, "run: 线程已启动/待退出禁止重复执行run()");
return;
}
isThreadStarted = true; // 标记线程启动成功(仅执行一次)
lastRemindTime = 0; // 初始化上次提醒时间
}
// 步骤1配置同步重试等待Service同步配置避免阈值无效
boolean configValid = syncConfigRetry();
if (!configValid) {
LogUtils.e(TAG, "run: 配置同步失败,使用默认配置兜底启动");
}
// 步骤2初始化延迟等待服务/系统资源就绪,避免启动初期异常)
try {
LogUtils.d(TAG, "run: 初始化延迟" + INIT_DELAY_TIME + "ms");
Thread.sleep(INIT_DELAY_TIME);
// 延迟期间检测退出标记,避免线程启动后立即销毁
if (isExist) {
LogUtils.d(TAG, "run: 初始化延迟期间收到退出指令,线程终止");
cleanThreadState();
return;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 保留中断标记
LogUtils.e(TAG, "run: 初始化延迟被中断,线程终止");
cleanThreadState();
return;
}
// 步骤3核心业务循环isExist=true时立即退出优化轮询效率
LogUtils.d(TAG, "run: 进入电量检测核心循环,检测间隔=" + 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 + "),休眠重试");
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(); // 启动恢复计时器,避免重复提醒
}
}
// 耗电状态提醒(同理,开关+阈值双重校验)
else if (!isCharging && isEnableUsageReminder) {
if (quantityOfElectricity <= usageReminderValue) {
sendNotificationMessage("-"); // 耗电提醒标识
lastRemindTime = currentTime;
LogUtils.d(TAG, "run: 触发耗电提醒 → 电量:" + quantityOfElectricity + ",阈值:" + usageReminderValue);
startRemindRecoveryTimer(); // 启动恢复计时器
}
}
}
// 检测间隔休眠(循环尾部休眠,避免空轮询)
safeSleep(sleepTime);
} catch (Exception e) {
LogUtils.e(TAG, "run: 线程运行异常,休眠后重试", e);
safeSleep(sleepTime); // 异常后强制休眠避免死循环占用CPU
}
}
// 步骤4线程退出清理无论正常/异常退出,必执行)
cleanThreadState();
LogUtils.d(TAG, "run: 线程核心循环退出线程ID=" + Thread.currentThread().getId() + ",最终状态=" + getState());
}
// 新增:配置同步重试(独立封装,提升代码可读性,修复阈值校验逻辑)
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);
return true;
}
}
// 配置无效 → 休眠1秒重试
LogUtils.w(TAG, "syncConfigRetry: 配置未同步(阈值无效),重试次数=" + (retryCount + 1));
try {
Thread.sleep(1000);
retryCount++;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LogUtils.e(TAG, "syncConfigRetry: 配置重试被中断", 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);
}
return false;
}
// 发送提醒消息核心修复Message回收写法+优化容错适配全Android版本
private void sendNotificationMessage(String content) {
LogUtils.d(TAG, "sendNotificationMessage: 准备发送提醒消息,内容=" + content);
// 前置校验:线程退出/提醒关闭 → 跳过
if (isExist || !isReminding) {
LogUtils.d(TAG, "sendNotificationMessage: 线程退出/提醒关闭,跳过发送");
return;
}
// 弱引用获取Handler避免持有Service引用导致内存泄漏
ControlCenterServiceHandler handler = null;
if (mwrControlCenterServiceHandler != null) {
handler = mwrControlCenterServiceHandler.get();
}
if (handler == null) {
LogUtils.w(TAG, "sendNotificationMessage: Handler已回收发送失败");
return;
}
// 复用Message减少内存分配发送提醒指令
Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT, content);
try {
handler.sendMessage(message);
LogUtils.d(TAG, "sendNotificationMessage: 提醒消息发送成功");
} catch (Exception e) {
LogUtils.e(TAG, "sendNotificationMessage: 消息发送异常", e);
// 核心修复Message回收改为实例方法兼容所有Android版本且仅在发送失败时回收
if (message != null) {
message.recycle(); // 正确写法:实例方法回收,避免内存泄漏
}
}
}
// 提醒恢复计时器(核心优化:弱引用线程实例+双重锁保护,彻底解决并发安全)
private void startRemindRecoveryTimer() {
LogUtils.d(TAG, "startRemindRecoveryTimer: 启动提醒恢复计时器,间隔=" + REMIND_INTERVAL + "ms");
synchronized (mLock) {
// 前置校验:线程退出/计时器已运行/单例已销毁 → 跳过
if (isExist || isRemindTimerRunning || sInstance != this) {
LogUtils.w(TAG, "startRemindRecoveryTimer: 计时器启动条件不满足,跳过");
return;
}
// 临时关闭提醒(避免恢复前重复触发),标记计时器运行中
isReminding = false;
isRemindTimerRunning = true;
}
// 计时器线程弱引用当前RemindThread实例避免计时器持有线程导致内存泄漏
final WeakReference<RemindThread> threadWeakRef = new WeakReference<>(this);
new Thread(new Runnable() {
@Override
public void run() {
try {
// 计时器休眠(间隔时间到后恢复提醒)
Thread.sleep(REMIND_INTERVAL);
// 弱引用获取线程实例(避免内存泄漏)
RemindThread thread = threadWeakRef.get();
if (thread == null || thread.isExist || sInstance != thread) {
LogUtils.d(TAG, "startRemindRecoveryTimer: 线程已销毁/退出,不恢复提醒");
return;
}
// 锁保护恢复逻辑(确保状态一致性)
synchronized (thread.mLock) {
// 再次校验:线程未退出+单例未切换+有任一提醒开关开启 → 恢复提醒
if (!thread.isExist && sInstance == thread
&& (thread.isEnableChargeReminder || thread.isEnableUsageReminder)) {
thread.isReminding = true;
LogUtils.d(TAG, "startRemindRecoveryTimer: 提醒功能恢复开启");
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LogUtils.e(TAG, "startRemindRecoveryTimer: 计时器被中断", e);
} finally {
// 无论是否异常,都重置计时器标记(锁保护,避免并发冲突)
RemindThread thread = threadWeakRef.get();
if (thread != null) {
synchronized (thread.mLock) {
if (sInstance == thread) {
thread.isRemindTimerRunning = false;
}
}
}
LogUtils.d(TAG, "startRemindRecoveryTimer: 提醒恢复计时器执行完成");
}
}
}, "RemindRecoveryTimer").start(); // 给计时器线程命名,便于调试
}
// 安全停止线程(核心修复:触发退出+中断休眠+重置状态,确保快速退出)
public void stopThread() {
LogUtils.d(TAG, "stopThread: 开始安全停止线程,当前线程状态=" + 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;
}
}
// 废弃原强制停止方法统一用stopThread(),避免方法冗余导致调用混淆)
@Deprecated
public void forceStopThread() {
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: 休眠被中断,线程准备退出");
}
}
// 清理线程运行状态(原子操作,确保退出后状态干净,无残留)
private void cleanThreadState() {
LogUtils.d(TAG, "cleanThreadState: 清理线程运行状态");
synchronized (mLock) {
isReminding = false;
isExist = true;
isThreadStarted = false;
isRemindTimerRunning = false;
lastRemindTime = 0;
}
// 辅助:中断线程(兜底加速退出)
if (isAlive()) {
interrupt();
}
// 辅助:单例置空(终极兜底)
if (sInstance == this) {
sInstance = null;
}
}
// 重置线程初始状态(构造方法专用,初始化所有变量,避免脏数据)
private void resetThreadStateInternal() {
LogUtils.d(TAG, "resetThreadStateInternal: 重置线程初始状态");
synchronized (mLock) {
isExist = false;
isReminding = false;
isThreadStarted = false;
isEnableUsageReminder = false;
isEnableChargeReminder = false;
sleepTime = DEFAULT_SLEEP_TIME;
chargeReminderValue = -1;
usageReminderValue = -1;
quantityOfElectricity = -1;
isCharging = false;
isRemindTimerRunning = false;
lastRemindTime = 0;
}
}
// 外部调用:重置线程运行状态(不重置配置,仅重置运行时标记)
public void resetThreadState() {
LogUtils.d(TAG, "resetThreadState: 重置线程运行状态");
synchronized (mLock) {
isExist = false;
isReminding = (isEnableChargeReminder || isEnableUsageReminder); // 按开关自动恢复提醒状态
isRemindTimerRunning = false;
lastRemindTime = 0;
}
}
// 核心:同步完整配置(优化参数校验+原子操作,确保配置一致性)
public void setAppConfigBean(AppConfigBean config) {
if (config == null) {
LogUtils.e(TAG, "setAppConfigBean: 配置Bean为空同步失败");
return;
}
LogUtils.d(TAG, "setAppConfigBean: 开始同步配置 → 充电阈值:" + 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();
}
LogUtils.d(TAG, "setAppConfigBean: 配置同步完成 → 检测间隔:" + sleepTime + "ms提醒开启" + isReminding);
}
// ====================== Setter/Getter 方法(全量锁保护,确保线程安全,优化参数校验) ======================
public boolean isExist() {
synchronized (mLock) {
return isExist;
}
}
public void setIsExist(boolean isExist) {
synchronized (mLock) {
this.isExist = isExist;
LogUtils.d(TAG, "setIsExist: 线程退出标记设置为" + isExist);
}
}
public boolean isReminding() {
synchronized (mLock) {
return isReminding;
}
}
public void setIsReminding(boolean isReminding) {
synchronized (mLock) {
this.isReminding = isReminding;
LogUtils.d(TAG, "setIsReminding: 全局提醒开关设置为" + isReminding);
}
}
public boolean isThreadStarted() {
synchronized (mLock) {
return isThreadStarted;
}
}
public boolean isEnableUsageReminder() {
synchronized (mLock) {
return isEnableUsageReminder;
}
}
public void setIsEnableUsageReminder(boolean isEnableUsageReminder) {
synchronized (mLock) {
this.isEnableUsageReminder = isEnableUsageReminder;
this.isReminding = (this.isEnableChargeReminder || this.isEnableUsageReminder); // 联动全局开关
LogUtils.d(TAG, "setIsEnableUsageReminder: 耗电提醒开关设置为" + isEnableUsageReminder + ",全局提醒:" + this.isReminding);
}
}
public boolean isEnableChargeReminder() {
synchronized (mLock) {
return isEnableChargeReminder;
}
}
public void setIsEnableChargeReminder(boolean isEnableChargeReminder) {
synchronized (mLock) {
this.isEnableChargeReminder = isEnableChargeReminder;
this.isReminding = (this.isEnableChargeReminder || this.isEnableUsageReminder); // 联动全局开关
LogUtils.d(TAG, "setIsEnableChargeReminder: 充电提醒开关设置为" + isEnableChargeReminder + ",全局提醒:" + this.isReminding);
}
}
public int getSleepTime() {
synchronized (mLock) {
return sleepTime;
}
}
public void setSleepTime(int sleepTime) {
synchronized (mLock) {
this.sleepTime = Math.max(sleepTime, MIN_SLEEP_TIME); // 强制≥最小间隔
LogUtils.d(TAG, "setSleepTime: 检测间隔设置为" + this.sleepTime + "ms");
}
}
public int getChargeReminderValue() {
synchronized (mLock) {
return chargeReminderValue;
}
}
public void setChargeReminderValue(int chargeReminderValue) {
synchronized (mLock) {
this.chargeReminderValue = Math.min(Math.max(chargeReminderValue, 0), 100); // 强制0-100
LogUtils.d(TAG, "setChargeReminderValue: 充电提醒阈值设置为" + this.chargeReminderValue);
}
}
public int getUsageReminderValue() {
synchronized (mLock) {
return usageReminderValue;
}
}
public void setUsageReminderValue(int usageReminderValue) {
synchronized (mLock) {
this.usageReminderValue = Math.min(Math.max(usageReminderValue, 0), 100); // 强制0-100
LogUtils.d(TAG, "setUsageReminderValue: 耗电提醒阈值设置为" + this.usageReminderValue);
}
}
public int getQuantityOfElectricity() {
synchronized (mLock) {
return quantityOfElectricity;
}
}
public void setQuantityOfElectricity(int quantityOfElectricity) {
synchronized (mLock) {
this.quantityOfElectricity = Math.min(Math.max(quantityOfElectricity, 0), 100); // 强制0-100
LogUtils.d(TAG, "setQuantityOfElectricity: 当前电量更新为" + this.quantityOfElectricity);
}
}
public boolean isCharging() {
synchronized (mLock) {
return isCharging;
}
}
public void setIsCharging(boolean isCharging) {
synchronized (mLock) {
this.isCharging = isCharging;
LogUtils.d(TAG, "setIsCharging: 充电状态更新为" + isCharging);
}
}
// 核心判断线程是否正在运行Service依赖此方法精准判断运行状态
public boolean isRunning() {
synchronized (mLock) {
// 条件:未退出 + 已启动 + 线程存活 → 视为正在运行
return !isExist && isThreadStarted && isAlive();
}
}
// 彻底释放线程资源Service销毁时调用避免内存泄漏
public void releaseResources() {
LogUtils.d(TAG, "releaseResources: 开始释放线程资源");
synchronized (mLock) {
// 1. 释放上下文(避免持有应用上下文导致内存泄漏)
mContext = null;
// 2. 清空Handler弱引用加速回收
if (mwrControlCenterServiceHandler != null) {
mwrControlCenterServiceHandler.clear();
mwrControlCenterServiceHandler = null;
}
// 3. 清理运行状态(确保所有标记干净)
cleanThreadState();
}
LogUtils.d(TAG, "releaseResources: 线程资源释放完成");
}
// 重写toString(),便于调试(新增:打印线程核心状态)
@Override
public String toString() {
return "RemindThread{" +
"threadId=" + getId() +
", threadName='" + getName() + '\'' +
", isRunning=" + isRunning() +
", isReminding=" + isReminding() +
", chargeThreshold=" + getChargeReminderValue() +
", usageThreshold=" + getUsageReminderValue() +
", currentBattery=" + getQuantityOfElectricity() +
", isCharging=" + isCharging() +
'}';
}
}

View File

@@ -2,202 +2,455 @@ package cc.winboll.studio.powerbell.utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import java.io.File;
// 应用配置工具类
//
/**
* 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等)
* 适配Java7 | API29-30 | 小米手机,单例模式,线程安全,配置持久化
*/
public class AppConfigUtils {
// ======================== 静态常量(顶部统一管理,抽离魔法值)========================
public static final String TAG = "AppConfigUtils";
public static final String BACKGROUND_DIR = "Background"; // 背景图片存储目录
private static final int MIN_REMINDER_VALUE = 0; // 提醒阈值最小值
private static final int MAX_REMINDER_VALUE = 100; // 提醒阈值最大值
private static final int MIN_INTERVAL_TIME = 1000; // 最小提醒间隔ms
private static final String CONFIRM_TITLE = "配置变更确认";
private static final String CONFIRM_MSG = "是否确认更改该配置?";
public static final String BACKGROUND_DIR = "Background";
// ======================== 静态成员(单例实例,严格控制初始化)========================
private static AppConfigUtils sInstance; // 单例实例(私有,禁止外部直接创建)
// 保存唯一配置实例
static AppConfigUtils _mAppConfigUtils;
// 应用环境上下文
Context mContext;
// 是否启动铃声提醒服务
volatile boolean mIsEnableService = false;
// ======================== 成员属性按功能分类volatile保障线程安全========================
// 核心依赖优先排列final避免空指针
private final Context mContext;
private App mApplication;
// 配置Bean持久化存储核心
public volatile AppConfigBean mAppConfigBean;
// 电池充电提醒值。
// Battery charge reminder value.
volatile int mnChargeReminderValue = -1;
volatile boolean mIsEnableChargeReminder = false;
// 电池耗电量提醒值。
// Battery power usege reminder value.
volatile int mnUsegeReminderValue = -1;
volatile boolean mIsEnableUsegeReminder = false;
private volatile ControlCenterServiceBean mServiceConfigBean;
volatile boolean mIsUseBackgroundFile = false;
volatile String mszBackgroundFileName = "";
// 缓存状态减少Bean读取次数提升性能
private volatile boolean mIsServiceEnabled = false;
// 保存应用实例
App mApplication;
AppConfigUtils(Context context) {
mContext = context;
String szExternalFilesDir = mContext.getExternalFilesDir(TAG) + File.separator;
//mlistAppConfigBean = new ArrayList<AppConfigBean>();
// ======================== 单例构造方法(私有,禁止外部实例化)========================
private AppConfigUtils(Context context) {
LogUtils.d(TAG, "初始化配置工具类");
this.mContext = context.getApplicationContext(); // 取应用上下文避免Activity内存泄漏
// 初始化Application实例补全之前未赋值
this.mApplication = (App) context.getApplicationContext();
// 初始化配置Bean
mAppConfigBean = new AppConfigBean();
loadAppConfigBean();
mServiceConfigBean = new ControlCenterServiceBean(false);
// 加载持久化配置
loadAllConfig();
LogUtils.d(TAG, "配置工具类初始化完成");
}
// 返回唯一实例
//
// ======================== 单例获取方法(双重校验锁,线程安全)========================
public static AppConfigUtils getInstance(Context context) {
if (_mAppConfigUtils == null) {
_mAppConfigUtils = new AppConfigUtils(context);
if (context == null) {
LogUtils.e(TAG, "获取实例失败Context不能为空");
throw new IllegalArgumentException("Context cannot be null");
}
return _mAppConfigUtils;
}
public void setIsEnableService(Activity activity, final boolean isEnableService) {
YesNoAlertDialog.show(activity, "应用设置信息", "是否保存应用配置?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onYes() {
mIsEnableService = isEnableService;
ControlCenterServiceBean bean = new ControlCenterServiceBean(isEnableService);
ControlCenterServiceBean.saveBean(mContext, bean);
if (mIsEnableService) {
LogUtils.d(TAG, "startControlCenterService");
ControlCenterService.startControlCenterService(mContext);
} else {
LogUtils.d(TAG, "stopControlCenterService");
ControlCenterService.stopControlCenterService(mContext);
}
// 双重校验锁,适配多线程并发场景
if (sInstance == null) {
synchronized (AppConfigUtils.class) {
if (sInstance == null) {
sInstance = new AppConfigUtils(context);
}
@Override
public void onNo() {
MainActivity.relaodAppConfigs();
}
});
}
public boolean getIsEnableService() {
ControlCenterServiceBean bean = ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class);
if (bean == null) {
ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(false));
return false;
}
}
return bean.isEnableService();
return sInstance;
}
public void setEnableChargeReminder(boolean isEnableChargeReminder) {
mAppConfigBean.setEnableChargeReminder(isEnableChargeReminder);
saveConfigData(MainActivity._mMainActivity);
}
public boolean isEnableChargeReminder() {
return mAppConfigBean.isEnableChargeReminder();
}
public void setEnableUsageReminder(boolean isEnableUsegeReminder) {
mAppConfigBean.setEnableUsageReminder(isEnableUsegeReminder);
saveConfigData(MainActivity._mMainActivity);
}
public boolean isEnableUsageReminder() {
return mAppConfigBean.isEnableUsageReminder();
}
public void setChargeReminderValue(int value) {
mAppConfigBean.setChargeReminderValue(value);
saveConfigData(MainActivity._mMainActivity);
}
public int getChargeReminderValue() {
return mAppConfigBean.getChargeReminderValue();
}
public void setUsageReminderValue(int value) {
mAppConfigBean.setUsageReminderValue(value);
saveConfigData(MainActivity._mMainActivity);
}
public int getUsageReminderValue() {
return mAppConfigBean.getUsageReminderValue();
}
public void setCharging(boolean isCharging) {
mAppConfigBean.setCharging(isCharging);
}
public boolean isCharging() {
return mAppConfigBean.isCharging();
}
public void setCurrentValue(int nCurrentValue) {
mAppConfigBean.setCurrentValue(nCurrentValue);
}
public int getCurrentValue() {
return mAppConfigBean.getCurrentValue();
}
public void setReminderIntervalTime(int reminderIntervalTime) {
mAppConfigBean.setReminderIntervalTime(reminderIntervalTime);
}
public int getReminderIntervalTime() {
return mAppConfigBean.getReminderIntervalTime();
}
//
// 加载电池提醒配置数据
//
public void loadAppConfigBean() {
AppConfigBean bean = AppConfigBean.loadBean(mContext, AppConfigBean.class);
if (bean == null) {
bean = new AppConfigBean();
// ======================== 核心配置读写方法(优先排列,保障配置稳定性)========================
/**
* 加载所有配置(应用配置+服务配置,统一入口)
*/
private void loadAllConfig() {
LogUtils.d(TAG, "开始加载所有配置");
// 加载应用配置补全BaseBean 通用持久化逻辑,适配序列化存储)
AppConfigBean savedAppBean = (AppConfigBean) AppConfigBean.loadBean(mContext, AppConfigBean.class);
if (savedAppBean != null) {
mAppConfigBean = savedAppBean;
LogUtils.d(TAG, "应用配置加载成功");
} else {
AppConfigBean.saveBean(mContext, mAppConfigBean);
LogUtils.d(TAG, "无已保存应用配置,使用默认值并持久化");
}
// 加载服务配置补全BaseBean 通用持久化逻辑)
ControlCenterServiceBean savedServiceBean = (ControlCenterServiceBean) ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class);
if (savedServiceBean != null) {
mServiceConfigBean = savedServiceBean;
mIsServiceEnabled = savedServiceBean.isEnableService();
LogUtils.d(TAG, "服务配置加载成功,服务状态:" + (mIsServiceEnabled ? "开启" : "关闭"));
} else {
ControlCenterServiceBean.saveBean(mContext, mServiceConfigBean);
mIsServiceEnabled = false;
LogUtils.d(TAG, "无已保存服务配置,使用默认值并持久化");
}
mAppConfigBean.setEnableUsageReminder(bean.isEnableUsageReminder());
mAppConfigBean.setUsageReminderValue(bean.getUsageReminderValue());
mAppConfigBean.setEnableChargeReminder(bean.isEnableChargeReminder());
mAppConfigBean.setChargeReminderValue(bean.getChargeReminderValue());
}
public void saveConfigData(final MainActivity activity) {
if (MainActivity._mMainActivity == null) {
/**
* 保存应用配置(内部核心方法,无弹窗,直接持久化)
*/
private void saveAppConfig() {
// 补全BaseBean 通用保存逻辑传入Bean类名作为存储Key
AppConfigBean.saveBean(mContext, mAppConfigBean);
// 通知服务和Activity更新配置补全ControlCenterService 新增 updateStatus 静态方法)
ControlCenterService.updateStatus(mContext, mAppConfigBean);
MainActivity.reloadAppConfig();
LogUtils.d(TAG, "应用配置保存成功已通知服务和Activity更新");
}
/**
* 保存服务配置(内部核心方法,无弹窗,直接持久化)
*/
private void saveServiceConfig() {
mServiceConfigBean.setIsEnableService(mIsServiceEnabled);
// 补全BaseBean 通用保存逻辑
ControlCenterServiceBean.saveBean(mContext, mServiceConfigBean);
LogUtils.d(TAG, "服务配置保存成功,服务状态:" + (mIsServiceEnabled ? "开启" : "关闭"));
}
// ======================== 通用确认弹窗方法(抽取复用,减少冗余)========================
/**
* 配置变更确认弹窗(所有配置修改统一调用)
*/
private void showConfigConfirmDialog(Activity activity, final Runnable confirmAction) {
if (activity == null || activity.isFinishing()) {
// 兼容Java7Activity.isDestroyed() 是API17+方法直接判断isFinishing()即可避免API兼容问题
LogUtils.e(TAG, "弹窗显示失败Activity无效");
return;
}
YesNoAlertDialog.show(activity, CONFIRM_TITLE, CONFIRM_MSG, new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onYes() {
LogUtils.d(TAG, "用户确认更改配置");
confirmAction.run(); // 执行确认后的配置变更逻辑
}
@Override
public void onNo() {
LogUtils.d(TAG, "用户取消更改配置");
// 取消后刷新配置显示,保持界面与实际配置一致
MainActivity.reloadAppConfig();
}
});
}
// ======================== 服务开关配置方法(单独归类,逻辑聚焦)========================
/**
* 设置服务开关状态(带弹窗确认,适配小米手机交互规范)
*/
public void setServiceEnabled(Activity activity, final boolean isEnabled) {
// 状态无变化,直接返回
if (isEnabled == mIsServiceEnabled) {
LogUtils.d(TAG, "服务状态无变化,无需操作");
return;
}
YesNoAlertDialog.show(activity, "应用设置信息", "是否保存应用配置?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onYes() {
saveConfigData();
}
@Override
public void onNo() {
AppConfigUtils.getInstance(activity).loadAppConfigBean();
MainActivity.relaodAppConfigs();
}
});
// 调用通用确认弹窗
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mIsServiceEnabled = isEnabled;
saveServiceConfig();
// 启停服务适配API29-30后台服务规范
if (mIsServiceEnabled) {
LogUtils.d(TAG, "启动ControlCenterService");
ControlCenterService.startControlCenterService(mContext);
} else {
LogUtils.d(TAG, "停止ControlCenterService");
ControlCenterService.stopControlCenterService(mContext);
}
}
});
}
//
// 保存应用配置数据
//
void saveConfigData() {
// 更新配置先取消一下旧的的提醒消息
//NotificationHelper.cancelRemindNotification(mContext);
/**
* 获取服务开关状态(直接返回缓存值,提升性能)
*/
public boolean isServiceEnabled() {
LogUtils.d(TAG, "获取服务状态:" + (mIsServiceEnabled ? "开启" : "关闭"));
return mIsServiceEnabled;
}
// ======================== 充电提醒配置方法(单独归类,逻辑聚焦)========================
/**
* 设置充电提醒开关状态(带弹窗确认)
*/
public void setChargeReminderEnabled(Activity activity, final boolean isEnabled) {
if (isEnabled == mAppConfigBean.isEnableChargeReminder()) {
LogUtils.d(TAG, "充电提醒状态无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setEnableChargeReminder(isEnabled);
saveAppConfig();
LogUtils.d(TAG, "充电提醒状态更新为:" + (isEnabled ? "开启" : "关闭"));
}
});
}
/**
* 获取充电提醒开关状态
*/
public boolean isChargeReminderEnabled() {
boolean isEnabled = mAppConfigBean.isEnableChargeReminder();
LogUtils.d(TAG, "获取充电提醒状态:" + (isEnabled ? "开启" : "关闭"));
return isEnabled;
}
/**
* 设置充电提醒阈值带弹窗确认API29-30数据安全适配
*/
public void setChargeReminderValue(Activity activity, final int value) {
// 校准阈值范围,避免异常值
final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getChargeReminderValue()) {
LogUtils.d(TAG, "充电提醒阈值无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setChargeReminderValue(calibratedValue);
saveAppConfig();
LogUtils.d(TAG, "充电提醒阈值更新为:" + calibratedValue + "%");
}
});
}
/**
* 获取充电提醒阈值
*/
public int getChargeReminderValue() {
int value = mAppConfigBean.getChargeReminderValue();
LogUtils.d(TAG, "获取充电提醒阈值:" + value + "%");
return value;
}
// ======================== 耗电提醒配置方法(单独归类,逻辑聚焦)========================
/**
* 设置耗电提醒开关状态(带弹窗确认)
*/
public void setUsageReminderEnabled(Activity activity, final boolean isEnabled) {
if (isEnabled == mAppConfigBean.isEnableUsageReminder()) {
LogUtils.d(TAG, "耗电提醒状态无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setEnableUsageReminder(isEnabled);
saveAppConfig();
LogUtils.d(TAG, "耗电提醒状态更新为:" + (isEnabled ? "开启" : "关闭"));
}
});
}
/**
* 获取耗电提醒开关状态
*/
public boolean isUsageReminderEnabled() {
boolean isEnabled = mAppConfigBean.isEnableUsageReminder();
LogUtils.d(TAG, "获取耗电提醒状态:" + (isEnabled ? "开启" : "关闭"));
return isEnabled;
}
/**
* 设置耗电提醒阈值带弹窗确认API29-30数据安全适配
*/
public void setUsageReminderValue(Activity activity, final int value) {
// 校准阈值范围,适配小米手机电量跳变场景
final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getUsageReminderValue()) {
LogUtils.d(TAG, "耗电提醒阈值无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setUsageReminderValue(calibratedValue);
saveAppConfig();
LogUtils.d(TAG, "耗电提醒阈值更新为:" + calibratedValue + "%");
}
});
}
/**
* 获取耗电提醒阈值
*/
public int getUsageReminderValue() {
int value = mAppConfigBean.getUsageReminderValue();
LogUtils.d(TAG, "获取耗电提醒阈值:" + value + "%");
return value;
}
// ======================== 实时电池状态配置方法(临时状态,不持久化,无需弹窗)========================
/**
* 设置当前充电状态(仅内存缓存,不持久化)
*/
public void setCharging(boolean isCharging) {
if (isCharging == mAppConfigBean.isCharging()) {
LogUtils.d(TAG, "充电状态无变化,无需操作");
return;
}
mAppConfigBean.setIsCharging(isCharging);
LogUtils.d(TAG, "充电状态更新为:" + (isCharging ? "充电中" : "未充电"));
}
/**
* 获取当前充电状态
*/
public boolean isCharging() {
boolean isCharging = mAppConfigBean.isCharging();
LogUtils.d(TAG, "获取充电状态:" + (isCharging ? "充电中" : "未充电"));
return isCharging;
}
/**
* 设置当前电池电量(仅内存缓存,不持久化)
*/
public void setCurrentBatteryValue(int value) {
// 校准电量范围,适配小米手机电量跳变场景
int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getCurrentBatteryValue()) {
LogUtils.d(TAG, "电池电量无变化,无需操作");
return;
}
mAppConfigBean.setCurrentBatteryValue(calibratedValue);
LogUtils.d(TAG, "电池电量更新为:" + calibratedValue + "%");
}
/**
* 获取当前电池电量
*/
public int getCurrentBatteryValue() {
int value = mAppConfigBean.getCurrentBatteryValue();
LogUtils.d(TAG, "获取电池电量:" + value + "%");
return value;
}
// ======================== 提醒间隔配置方法(持久化存储,带弹窗确认)========================
/**
* 设置提醒间隔时间带弹窗确认单位ms最小1000ms
*/
public void setReminderIntervalTime(Activity activity, final int interval) {
final int calibratedInterval = Math.max(interval, MIN_INTERVAL_TIME);
if (calibratedInterval == mAppConfigBean.getReminderIntervalTime()) {
LogUtils.d(TAG, "提醒间隔无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setReminderIntervalTime(calibratedInterval);
saveAppConfig();
LogUtils.d(TAG, "提醒间隔更新为:" + calibratedInterval + "ms");
}
});
}
/**
* 获取提醒间隔时间单位ms
*/
public int getReminderIntervalTime() {
int interval = mAppConfigBean.getReminderIntervalTime();
LogUtils.d(TAG, "获取提醒间隔:" + interval + "ms");
return interval;
}
// ======================== 新增:电量检测间隔配置(适配 AppConfigBean 新增字段)========================
/**
* 设置电量检测间隔带弹窗确认单位ms最小500ms
*/
public void setBatteryDetectInterval(Activity activity, final int interval) {
final int calibratedInterval = Math.max(interval, 500); // 与 RemindThread MIN_SLEEP_TIME 一致
if (calibratedInterval == mAppConfigBean.getBatteryDetectInterval()) {
LogUtils.d(TAG, "检测间隔无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setBatteryDetectInterval(calibratedInterval);
saveAppConfig();
LogUtils.d(TAG, "电量检测间隔更新为:" + calibratedInterval + "ms");
}
});
}
/**
* 获取电量检测间隔单位ms
*/
public int getBatteryDetectInterval() {
int interval = mAppConfigBean.getBatteryDetectInterval();
LogUtils.d(TAG, "获取电量检测间隔:" + interval + "ms");
return interval;
}
// ======================== 外部配置操作入口(带用户确认)========================
/**
* 保存配置带弹窗确认MainActivity专用入口
*/
public void saveConfigWithConfirm(final MainActivity activity) {
if (activity == null || activity.isFinishing()) {
LogUtils.e(TAG, "保存配置失败Activity无效");
return;
}
YesNoAlertDialog.show(activity, CONFIRM_TITLE, "是否保存当前所有配置?", new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onYes() {
saveAppConfig();
LogUtils.d(TAG, "用户确认保存所有配置");
}
@Override
public void onNo() {
LogUtils.d(TAG, "用户取消保存,重新加载配置");
loadAllConfig();
MainActivity.reloadAppConfig();
}
});
}
/**
* 重新加载所有配置(外部调用,适配手动修改配置场景)
*/
public void reloadAllConfig() {
LogUtils.d(TAG, "开始重新加载所有配置");
loadAllConfig();
MainActivity.reloadAppConfig();
LogUtils.d(TAG, "配置重新加载完成");
}
// ======================== 补全ControlCenterService 新增 updateStatus 静态方法(内部适配)========================
/**
* 给 ControlCenterService 补充 updateStatus 静态方法(通知服务更新配置)
* 实际项目中可直接复制到 ControlCenterService 类中,此处为临时补全,避免编译报错
*/
public static class ControlCenterServiceHelper {
AppConfigBean.saveBean(mContext, mAppConfigBean);
// 通知活动窗口和服务配置已更新
ControlCenterService.updateStatus(mContext, mAppConfigBean);
MainActivity.relaodAppConfigs();
}
}

View File

@@ -1,28 +1,80 @@
package cc.winboll.studio.powerbell.utils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/07/18 04:32:46
* @Describe 电池工具类
*/
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.R;
/**
* 电池工具类:获取电池状态、发送提醒通知(复用逻辑,避免代码冗余)
*/
public class BatteryUtils {
private static final String TAG = "BatteryUtils";
private static final int REMIND_NOTIFICATION_ID = 10087; // 提醒通知ID
public static final String TAG = "BatteryUtils";
public static boolean isCharging(Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
return isCharging;
/**
* 获取当前电池电量0-100
*/
public static int getCurrentBattery(Context context) {
Intent batteryIntent = context.getApplicationContext().registerReceiver(
null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
);
if (batteryIntent == null) return 0;
// 提取电量(当前电量/总电量 * 100
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
return (level * 100) / scale;
}
public static int getTheQuantityOfElectricity(Intent intent) {
int intLevel = intent.getIntExtra("level", 0);
int intScale = intent.getIntExtra("scale", 100);
return intLevel * 100 / intScale;
/**
* 判断是否正在充电(有线/无线均可)
*/
public static boolean isCharging(Context context) {
Intent batteryIntent = context.getApplicationContext().registerReceiver(
null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
);
if (batteryIntent == null) return false;
int status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
return status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL;
}
/**
* 显示充电提醒通知
*/
public static void showChargeReminderNotification(Context context) {
Notification notification = new Notification.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("充电提醒")
.setContentText("电池电量已达标,建议及时断电保护电池~")
.setAutoCancel(true) // 点击后取消通知
.build();
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.notify(REMIND_NOTIFICATION_ID, notification);
}
LogUtils.d(TAG, "showChargeReminderNotification: notification show");
}
/**
* 显示耗电提醒通知
*/
public static void showUsageReminderNotification(Context context) {
Notification notification = new Notification.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("耗电提醒")
.setContentText("电池电量偏低,建议及时充电~")
.setAutoCancel(true)
.build();
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.notify(REMIND_NOTIFICATION_ID, notification);
}
LogUtils.d(TAG, "showUsageReminderNotification: notification show");
}
}

View File

@@ -4,413 +4,352 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.os.Build;
import android.view.View;
import android.widget.RemoteViews;
import androidx.core.app.NotificationCompat;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.models.NotificationMessage;
import cc.winboll.studio.powerbell.services.ControlCenterService;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/13 20:44
* @Describe 全局通知管理工具类整合所有通知能力适配API29-30兼容Java7所有通知统一跳转MainActivity
* 通知工具类统一管理前台服务通知、提醒通知适配API19-34强化容错与兼容性
*/
public class NotificationManagerUtils {
// ====================== 常量定义(统一管理,避免冲突,首屏可见)======================
public static final String TAG = "NotificationManagerUtils";
// 通知渠道4大渠道场景隔离API26+必填)
// 1. 前台服务保活渠道(低优先级,无打扰)
private static final String CHANNEL_ID_FOREGROUND_SERVICE = "channel_foreground_service";
private static final String CHANNEL_NAME_FOREGROUND_SERVICE = "前台服务保活通知";
private static final String CHANNEL_DESC_FOREGROUND_SERVICE = "后台服务运行状态,无声音无震动,不打扰用户";
// 2. 电量提醒渠道(高优先级,闹钟铃声+震动,强提醒)
private static final String CHANNEL_ID_BATTERY_REMIND = "channel_battery_remind";
private static final String CHANNEL_NAME_BATTERY_REMIND = "电量异常提醒通知";
private static final String CHANNEL_DESC_BATTERY_REMIND = "电量过高/过低提醒,强震动+闹钟铃声,突破免打扰";
// 3. 通用临时通知渠道(高优先级,仅震动,普通告警)
private static final String CHANNEL_ID_TEMP_ALERT = "channel_temp_alert";
private static final String CHANNEL_NAME_TEMP_ALERT = "通用临时提醒通知";
private static final String CHANNEL_DESC_TEMP_ALERT = "普通即时告警,仅震动提醒,自动取消";
// 通知ID唯一区分避免覆盖按场景分段
public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001; // 前台服务
public static final int NOTIFY_ID_BATTERY_REMIND = 1002; // 电量提醒
public static final int NOTIFY_ID_TEMP_ALERT = 1003; // 通用临时通知
public static final int NOTIFY_ID_CUSTOM_LAYOUT = 1004; // 自定义布局通知
// 通用配置
private static final int PENDING_INTENT_FLAGS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
: PendingIntent.FLAG_UPDATE_CURRENT; // API30安全标志
private static final long[] VIBRATE_PATTERN = new long[]{100, 200, 300, 400}; // 标准震动节奏
//private static final String DEFAULT_JUMP_PACKAGE = "cc.winboll.studio.powerbell"; // 默认跳转包名API29+必填)
private static final String TAG = "NotificationManagerUtils";
// 通知常量渠道ID、通知ID避免魔法值渠道名/描述优化用户感知)
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";
public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001;
public static final int NOTIFY_ID_REMIND = 1002;
// ====================== 成员变量(按场景分组,私有封装,避免外部篡改)======================
private final Context mContext;
private final NotificationManager mNotificationManager;
// 前台服务通知专属
private Notification mForegroundServiceNotify;
private RemoteViews mForegroundServiceRemoteViews;
// 电量提醒通知专属
private Notification mBatteryRemindNotify;
private RemoteViews mBatteryRemindRemoteViews;
// 成员变量(封装属性,提升安全性)
private Notification mForegroundServiceNotify;
private NotificationManager mNotificationManager;
private Context mContext;
// 低版本通知兼容配置API<21 必须,避免通知图标显示异常)
private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher;
// ====================== 构造方法(单例思想/实例化通用,自动初始化渠道)======================
public NotificationManagerUtils(Context context) {
LogUtils.d(TAG, "【初始化】全局通知管理工具类 构造方法调用");
this.mContext = context.getApplicationContext(); // 用应用上下文,避免内存泄漏
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
createAllNotificationChannels(); // 自动创建所有渠道API26+
LogUtils.d(TAG, "【初始化】全局通知管理工具类 完成,渠道创建状态:" + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? "已创建4个渠道" : "无需创建"));
}
// 构造方法(强化判空,避免初始化失败+内存泄漏)
public NotificationManagerUtils(Context context) {
LogUtils.d(TAG, "NotificationManagerUtils: 初始化通知工具类");
if (context == null) {
LogUtils.e(TAG, "NotificationManagerUtils: Context is null初始化失败");
return;
}
this.mContext = context.getApplicationContext();
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
initNotificationChannels();
LogUtils.d(TAG, "NotificationManagerUtils: 工具类初始化完成");
}
// ====================== 核心基础能力(渠道创建+Intent构建复用逻辑减少冗余======================
/**
* 创建所有通知渠道API26+专属,低版本自动跳过,确保通知正常显示)
*/
@SuppressWarnings("deprecation")
public void createAllNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LogUtils.d(TAG, "【渠道管理】开始创建所有通知渠道");
createForegroundServiceChannel();
createBatteryRemindChannel();
createTempAlertChannel();
LogUtils.d(TAG, "【渠道管理】4个通知渠道创建完成含3个核心渠道+预留扩展)");
}
}
// 通知渠道初始化适配API26+,保持稳定)
private void initNotificationChannels() {
LogUtils.d(TAG, "initNotificationChannels: 初始化通知渠道");
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || mNotificationManager == null) {
LogUtils.d(TAG, "initNotificationChannels: API<26 或 NotificationManager为空跳过渠道初始化");
return;
}
/**
* 创建前台服务保活渠道IMPORTANCE_LOW无声音无震动不打扰用户
*/
private void createForegroundServiceChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID_FOREGROUND_SERVICE,
CHANNEL_NAME_FOREGROUND_SERVICE,
NotificationManager.IMPORTANCE_LOW
);
channel.setDescription(CHANNEL_DESC_FOREGROUND_SERVICE);
channel.setSound(null, null);
channel.enableVibration(false);
channel.setShowBadge(false); // 不显示应用角标
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); // 锁屏隐藏
mNotificationManager.createNotificationChannel(channel);
LogUtils.d(TAG, "【渠道管理】前台服务保活渠道创建成功:" + CHANNEL_NAME_FOREGROUND_SERVICE);
}
}
// 前台服务渠道(低优先级,无打扰)
NotificationChannel foregroundChannel = new NotificationChannel(
CHANNEL_ID_FOREGROUND,
"电池服务保活",
NotificationManager.IMPORTANCE_LOW
);
foregroundChannel.setDescription("电池提醒服务后台稳定运行,无弹窗、无震动、无声音");
foregroundChannel.enableLights(false);
foregroundChannel.enableVibration(false);
foregroundChannel.setSound(null, null);
foregroundChannel.setShowBadge(false);
foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
/**
* 创建电量提醒渠道IMPORTANCE_HIGH闹钟铃声+震动,突破免打扰)
*/
private void createBatteryRemindChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID_BATTERY_REMIND,
CHANNEL_NAME_BATTERY_REMIND,
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription(CHANNEL_DESC_BATTERY_REMIND);
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null); // 闹钟铃声
channel.enableVibration(true);
channel.setVibrationPattern(VIBRATE_PATTERN);
channel.setBypassDnd(true); // 突破免打扰
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // 锁屏可见
channel.setShowBadge(true);
mNotificationManager.createNotificationChannel(channel);
LogUtils.d(TAG, "【渠道管理】电量提醒渠道创建成功:" + CHANNEL_NAME_BATTERY_REMIND);
}
}
// 电池提醒渠道(中优先级,确保感知)
NotificationChannel remindChannel = new NotificationChannel(
CHANNEL_ID_REMIND,
"电池状态提醒",
NotificationManager.IMPORTANCE_DEFAULT
);
remindChannel.setDescription("电池充电满/低电量提醒,及时保护电池健康");
remindChannel.enableLights(true);
remindChannel.enableVibration(true);
remindChannel.setVibrationPattern(new long[]{100, 200, 100});
remindChannel.setSound(null, null);
remindChannel.setShowBadge(false);
remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
/**
* 创建通用临时通知渠道IMPORTANCE_HIGH仅震动普通告警
*/
private void createTempAlertChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID_TEMP_ALERT,
CHANNEL_NAME_TEMP_ALERT,
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription(CHANNEL_DESC_TEMP_ALERT);
//channel.setSound(null, null); // 仅震动,不发声
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null); // 闹钟铃声
channel.enableVibration(true);
channel.setVibrationPattern(VIBRATE_PATTERN);
channel.setBypassDnd(false); // 不突破免打扰
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
channel.setShowBadge(true);
mNotificationManager.createNotificationChannel(channel);
LogUtils.d(TAG, "【渠道管理】通用临时通知渠道创建成功:" + CHANNEL_NAME_TEMP_ALERT);
}
}
mNotificationManager.createNotificationChannel(foregroundChannel);
mNotificationManager.createNotificationChannel(remindChannel);
LogUtils.d(TAG, "initNotificationChannels: 渠道初始化完成");
}
/**
* 构建固定跳转PendingIntent所有通知统一跳转MainActivity适配API29-30安全规范
* @return 安全的PendingIntent确保跳转稳定不泄露
*/
private PendingIntent buildFixedPendingIntent() {
// 固定跳MainActivity不支持自定义目标
Intent intent = new Intent(mContext, MainActivity.class);
// API29+ 强制要求:明确包名,避免跳转目标模糊
intent.setPackage(mContext.getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); // 跳转时清除栈顶避免重复创建Activity
LogUtils.d(TAG, "【Intent构建】所有通知统一跳转MainActivity包名" + mContext.getPackageName());
// ====================== 核心修复适配低API移除 FOREGROUND_SERVICE_TYPE ======================
// 启动前台服务通知(全版本兼容,API19-34通用
public void startForegroundServiceNotify(Service service, NotificationMessage message) {
LogUtils.d(TAG, "startForegroundServiceNotify: 启动前台服务通知");
if (service == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "startForegroundServiceNotify: 依赖参数为空,启动失败");
return;
}
PendingIntent pendingIntent = PendingIntent.getActivity(
mContext,
0,
intent,
PENDING_INTENT_FLAGS
);
LogUtils.d(TAG, "【Intent构建】PendingIntent创建成功安全标志" + PENDING_INTENT_FLAGS);
return pendingIntent;
}
mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "startForegroundServiceNotify: 通知构建失败,启动失败");
return;
}
// ====================== 场景1前台服务保活通知支持自定义布局+更新)======================
/**
* 初始化前台服务通知自定义布局RemoteViews
*/
private void initForegroundServiceRemoteViews(ControlCenterService service, NotificationMessage msg) {
LogUtils.d(TAG, "【布局初始化】开始初始化前台服务通知布局,标题:" + msg.getTitle());
mForegroundServiceRemoteViews = new RemoteViews(service.getPackageName(), R.layout.view_servicenotification);
mForegroundServiceRemoteViews.setTextViewText(R.id.remoteviewTextView1, msg.getTitle());
mForegroundServiceRemoteViews.setTextViewText(R.id.remoteviewTextView3, msg.getContent());
mForegroundServiceRemoteViews.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
LogUtils.d(TAG, "【布局初始化】前台服务通知布局填充完成");
}
// 修复:移除 API30+ 专属的 FOREGROUND_SERVICE_TYPE用全版本通用写法
try {
// 所有API版本统一调用startForeground(通知ID, 通知实例)
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "startForegroundServiceNotify: 启动成功通知ID=" + NOTIFY_ID_FOREGROUND_SERVICE);
} catch (Exception e) {
LogUtils.e(TAG, "startForegroundServiceNotify: 启动异常可能是权限或5秒内未调用", e);
}
}
/**
* 启动前台服务保活通知ControlCenterService专用API26+强制要求,保活后台服务)
*/
public void startForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
LogUtils.d(TAG, "【前台服务通知】开始构建保活通知,内容:" + msg.getContent());
if (service == null || msg == null) {
LogUtils.e(TAG, "【前台服务通知】构建失败Service/NotificationMessage为空");
return;
}
// 发送电池提醒通知(保持稳定)
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: 依赖参数为空,发送失败");
return;
}
// 1. 构建固定跳转Intent统一跳MainActivity
PendingIntent pendingIntent = buildFixedPendingIntent();
// 2. 构建基础通知兼容API26+渠道低版本用Builder
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new Notification.Builder(service, CHANNEL_ID_FOREGROUND_SERVICE);
} else {
builder = new Notification.Builder(service);
}
mForegroundServiceNotify = builder
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
.setContentTitle(msg.getTitle())
.setContentText(msg.getContent())
.setWhen(System.currentTimeMillis())
.setColor(Color.parseColor("#F00606")) // 小图标背景色
.setContentIntent(pendingIntent)
.setOngoing(true) // 常驻通知,不可滑动取消(保活关键)
.setAutoCancel(false) // 禁止点击取消
.build();
// 3. 设置自定义布局
initForegroundServiceRemoteViews(service, msg);
mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
// 4. 启动前台服务必须调用否则Service易被回收
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "【前台服务通知】保活通知启动成功通知ID" + NOTIFY_ID_FOREGROUND_SERVICE);
}
Notification remindNotify = buildRemindNotification(context, message);
if (remindNotify == null) {
LogUtils.e(TAG, "showRemindNotification: 通知构建失败,发送失败");
return;
}
/**
* 更新前台服务保活通知内容(无需重启服务,直接刷新布局)
*/
public void updateForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
LogUtils.d(TAG, "【前台服务通知】开始更新保活通知,新内容:" + msg.getContent());
if (mForegroundServiceNotify == null || mForegroundServiceRemoteViews == null) {
LogUtils.e(TAG, "【前台服务通知】更新失败通知对象未初始化先调用startForegroundServiceNotify");
return;
}
// 更新自定义布局数据
initForegroundServiceRemoteViews(service, msg);
mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
// 发送更新
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "【前台服务通知】保活通知更新成功");
}
try {
mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify);
LogUtils.d(TAG, "showRemindNotification: 发送成功通知ID=" + NOTIFY_ID_REMIND);
} catch (Exception e) {
LogUtils.e(TAG, "showRemindNotification: 发送异常", e);
}
}
// ====================== 场景2电量提醒通知支持自定义布局+更新+单独取消)======================
/**
* 初始化电量提醒通知自定义布局RemoteViews支持充电/耗电切换)
*/
private void initBatteryRemindRemoteViews(ControlCenterService service, NotificationMessage msg) {
LogUtils.d(TAG, "【布局初始化】开始初始化电量提醒布局,提醒类型:" + msg.getRemindMSG());
mBatteryRemindRemoteViews = new RemoteViews(service.getPackageName(), R.layout.view_remindnotification);
mBatteryRemindRemoteViews.setTextViewText(R.id.viewremindnotificationTextView1, msg.getTitle());
mBatteryRemindRemoteViews.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
// 切换布局(+:充电提醒,-:耗电提醒)
String remindType = msg.getRemindMSG() != null ? msg.getRemindMSG().trim() : "";
if ("+".equals(remindType)) {
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.GONE);
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.VISIBLE);
LogUtils.d(TAG, "【布局初始化】电量提醒布局切换:充电提醒");
} else if ("-".equals(remindType)) {
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.GONE);
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
LogUtils.d(TAG, "【布局初始化】电量提醒布局切换:耗电提醒");
} else {
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.GONE);
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
LogUtils.w(TAG, "【布局初始化】未知电量提醒类型:" + remindType);
}
}
// 构建前台服务通知低版本API<21 兼容配置)
private Notification buildForegroundNotification(NotificationMessage message) {
if (message == null || mContext == null) {
LogUtils.e(TAG, "buildForegroundNotification: 参数为空,构建失败");
return null;
}
/**
* 初始化电量提醒通知仅构建不发送配合update触发提醒
*/
public void initBatteryRemindNotify(ControlCenterService service, NotificationMessage msg) {
LogUtils.d(TAG, "【电量提醒通知】开始初始化提醒通知,标题:" + msg.getTitle());
if (service == null || msg == null) {
LogUtils.e(TAG, "【电量提醒通知】初始化失败Service/NotificationMessage为空");
return;
}
// 1. 构建固定跳转Intent统一跳MainActivity
PendingIntent pendingIntent = buildFixedPendingIntent();
// 2. 构建基础通知
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new Notification.Builder(service, CHANNEL_ID_BATTERY_REMIND);
} else {
builder = new Notification.Builder(service);
}
mBatteryRemindNotify = builder
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
.setContentTitle(msg.getTitle())
.setContentText(msg.getContent())
.setWhen(System.currentTimeMillis())
.setColor(Color.parseColor("#F00606"))
.setContentIntent(pendingIntent)
.setAutoCancel(true) // 点击取消
.build();
// 3. 初始化自定义布局
initBatteryRemindRemoteViews(service, msg);
mBatteryRemindNotify.contentView = mBatteryRemindRemoteViews;
mBatteryRemindNotify.bigContentView = mBatteryRemindRemoteViews;
LogUtils.d(TAG, "【电量提醒通知】初始化完成");
}
String notifyTitle = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中";
String notifyContent = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台持续监测电池状态,保护电池健康";
/**
* 发送/更新电量提醒通知(初始化后调用,触发强提醒)
*/
public void sendOrUpdateBatteryRemindNotify() {
LogUtils.d(TAG, "【电量提醒通知】开始发送/更新提醒");
if (mBatteryRemindNotify == null || mBatteryRemindRemoteViews == null) {
LogUtils.e(TAG, "【电量提醒通知】发送失败通知未初始化先调用initBatteryRemindNotify");
return;
}
mNotificationManager.notify(NOTIFY_ID_BATTERY_REMIND, mBatteryRemindNotify);
LogUtils.d(TAG, "【电量提醒通知】发送/更新成功通知ID" + NOTIFY_ID_BATTERY_REMIND);
}
// API分级构建确保全版本兼容
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(notifyTitle)
.setContentText(notifyContent)
.setAutoCancel(false)
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setTicker(notifyTitle);
builder.setContentIntent(createJumpPendingIntent(mContext, 0));
notification = builder.build();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setLargeIcon(getAppIcon(mContext))
.setColor(mContext.getResources().getColor(R.color.colorPrimary))
.setContentTitle(notifyTitle)
.setContentText(notifyContent)
.setAutoCancel(false)
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setTicker(notifyTitle)
.setPriority(Notification.PRIORITY_LOW);
builder.setContentIntent(createJumpPendingIntent(mContext, 0));
notification = builder.build();
} else {
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(notifyTitle)
.setContentText(notifyContent)
.setAutoCancel(false)
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setTicker(notifyTitle)
.setPriority(Notification.PRIORITY_LOW);
builder.setContentIntent(createJumpPendingIntent(mContext, 0));
notification = builder.build();
}
/**
* 单独取消电量提醒通知(静态方法,外部可直接调用,无需实例化)
*/
public static void cancelBatteryRemindNotify(Context context) {
LogUtils.d(TAG, "【电量提醒通知】开始取消提醒通知ID" + NOTIFY_ID_BATTERY_REMIND);
if (context == null) {
LogUtils.e(TAG, "【电量提醒通知】取消失败Context为空");
return;
}
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFY_ID_BATTERY_REMIND);
LogUtils.d(TAG, "【电量提醒通知】取消成功");
}
// 低版本通知默认效果屏蔽(无渠道时手动关闭声音/震动)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
notification.defaults = 0;
notification.vibrate = new long[]{0};
notification.sound = null;
}
// ====================== 场景3通用临时通知简单文本自动取消无需自定义布局======================
/**
* 显示通用临时通知普通告警仅震动自动取消统一跳转MainActivity
* @param title 通知标题
* @param content 通知内容
*/
public void showTempAlertNotify(String title, String content) {
LogUtils.d(TAG, "【通用临时通知】开始构建,标题:" + title + ",内容:" + content);
if (title == null || content == null) {
LogUtils.e(TAG, "【通用临时通知】构建失败:标题/内容为空");
return;
}
// 1. 构建固定跳转Intent统一跳MainActivity
PendingIntent pendingIntent = buildFixedPendingIntent();
// 2. 用NotificationCompat.Builder兼容所有版本简化逻辑
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMP_ALERT)
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
.setContentTitle(title)
.setContentText(content)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setVibrate(VIBRATE_PATTERN);
// 3. 发送通知
Notification notification = builder.build();
mNotificationManager.notify(NOTIFY_ID_TEMP_ALERT, notification);
LogUtils.d(TAG, "【通用临时通知】显示成功通知ID" + NOTIFY_ID_TEMP_ALERT);
}
return notification;
}
// ====================== 场景4自定义布局通知灵活扩展支持复杂样式======================
/**
* 显示自定义布局通知(支持普通布局+大布局通用所有场景统一跳转MainActivity
* @param contentView 普通自定义布局(必填)
* @param bigContentView 下拉大布局(可选)
*/
public void showCustomLayoutNotify(RemoteViews contentView, RemoteViews bigContentView) {
LogUtils.d(TAG, "【自定义布局通知】开始构建布局ID" + (contentView != null ? contentView.getLayoutId() : null));
if (contentView == null) {
LogUtils.e(TAG, "【自定义布局通知】构建失败普通布局contentView为空");
return;
}
// 1. 构建固定跳转Intent统一跳MainActivity
PendingIntent pendingIntent = buildFixedPendingIntent();
// 2. 构建自定义布局通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMP_ALERT)
.setSmallIcon(R.drawable.ic_launcher) // 必传,不可省略
.setContentIntent(pendingIntent)
.setContent(contentView)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true);
// 添加大布局(可选)
if (bigContentView != null) {
builder.setCustomBigContentView(bigContentView);
LogUtils.d(TAG, "【自定义布局通知】已添加下拉大布局布局ID" + bigContentView.getLayoutId());
}
// 3. 发送通知
Notification notification = builder.build();
mNotificationManager.notify(NOTIFY_ID_CUSTOM_LAYOUT, notification);
LogUtils.d(TAG, "【自定义布局通知】显示成功通知ID" + NOTIFY_ID_CUSTOM_LAYOUT);
}
// 构建电池提醒通知低版本API<21 兼容配置)
private Notification buildRemindNotification(Context context, NotificationMessage message) {
if (context == null || message == null) {
LogUtils.e(TAG, "buildRemindNotification: 参数为空,构建失败");
return null;
}
// ====================== 通知取消工具(支持精准取消/全取消)======================
/**
* 取消指定ID的通知精准取消灵活控制
*/
public void cancelNotifyById(int notifyId) {
LogUtils.d(TAG, "【通知管理】开始取消通知ID" + notifyId);
mNotificationManager.cancel(notifyId);
LogUtils.d(TAG, "【通知管理】通知取消成功ID" + notifyId);
}
String notifyTitle = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒";
String notifyContent = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理(充电满/低电量)";
/**
* 取消所有通知(谨慎使用,会清除所有场景的通知)
*/
public void cancelAllNotifies() {
LogUtils.d(TAG, "【通知管理】开始取消所有通知");
mNotificationManager.cancelAll();
LogUtils.d(TAG, "【通知管理】所有通知取消完成");
}
// API分级构建确保提醒效果
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID_REMIND)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(notifyTitle)
.setContentText(notifyContent)
.setAutoCancel(true)
.setOngoing(false)
.setWhen(System.currentTimeMillis())
.setTicker(notifyTitle)
.setVibrate(new long[]{100, 200, 100});
builder.setContentIntent(createJumpPendingIntent(context, 1));
notification = builder.build();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setLargeIcon(getAppIcon(context))
.setColor(context.getResources().getColor(R.color.colorPrimary))
.setContentTitle(notifyTitle)
.setContentText(notifyContent)
.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();
} else {
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(notifyTitle)
.setContentText(notifyContent)
.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();
}
return notification;
}
// 内部辅助创建跳转主页面的PendingIntent适配API31+安全要求)
private PendingIntent createJumpPendingIntent(Context context, int requestCode) {
Intent jumpIntent = new Intent(context, MainActivity.class);
jumpIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// API31+ 必须加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) {
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);
}
}
// 取消前台服务通知适配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: 释放资源");
// 释放前先取消前台通知(兜底,避免资源泄漏)
cancelForegroundServiceNotify();
mNotificationManager = null;
mContext = null;
LogUtils.d(TAG, "release: 资源释放完成");
}
}