豆包完美想象版,未调试。
This commit is contained in:
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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";
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
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);
|
||||
super.handleMessage(msg);
|
||||
ControlCenterService service = mwrControlCenterService.get();
|
||||
if (service == null) {
|
||||
LogUtils.e(TAG, "handleMessage: 服务实例为空,处理失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理不同类型消息
|
||||
switch (msg.what) {
|
||||
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: 提醒通知发送完成");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,19 @@ 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";
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
// 应用消息结构
|
||||
//
|
||||
/**
|
||||
* 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递
|
||||
*/
|
||||
public class NotificationMessage {
|
||||
private String title; // 通知标题
|
||||
private String content; // 通知内容
|
||||
private String remindMSG; // 通知标识(区分服务运行/充电/耗电)
|
||||
|
||||
String Title;
|
||||
String Content;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
LogUtils.d(TAG, "onReceive: enter, action=" + intent.getAction());
|
||||
if (context == null || intent == null || intent.getAction() == null) {
|
||||
LogUtils.e(TAG, "onReceive: invalid params");
|
||||
return;
|
||||
}
|
||||
if (_mnTheQuantityOfElectricityOld != nTheQuantityOfElectricity) {
|
||||
mwrService.get().getRemindThread().setQuantityOfElectricity(nTheQuantityOfElectricity);
|
||||
}
|
||||
}
|
||||
// 新电池状态标志某一个有变化就更新显示信息
|
||||
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;
|
||||
// 弱引用获取服务,双重校验服务有效性(核心防护,避免内存泄漏+空指针)
|
||||
ControlCenterService service = mwrControlCenterService != null ? mwrControlCenterService.get() : null;
|
||||
if (service == null || service.isDestroyed()) {
|
||||
LogUtils.e(TAG, "onReceive: ControlCenterService is destroyed or null");
|
||||
// 服务销毁后主动注销广播,彻底避免无效回调
|
||||
unregisterAction(context);
|
||||
return;
|
||||
}
|
||||
} else if (intent.getAction().equals(ACTION_START_REMINDTHREAD)) {
|
||||
LogUtils.d(TAG, "ACTION_START_REMINDTHREAD");
|
||||
//AppConfigUtils appConfigUtils = AppConfigUtils.getInstance(context);
|
||||
//appConfigUtils.loadAppConfigBean();
|
||||
|
||||
// 分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 {
|
||||
AppConfigBean appConfigBean = intent.getParcelableExtra("appConfigBean");
|
||||
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;
|
||||
}
|
||||
// 调用工具类更新通知,确保方法匹配
|
||||
notifyUtils.updateForegroundServiceNotify(notifyMsg);
|
||||
LogUtils.d(TAG, "handleUpdateServiceNotification: success");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "handleUpdateServiceNotification: failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理电池状态变化(核心适配 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) {
|
||||
mwrService.get().startRemindThread(appConfigBean);
|
||||
// 先更新服务配置,再重启线程(确保线程启动时拿到最新配置)
|
||||
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("ControlReceiver", "parse Parcelable config failed", e);
|
||||
}
|
||||
|
||||
LogUtils.e(TAG, "handleStartRemindThread: failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 Receiver
|
||||
//
|
||||
// ====================== 内部辅助方法(拆分逻辑+统一容错) ======================
|
||||
/**
|
||||
* 加载最新配置(适配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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 注册 Receiver
|
||||
//
|
||||
// 仅处理电池状态变化广播
|
||||
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); // 同步充电状态
|
||||
}
|
||||
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(mReceiver, filter);
|
||||
mGlobalApplication.registerReceiver(mCurrentReceiver, filter);
|
||||
LogUtils.d(TAG, "registerAction: broadcast receiver registered success");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "registerAction: register failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:注销广播(避免内存泄漏,供外部调用)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// 运行服务内容
|
||||
//
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
LogUtils.d(TAG, "onStartCommand: 触发服务启动命令,flags=" + flags);
|
||||
handleExternalCommand(intent);
|
||||
|
||||
// 重新绑定前台服务,避免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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return START_STICKY; // 服务回收后自动重启
|
||||
}
|
||||
|
||||
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.");
|
||||
// 处理外部指令(重启线程)- 加锁避免并发调用
|
||||
private void handleExternalCommand(Intent intent) {
|
||||
LogUtils.d(TAG, "handleExternalCommand: 处理外部指令");
|
||||
if (intent == null) {
|
||||
LogUtils.e(TAG, "handleExternalCommand: Intent为空,跳过");
|
||||
return;
|
||||
}
|
||||
}
|
||||
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()");
|
||||
String action = intent.getAction();
|
||||
if (TextUtils.isEmpty(action)) {
|
||||
LogUtils.e(TAG, "handleExternalCommand: 指令Action为空,跳过");
|
||||
return;
|
||||
}
|
||||
|
||||
public void stopRemindThread() {
|
||||
if (mRemindThread != null) {
|
||||
mRemindThread.setIsExist(true);
|
||||
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: 新配置为空,重启失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 安全重启提醒线程 - 核心修复:彻底销毁旧线程,确保新线程是全新实例
|
||||
public void restartRemindThreadSafely() {
|
||||
LogUtils.d(TAG, "restartRemindThreadSafely: 重启提醒线程");
|
||||
synchronized (mServiceLock) { // 加锁,避免与服务销毁/其他重启操作冲突
|
||||
// 1. 先彻底停止旧线程(调用独立封装的安全停止方法)
|
||||
stopRemindThreadSafely();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 安全停止提醒线程 - 优化:确保线程彻底终止,无残留
|
||||
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() {
|
||||
//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");
|
||||
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);
|
||||
}
|
||||
|
||||
// 主进程与守护进程连接时需要用到此类
|
||||
//
|
||||
private class MyServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
//LogUtils.d(TAG, "call onServiceConnected(...)");
|
||||
// 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 onServiceDisconnected(ComponentName name) {
|
||||
//LogUtils.d(TAG, "call onServiceConnected(...)");
|
||||
if (mAppConfigUtils.getIsEnableService()) {
|
||||
// 唤醒守护进程
|
||||
wakeupAndBindAssistant();
|
||||
}
|
||||
}
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null; // 无需绑定服务
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
}
|
||||
|
||||
//
|
||||
// 停止服务
|
||||
//
|
||||
public static void stopControlCenterService(Context context) {
|
||||
Intent intent = new Intent(context, ControlCenterService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
LogUtils.d(TAG, "updateStatus: 配置更新指令发送成功");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "updateStatus: 发送指令异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
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; // 配置同步重试次数(兜底配置有效性)
|
||||
|
||||
public static final String TAG = RemindThread.class.getSimpleName();
|
||||
// 单例核心(volatile+双重校验锁,线程安全,修复复用漏洞)
|
||||
private static volatile RemindThread sInstance;
|
||||
|
||||
Context mContext;
|
||||
// 成员变量(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(); // 全局锁(保护所有状态变量,解决并发安全)
|
||||
|
||||
// 控制线程是否退出的标志
|
||||
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;
|
||||
|
||||
public void setIsExist(boolean isExist) {
|
||||
this.isExist = isExist;
|
||||
// 私有构造器(单例模式,强化上下文弱引用+状态重置)
|
||||
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 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 static void setIsReminding(boolean isReminding) {
|
||||
RemindThread.isReminding = isReminding;
|
||||
// 关键修复:旧线程已停止(未运行),直接销毁单例,确保新实例创建
|
||||
if (sInstance != null && !sInstance.isRunning()) {
|
||||
destroyInstance();
|
||||
LogUtils.d(TAG, "getInstance: 旧线程已停止,销毁单例准备创建新实例");
|
||||
}
|
||||
|
||||
public static boolean isReminding() {
|
||||
return isReminding;
|
||||
// 双重校验锁,线程安全创建单例
|
||||
if (sInstance == null) {
|
||||
synchronized (RemindThread.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new RemindThread(context, handler);
|
||||
LogUtils.d(TAG, "getInstance: 新线程实例创建成功,线程ID=" + sInstance.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public static void setIsEnableUsegeReminder(boolean isEnableUsegeReminder) {
|
||||
RemindThread.isEnableUsegeReminder = isEnableUsegeReminder;
|
||||
// 单例销毁(核心优化:彻底释放资源+终止线程,与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: 单例已为空,跳过销毁");
|
||||
}
|
||||
|
||||
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 RemindThread(Context context, ControlCenterServiceHandler handler) {
|
||||
mContext = context;
|
||||
mwrControlCenterServiceHandler = new WeakReference<ControlCenterServiceHandler>(handler);
|
||||
}
|
||||
|
||||
// 线程核心逻辑(彻底修复重复启动、优化退出效率、强化异常容错)
|
||||
@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;
|
||||
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; // 初始化上次提醒时间
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((quantityOfElectricity <= usegeReminderValue)
|
||||
&& (isEnableUsegeReminder)) {
|
||||
// 正在放电时电量小于指定电量发送提醒
|
||||
sendNotificationMessage("-");
|
||||
// 应用需要继续提醒,设置退出标志为否
|
||||
setIsExist(false);
|
||||
//sendNotificationMessage("I am ready! -");
|
||||
} else {
|
||||
// 设置退出标志,如果后续不需要继续提醒就退出当前进程,用于应用节能。
|
||||
setIsExist(true);
|
||||
isReminding = false;
|
||||
// 步骤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;
|
||||
}
|
||||
}
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
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(); // 启动恢复计时器,避免重复提醒
|
||||
}
|
||||
}
|
||||
//ToastUtils.show("Service Is Stop.");
|
||||
//LogUtils.i(TAG, "Service Is Stop.");
|
||||
// 耗电状态提醒(同理,开关+阈值双重校验)
|
||||
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() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
private volatile ControlCenterServiceBean mServiceConfigBean;
|
||||
|
||||
// 电池充电提醒值。
|
||||
// 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;
|
||||
// 缓存状态(减少Bean读取次数,提升性能)
|
||||
private volatile boolean mIsServiceEnabled = false;
|
||||
|
||||
volatile boolean mIsUseBackgroundFile = false;
|
||||
volatile String mszBackgroundFileName = "";
|
||||
|
||||
// 保存应用实例
|
||||
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;
|
||||
// 双重校验锁,适配多线程并发场景
|
||||
if (sInstance == null) {
|
||||
synchronized (AppConfigUtils.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new AppConfigUtils(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void setIsEnableService(Activity activity, final boolean isEnableService) {
|
||||
YesNoAlertDialog.show(activity, "应用设置信息", "是否保存应用配置?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
// ======================== 核心配置读写方法(优先排列,保障配置稳定性)========================
|
||||
/**
|
||||
* 加载所有配置(应用配置+服务配置,统一入口)
|
||||
*/
|
||||
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, "无已保存服务配置,使用默认值并持久化");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存应用配置(内部核心方法,无弹窗,直接持久化)
|
||||
*/
|
||||
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()) {
|
||||
// 兼容Java7:Activity.isDestroyed() 是API17+方法,直接判断isFinishing()即可,避免API兼容问题
|
||||
LogUtils.e(TAG, "弹窗显示失败:Activity无效");
|
||||
return;
|
||||
}
|
||||
YesNoAlertDialog.show(activity, CONFIRM_TITLE, CONFIRM_MSG, 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);
|
||||
}
|
||||
LogUtils.d(TAG, "用户确认更改配置");
|
||||
confirmAction.run(); // 执行确认后的配置变更逻辑
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
MainActivity.relaodAppConfigs();
|
||||
LogUtils.d(TAG, "用户取消更改配置");
|
||||
// 取消后刷新配置显示,保持界面与实际配置一致
|
||||
MainActivity.reloadAppConfig();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean getIsEnableService() {
|
||||
ControlCenterServiceBean bean = ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class);
|
||||
if (bean == null) {
|
||||
ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(false));
|
||||
return false;
|
||||
}
|
||||
return bean.isEnableService();
|
||||
}
|
||||
|
||||
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();
|
||||
AppConfigBean.saveBean(mContext, mAppConfigBean);
|
||||
}
|
||||
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) {
|
||||
// ======================== 服务开关配置方法(单独归类,逻辑聚焦)========================
|
||||
/**
|
||||
* 设置服务开关状态(带弹窗确认,适配小米手机交互规范)
|
||||
*/
|
||||
public void setServiceEnabled(Activity activity, final boolean isEnabled) {
|
||||
// 状态无变化,直接返回
|
||||
if (isEnabled == mIsServiceEnabled) {
|
||||
LogUtils.d(TAG, "服务状态无变化,无需操作");
|
||||
return;
|
||||
}
|
||||
|
||||
YesNoAlertDialog.show(activity, "应用设置信息", "是否保存应用配置?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
|
||||
// 调用通用确认弹窗
|
||||
showConfigConfirmDialog(activity, new Runnable() {
|
||||
@Override
|
||||
public void onYes() {
|
||||
saveConfigData();
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
AppConfigUtils.getInstance(activity).loadAppConfigBean();
|
||||
MainActivity.relaodAppConfigs();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// 保存应用配置数据
|
||||
//
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 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(); // 用应用上下文,避免内存泄漏
|
||||
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);
|
||||
createAllNotificationChannels(); // 自动创建所有渠道(API26+)
|
||||
LogUtils.d(TAG, "【初始化】全局通知管理工具类 完成,渠道创建状态:" + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? "已创建4个渠道" : "无需创建"));
|
||||
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,
|
||||
// 前台服务渠道(低优先级,无打扰)
|
||||
NotificationChannel foregroundChannel = new NotificationChannel(
|
||||
CHANNEL_ID_FOREGROUND,
|
||||
"电池服务保活",
|
||||
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);
|
||||
}
|
||||
}
|
||||
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
|
||||
// 电池提醒渠道(中优先级,确保感知)
|
||||
NotificationChannel remindChannel = new NotificationChannel(
|
||||
CHANNEL_ID_REMIND,
|
||||
"电池状态提醒",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
mNotificationManager.createNotificationChannel(foregroundChannel);
|
||||
mNotificationManager.createNotificationChannel(remindChannel);
|
||||
LogUtils.d(TAG, "initNotificationChannels: 渠道初始化完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通用临时通知渠道(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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建固定跳转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());
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
mContext,
|
||||
0,
|
||||
intent,
|
||||
PENDING_INTENT_FLAGS
|
||||
);
|
||||
LogUtils.d(TAG, "【Intent构建】PendingIntent创建成功,安全标志:" + PENDING_INTENT_FLAGS);
|
||||
return pendingIntent;
|
||||
}
|
||||
|
||||
// ====================== 场景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, "【布局初始化】前台服务通知布局填充完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动前台服务保活通知(ControlCenterService专用,API26+强制要求,保活后台服务)
|
||||
*/
|
||||
public void startForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【前台服务通知】开始构建保活通知,内容:" + msg.getContent());
|
||||
if (service == null || msg == null) {
|
||||
LogUtils.e(TAG, "【前台服务通知】构建失败:Service/NotificationMessage为空");
|
||||
// ====================== 核心修复:适配低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;
|
||||
}
|
||||
|
||||
// 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 = buildForegroundNotification(message);
|
||||
if (mForegroundServiceNotify == null) {
|
||||
LogUtils.e(TAG, "startForegroundServiceNotify: 通知构建失败,启动失败");
|
||||
return;
|
||||
}
|
||||
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易被回收)
|
||||
|
||||
// 修复:移除 API30+ 专属的 FOREGROUND_SERVICE_TYPE,用全版本通用写法
|
||||
try {
|
||||
// 所有API版本统一调用:startForeground(通知ID, 通知实例)
|
||||
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
|
||||
LogUtils.d(TAG, "【前台服务通知】保活通知启动成功,通知ID:" + NOTIFY_ID_FOREGROUND_SERVICE);
|
||||
LogUtils.d(TAG, "startForegroundServiceNotify: 启动成功,通知ID=" + NOTIFY_ID_FOREGROUND_SERVICE);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "startForegroundServiceNotify: 启动异常(可能是权限或5秒内未调用)", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前台服务保活通知内容(无需重启服务,直接刷新布局)
|
||||
*/
|
||||
public void updateForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【前台服务通知】开始更新保活通知,新内容:" + msg.getContent());
|
||||
if (mForegroundServiceNotify == null || mForegroundServiceRemoteViews == null) {
|
||||
LogUtils.e(TAG, "【前台服务通知】更新失败:通知对象未初始化,先调用startForegroundServiceNotify");
|
||||
// 发送电池提醒通知(保持稳定)
|
||||
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;
|
||||
}
|
||||
// 更新自定义布局数据
|
||||
initForegroundServiceRemoteViews(service, msg);
|
||||
mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
|
||||
mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
|
||||
// 发送更新
|
||||
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
|
||||
LogUtils.d(TAG, "【前台服务通知】保活通知更新成功");
|
||||
}
|
||||
|
||||
// ====================== 场景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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化电量提醒通知(仅构建,不发送,配合update触发提醒)
|
||||
*/
|
||||
public void initBatteryRemindNotify(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【电量提醒通知】开始初始化提醒通知,标题:" + msg.getTitle());
|
||||
if (service == null || msg == null) {
|
||||
LogUtils.e(TAG, "【电量提醒通知】初始化失败:Service/NotificationMessage为空");
|
||||
Notification remindNotify = buildRemindNotification(context, message);
|
||||
if (remindNotify == null) {
|
||||
LogUtils.e(TAG, "showRemindNotification: 通知构建失败,发送失败");
|
||||
return;
|
||||
}
|
||||
// 1. 构建固定跳转Intent(统一跳MainActivity)
|
||||
PendingIntent pendingIntent = buildFixedPendingIntent();
|
||||
// 2. 构建基础通知
|
||||
Notification.Builder builder;
|
||||
|
||||
try {
|
||||
mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify);
|
||||
LogUtils.d(TAG, "showRemindNotification: 发送成功,通知ID=" + NOTIFY_ID_REMIND);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "showRemindNotification: 发送异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建前台服务通知(低版本API<21 兼容配置)
|
||||
private Notification buildForegroundNotification(NotificationMessage message) {
|
||||
if (message == null || mContext == null) {
|
||||
LogUtils.e(TAG, "buildForegroundNotification: 参数为空,构建失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
String notifyTitle = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中";
|
||||
String notifyContent = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台持续监测电池状态,保护电池健康";
|
||||
|
||||
// API分级构建,确保全版本兼容
|
||||
Notification notification;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder = new Notification.Builder(service, CHANNEL_ID_BATTERY_REMIND);
|
||||
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 {
|
||||
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())
|
||||
Notification.Builder builder = new Notification.Builder(mContext)
|
||||
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
|
||||
.setContentTitle(notifyTitle)
|
||||
.setContentText(notifyContent)
|
||||
.setAutoCancel(false)
|
||||
.setOngoing(true)
|
||||
.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, "【电量提醒通知】初始化完成");
|
||||
.setTicker(notifyTitle)
|
||||
.setPriority(Notification.PRIORITY_LOW);
|
||||
builder.setContentIntent(createJumpPendingIntent(mContext, 0));
|
||||
notification = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送/更新电量提醒通知(初始化后调用,触发强提醒)
|
||||
*/
|
||||
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);
|
||||
// 低版本通知默认效果屏蔽(无渠道时手动关闭声音/震动)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
notification.defaults = 0;
|
||||
notification.vibrate = new long[]{0};
|
||||
notification.sound = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单独取消电量提醒通知(静态方法,外部可直接调用,无需实例化)
|
||||
*/
|
||||
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, "【电量提醒通知】取消成功");
|
||||
return notification;
|
||||
}
|
||||
|
||||
// ====================== 场景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;
|
||||
// 构建电池提醒通知(低版本API<21 兼容配置)
|
||||
private Notification buildRemindNotification(Context context, NotificationMessage message) {
|
||||
if (context == null || message == null) {
|
||||
LogUtils.e(TAG, "buildRemindNotification: 参数为空,构建失败");
|
||||
return null;
|
||||
}
|
||||
// 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)
|
||||
|
||||
String notifyTitle = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒";
|
||||
String notifyContent = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理(充电满/低电量)";
|
||||
|
||||
// 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)
|
||||
.setVibrate(VIBRATE_PATTERN);
|
||||
// 3. 发送通知
|
||||
Notification notification = builder.build();
|
||||
mNotificationManager.notify(NOTIFY_ID_TEMP_ALERT, notification);
|
||||
LogUtils.d(TAG, "【通用临时通知】显示成功,通知ID:" + NOTIFY_ID_TEMP_ALERT);
|
||||
.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();
|
||||
}
|
||||
|
||||
// ====================== 场景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 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
// ====================== 通知取消工具(支持精准取消/全取消)======================
|
||||
/**
|
||||
* 取消指定ID的通知(精准取消,灵活控制)
|
||||
*/
|
||||
public void cancelNotifyById(int notifyId) {
|
||||
LogUtils.d(TAG, "【通知管理】开始取消通知,ID:" + notifyId);
|
||||
mNotificationManager.cancel(notifyId);
|
||||
LogUtils.d(TAG, "【通知管理】通知取消成功,ID:" + notifyId);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有通知(谨慎使用,会清除所有场景的通知)
|
||||
*/
|
||||
public void cancelAllNotifies() {
|
||||
LogUtils.d(TAG, "【通知管理】开始取消所有通知");
|
||||
mNotificationManager.cancelAll();
|
||||
LogUtils.d(TAG, "【通知管理】所有通知取消完成");
|
||||
} 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: 资源释放完成");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user