源码整理

This commit is contained in:
2025-12-23 14:16:58 +08:00
parent eff1822ee5
commit 34082c49e9
16 changed files with 845 additions and 642 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Dec 23 05:35:18 GMT 2025 #Tue Dec 23 06:15:42 GMT 2025
stageCount=24 stageCount=24
libraryProject= libraryProject=
baseVersion=15.14 baseVersion=15.14
publishVersion=15.14.23 publishVersion=15.14.23
buildCount=7 buildCount=8
baseBetaVersion=15.14.24 baseBetaVersion=15.14.24

View File

@@ -3,12 +3,11 @@ package cc.winboll.studio.powerbell.models;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
/** /**
* 电池报告数据模型
* 适配 API30存储当前电量、放电时间、充电时间核心数据
* 支持参数校验与调试日志输出
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/03/22 14:30:51 * @Date 2025/03/22 14:30:51
* @Describe 电池报告数据模型 * @Describe 电池报告数据模型
* 适配 API30存储当前电量、放电时间、充电时间核心数据
* 支持参数校验与调试日志输出
*/ */
public class BatteryData { public class BatteryData {
// ====================== 静态常量(首屏可见,统一管理) ====================== // ====================== 静态常量(首屏可见,统一管理) ======================

View File

@@ -8,11 +8,10 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 电池信息数据模型
* 适配 API30存储电量时间戳与电量值支持 JSON 序列化/反序列化
* 修复字段拼写错误,补充数据校验与调试日志
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen<zhangsken@qq.com>
* @Describe 电池信息数据模型 * @Describe 电池信息数据模型
* 适配 API30存储电量时间戳与电量值支持 JSON 序列化/反序列化
* 修复字段拼写错误,补充数据校验与调试日志
*/ */
public class BatteryInfoBean extends BaseBean implements Serializable { public class BatteryInfoBean extends BaseBean implements Serializable {
// ====================== 静态常量(首屏可见,统一管理) ====================== // ====================== 静态常量(首屏可见,统一管理) ======================

View File

@@ -4,48 +4,44 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.JsonReader; import android.util.JsonReader;
import android.util.JsonWriter; import android.util.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import cc.winboll.studio.libappbase.BaseBean; import cc.winboll.studio.libappbase.BaseBean;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 15:55 * @Date 2025/12/17 15:55
* @Describe 服务控制参数模型管理服务启用状态支持序列化、Parcel传递、JSON解析 * @Describe 服务控制参数模型
* 适配 API30管理服务启用状态支持 Serializable 持久化、Parcelable 组件传递、JSON 序列化解析
*/ */
public class ControlCenterServiceBean extends BaseBean implements Parcelable, Serializable { public class ControlCenterServiceBean extends BaseBean implements Parcelable, Serializable {
// ================================== 静态常量(置顶统一管理,避免魔法值)================================= // ====================== 静态常量(置顶统一管理,避免魔法值) ======================
private static final long serialVersionUID = 1L; // Serializable 必备,保障反序列化兼容 private static final long serialVersionUID = 1L; // Serializable 必备,保障反序列化兼容
private static final String TAG = "ControlCenterServiceBean"; private static final String TAG = "ControlCenterServiceBean";
// JSON 字段常量,避免硬编码,减少拼写错误 private static final String JSON_FIELD_IS_ENABLE_SERVICE = "isEnableService"; // JSON 字段常量,避免硬编码
private static final String JSON_FIELD_IS_ENABLE_SERVICE = "isEnableService";
// ================================== 核心成员变量(私有封装,规范命名)================================= // ====================== 核心成员变量(私有封装,规范命名) ======================
private boolean isEnableService = false; // 服务启用状态true=启用false=禁用 private boolean isEnableService = false; // 服务启用状态true=启用false=禁用
// ================================== Parcelable 静态创建器(必须 public static final适配 API30 传递)================================= // ====================== Parcelable 静态创建器(必须 public static final适配 API30 组件传递) ======================
public static final Parcelable.Creator<ControlCenterServiceBean> CREATOR = new Parcelable.Creator<ControlCenterServiceBean>() { public static final Parcelable.Creator<ControlCenterServiceBean> CREATOR = new Parcelable.Creator<ControlCenterServiceBean>() {
@Override @Override
public ControlCenterServiceBean createFromParcel(Parcel source) { public ControlCenterServiceBean createFromParcel(Parcel source) {
LogUtils.d(TAG, "Parcelable createFromParcel: 从Parcel反序列化对象");
// Java7 + API30 适配Parcel 无直接 writeBoolean用 byte 存储/读取
boolean isEnable = source.readByte() != 0; boolean isEnable = source.readByte() != 0;
ControlCenterServiceBean bean = new ControlCenterServiceBean(isEnable); ControlCenterServiceBean bean = new ControlCenterServiceBean(isEnable);
LogUtils.d(TAG, "Parcelable createFromParcel: 反序列化完成isEnableService=" + isEnable); LogUtils.d(TAG, String.format("createFromParcel: 反序列化完成isEnableService=%b", isEnable));
return bean; return bean;
} }
@Override @Override
public ControlCenterServiceBean[] newArray(int size) { public ControlCenterServiceBean[] newArray(int size) {
LogUtils.d(TAG, "Parcelable newArray: 创建数组,长度=" + size); LogUtils.d(TAG, String.format("newArray: 创建数组,长度=%d", size));
return new ControlCenterServiceBean[size]; return new ControlCenterServiceBean[size];
} }
}; };
// ================================== 构造方法(无参+有参,满足不同初始化场景)================================= // ====================== 构造方法(无参+有参,满足不同初始化场景) ======================
/** /**
* 无参构造JSON解析、反射创建必备 * 无参构造JSON解析、反射创建必备
*/ */
@@ -60,25 +56,25 @@ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Se
*/ */
public ControlCenterServiceBean(boolean isEnableService) { public ControlCenterServiceBean(boolean isEnableService) {
this.isEnableService = isEnableService; this.isEnableService = isEnableService;
LogUtils.d(TAG, "有参构造初始化服务状态isEnableService=" + isEnableService); LogUtils.d(TAG, String.format("有参构造初始化服务状态isEnableService=%b", isEnableService));
} }
// ================================== Getter/Setter 方法(封装成员变量,控制访问)================================= // ====================== Getter/Setter 方法(封装成员变量,控制访问) ======================
public boolean isEnableService() { public boolean isEnableService() {
LogUtils.d(TAG, "get isEnableService: 当前状态=" + isEnableService); LogUtils.d(TAG, String.format("isEnableService: 当前状态=%b", isEnableService));
return isEnableService; return isEnableService;
} }
public void setIsEnableService(boolean isEnableService) { public void setIsEnableService(boolean isEnableService) {
LogUtils.d(TAG, "set isEnableService: 旧状态=" + this.isEnableService + ",新状态=" + isEnableService); LogUtils.d(TAG, String.format("setIsEnableService: 旧状态=%b新状态=%b", this.isEnableService, isEnableService));
this.isEnableService = isEnableService; this.isEnableService = isEnableService;
} }
// ================================== 父类 BaseBean 方法重写(核心业务逻辑================================= // ====================== 父类 BaseBean 方法重写(核心业务逻辑JSON 序列化/反序列化) ======================
@Override @Override
public String getName() { public String getName() {
String className = ControlCenterServiceBean.class.getName(); String className = ControlCenterServiceBean.class.getName();
LogUtils.d(TAG, "getName: 返回类名=" + className); LogUtils.d(TAG, String.format("getName: 返回类名=%s", className));
return className; return className;
} }
@@ -87,11 +83,9 @@ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Se
*/ */
@Override @Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
LogUtils.d(TAG, "writeThisToJsonWriter: 开始将对象序列化到JSON");
super.writeThisToJsonWriter(jsonWriter); super.writeThisToJsonWriter(jsonWriter);
// 写入服务启用状态字段
jsonWriter.name(JSON_FIELD_IS_ENABLE_SERVICE).value(this.isEnableService); jsonWriter.name(JSON_FIELD_IS_ENABLE_SERVICE).value(this.isEnableService);
LogUtils.d(TAG, "writeThisToJsonWriter: JSON序列化完成,字段=" + JSON_FIELD_IS_ENABLE_SERVICE + ",值=" + this.isEnableService); LogUtils.d(TAG, String.format("writeThisToJsonWriter: 序列化完成,%s=%b", JSON_FIELD_IS_ENABLE_SERVICE, this.isEnableService));
} }
/** /**
@@ -99,44 +93,39 @@ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Se
*/ */
@Override @Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
LogUtils.d(TAG, "readBeanFromJsonReader: 开始从JSON反序列化对象");
ControlCenterServiceBean bean = new ControlCenterServiceBean(); ControlCenterServiceBean bean = new ControlCenterServiceBean();
jsonReader.beginObject(); jsonReader.beginObject();
while (jsonReader.hasNext()) { while (jsonReader.hasNext()) {
String fieldName = jsonReader.nextName(); String fieldName = jsonReader.nextName();
if (JSON_FIELD_IS_ENABLE_SERVICE.equals(fieldName)) { if (JSON_FIELD_IS_ENABLE_SERVICE.equals(fieldName)) {
// 读取并设置服务启用状态
boolean isEnable = jsonReader.nextBoolean(); boolean isEnable = jsonReader.nextBoolean();
bean.setIsEnableService(isEnable); bean.setIsEnableService(isEnable);
LogUtils.d(TAG, "readBeanFromJsonReader: 读取JSON字段" + fieldName + "=" + isEnable); LogUtils.d(TAG, String.format("readBeanFromJsonReader: 读取字段,%s=%b", fieldName, isEnable));
} else { } else {
// 跳过未知字段,避免解析异常
jsonReader.skipValue(); jsonReader.skipValue();
LogUtils.w(TAG, "readBeanFromJsonReader: 跳过未知JSON字段=" + fieldName); LogUtils.w(TAG, String.format("readBeanFromJsonReader: 跳过未知字段=%s", fieldName));
} }
} }
jsonReader.endObject(); jsonReader.endObject();
LogUtils.d(TAG, "readBeanFromJsonReader: JSON反序列化完成"); LogUtils.d(TAG, "readBeanFromJsonReader: 反序列化完成");
return bean; return bean;
} }
// ================================== Parcelable 接口方法实现(适配 Intent 组件间传递================================= // ====================== Parcelable 接口方法实现(适配 Intent 组件间传递Java7 适配) ======================
@Override @Override
public int describeContents() { public int describeContents() {
// 无特殊内容如文件描述符返回0即可API30 标准实现)
LogUtils.d(TAG, "describeContents: 返回内容描述符=0"); LogUtils.d(TAG, "describeContents: 返回内容描述符=0");
return 0; return 0; // 无特殊内容如文件描述符返回0即可API30 标准实现)
} }
/** /**
* 序列化对象到 ParcelIntent 传递必备Java7 适配) * 序列化对象到 ParcelIntent 传递必备Java7 适配:用 byte 存储 boolean
*/ */
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
LogUtils.d(TAG, "writeToParcel: 开始将对象序列化到Parcelflags=" + flags); byte flag = (byte) (this.isEnableService ? 1 : 0);
// Java7 + API30 适配Parcel 无 writeBoolean 方法,用 byte 存储1=true0=false dest.writeByte(flag);
dest.writeByte((byte) (this.isEnableService ? 1 : 0)); LogUtils.d(TAG, String.format("writeToParcel: 序列化完成isEnableService=%b存储为byte=%d", this.isEnableService, flag));
LogUtils.d(TAG, "writeToParcel: Parcel序列化完成isEnableService=" + this.isEnableService + "存储为byte=" + (this.isEnableService ? 1 : 0) + "");
} }
} }

View File

@@ -1,36 +1,75 @@
package cc.winboll.studio.powerbell.models; package cc.winboll.studio.powerbell.models;
import cc.winboll.studio.libappbase.LogUtils;
/** /**
* 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递 * 通知数据模型
* 适配 API30统一存储通知标题、内容、标识信息支持各组件数据传递
* @Author ZhanGSKen<zhangsken@qq.com>
* @Describe 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递
*/ */
public class NotificationMessage { public class NotificationMessage {
// ====================== 静态常量(统一管理) ======================
private static final String TAG = "NotificationMessage";
private static final String EMPTY_STRING = "";
// ====================== 核心成员变量(按业务逻辑排序) ======================
private String title; // 通知标题 private String title; // 通知标题
private String content; // 通知内容 private String content; // 通知内容
private String remindMSG; // 通知标识(区分服务运行/充电/耗电) private String remindMSG; // 通知标识(区分服务运行/充电/耗电)
// ====================== Setter/Getter 方法 ====================== // ====================== 构造方法(无参+全参,满足不同初始化场景) ======================
public String getTitle() { /**
return title; * 无参构造器反射实例化、JSON反序列化必备
*/
public NotificationMessage() {
this.title = EMPTY_STRING;
this.content = EMPTY_STRING;
this.remindMSG = EMPTY_STRING;
LogUtils.d(TAG, "无参构造:初始化通知数据模型,默认值为空字符串");
} }
/**
* 全参构造器(直接传参创建实例,简化调用)
* @param title 通知标题
* @param content 通知内容
* @param remindMSG 通知标识
*/
public NotificationMessage(String title, String content, String remindMSG) {
this.title = title == null ? EMPTY_STRING : title;
this.content = content == null ? EMPTY_STRING : content;
this.remindMSG = remindMSG == null ? EMPTY_STRING : remindMSG;
LogUtils.d(TAG, String.format("全参构造:初始化完成 | 标题:%s | 内容:%s | 标识:%s",
this.title, this.content, this.remindMSG));
}
// ====================== Setter 方法(补充空值防护与调试日志) ======================
public void setTitle(String title) { public void setTitle(String title) {
this.title = title; this.title = title == null ? EMPTY_STRING : title;
LogUtils.d(TAG, String.format("setTitle通知标题设置为「%s」", this.title));
}
public void setContent(String content) {
this.content = content == null ? EMPTY_STRING : content;
LogUtils.d(TAG, String.format("setContent通知内容设置为「%s」", this.content));
}
public void setRemindMSG(String remindMSG) {
this.remindMSG = remindMSG == null ? EMPTY_STRING : remindMSG;
LogUtils.d(TAG, String.format("setRemindMSG通知标识设置为「%s」", this.remindMSG));
}
// ====================== Getter 方法(按成员变量顺序排列) ======================
public String getTitle() {
return title;
} }
public String getContent() { public String getContent() {
return content; return content;
} }
public void setContent(String content) {
this.content = content;
}
public String getRemindMSG() { public String getRemindMSG() {
return remindMSG; return remindMSG;
} }
public void setRemindMSG(String remindMSG) {
this.remindMSG = remindMSG;
}
} }

View File

@@ -14,14 +14,14 @@ import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
/** /**
* 控制中心广播接收器
* 功能:监听电池状态变化、前台通知更新、配置变更指令
* 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/19 20:23 * @Date 2025/12/19 20:23
* @Describe 控制中心广播接收器
* 功能:监听电池状态变化、前台通知更新、配置变更指令
* 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步
*/ */
public class ControlCenterServiceReceiver extends BroadcastReceiver { public class ControlCenterServiceReceiver extends BroadcastReceiver {
// ================================== 静态常量区(置顶归类,消除魔法值)================================= // ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "ControlCenterServiceReceiver"; public static final String TAG = "ControlCenterServiceReceiver";
// 广播Action常量带包名前缀防冲突 // 广播Action常量带包名前缀防冲突
@@ -34,27 +34,28 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
private static final int BATTERY_LEVEL_MIN = 0; private static final int BATTERY_LEVEL_MIN = 0;
private static final int BATTERY_LEVEL_MAX = 100; private static final int BATTERY_LEVEL_MAX = 100;
// ================================== 静态状态标记volatile保证多线程可见性================================= // ====================== 静态状态标记volatile保证多线程可见性 ======================
private static volatile int sLastBatteryLevel = -1; // 上次电量(多线程可见) private static volatile int sLastBatteryLevel = -1; // 上次电量(多线程可见)
private static volatile boolean sIsCharging = false; // 上次充电状态(多线程可见) private static volatile boolean sIsCharging = false; // 上次充电状态(多线程可见)
// ================================== 成员变量区(弱引用防泄漏,按功能分层)================================= // ====================== 成员变量区(弱引用防泄漏,按功能分层) ======================
private WeakReference<ControlCenterService> mwrControlCenterService; private WeakReference<ControlCenterService> mwrControlCenterService;
private boolean isRegistered = false; // 新增:标记广播注册状态,避免冗余操作 private boolean isRegistered = false; // 标记广播注册状态,避免冗余操作
// ================================== 构造方法(初始化弱引用,避免服务强引用泄漏)================================= // ====================== 构造方法(初始化弱引用,避免服务强引用泄漏) ======================
public ControlCenterServiceReceiver(ControlCenterService service) { public ControlCenterServiceReceiver(ControlCenterService service) {
LogUtils.d(TAG, "构造接收器 | service=" + (service != null ? service.getClass().getSimpleName() : "null")); LogUtils.d(TAG, String.format("构造接收器 | 服务实例:%s", service != null ? service.getClass().getSimpleName() : "null"));
this.mwrControlCenterService = new WeakReference<>(service); this.mwrControlCenterService = new WeakReference<>(service);
} }
// ================================== 广播核心接收逻辑入口方法分Action分发处理================================= // ====================== 广播核心接收逻辑入口方法分Action分发处理 ======================
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
LogUtils.d(TAG, "onReceive: 接收广播 | action=" + (intent != null ? intent.getAction() : "null")); String action = intent != null ? intent.getAction() : "null";
LogUtils.d(TAG, String.format("onReceive: 接收广播 | Action%s", action));
// 基础参数校验 // 基础参数校验
if (context == null || intent == null || intent.getAction() == null) { if (context == null || intent == null || action == null) {
LogUtils.e(TAG, "onReceive: 参数无效context=" + context + " | intent=" + intent + "),终止处理"); LogUtils.e(TAG, "onReceive: 参数无效context=" + context + " | intent=" + intent + "),终止处理");
return; return;
} }
@@ -62,13 +63,12 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
// 弱引用获取服务,双重校验服务有效性 // 弱引用获取服务,双重校验服务有效性
ControlCenterService service = mwrControlCenterService != null ? mwrControlCenterService.get() : null; ControlCenterService service = mwrControlCenterService != null ? mwrControlCenterService.get() : null;
if (service == null || service.isDestroyed()) { if (service == null || service.isDestroyed()) {
LogUtils.e(TAG, "onReceive: 服务已销毁或为空service=" + service + ",注销广播"); LogUtils.e(TAG, "onReceive: 服务已销毁或为空,注销广播");
unregisterAction(context); unregisterAction(context);
return; return;
} }
// 分Action处理业务逻辑 // 分Action处理业务逻辑
String action = intent.getAction();
switch (action) { switch (action) {
case Intent.ACTION_BATTERY_CHANGED: case Intent.ACTION_BATTERY_CHANGED:
handleBatteryStateChanged(service, intent); handleBatteryStateChanged(service, intent);
@@ -77,30 +77,30 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
handleUpdateForegroundNotification(service); handleUpdateForegroundNotification(service);
break; break;
case ACTION_APPCONFIG_CHANGED: case ACTION_APPCONFIG_CHANGED:
LogUtils.d(TAG, "onReceive: 开始处理配置更新广播"); // 新增:标记配置广播处理起点 LogUtils.d(TAG, "onReceive: 开始处理配置更新广播");
handleNotifyAppConfigUpdate(service); handleNotifyAppConfigUpdate(service);
break; break;
default: default:
LogUtils.w(TAG, "onReceive: 未知Action=" + action); LogUtils.w(TAG, String.format("onReceive: 未知Action=%s", action));
} }
LogUtils.d(TAG, "onReceive: 广播处理完成"); LogUtils.d(TAG, "onReceive: 广播处理完成");
} }
// ================================== 业务处理方法(按功能拆分,强化容错与日志)================================= // ====================== 业务处理方法(按功能拆分,强化容错与日志) ======================
/** /**
* 处理电池状态变化广播 * 处理电池状态变化广播
* @param service 控制中心服务实例 * @param service 控制中心服务实例
* @param intent 电池状态广播意图 * @param intent 电池状态广播意图
*/ */
private void handleBatteryStateChanged(ControlCenterService service, Intent intent) { private void handleBatteryStateChanged(ControlCenterService service, Intent intent) {
LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态 | service=" + service + " | intent=" + intent); LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态");
try { try {
// 1. 解析并校验当前电池状态 // 1. 解析并校验当前电池状态
boolean currentCharging = BatteryUtils.isCharging(intent); boolean currentCharging = BatteryUtils.isCharging(intent);
int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent); int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent);
currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX); currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX);
LogUtils.d(TAG, "handleBatteryStateChanged: 当前状态 | 充电=" + currentCharging + " | 电量=" + currentBatteryLevel + "%"); LogUtils.d(TAG, String.format("handleBatteryStateChanged: 当前状态 | 充电=%b | 电量=%d%%", currentCharging, currentBatteryLevel));
// 2. 状态无变化则跳过,减少无效运算 // 2. 状态无变化则跳过,减少无效运算
if (currentCharging == sIsCharging && currentBatteryLevel == sLastBatteryLevel) { if (currentCharging == sIsCharging && currentBatteryLevel == sLastBatteryLevel) {
@@ -108,13 +108,14 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
return; return;
} }
// 4. 更新静态缓存状态,保证多线程可见 // 3. 更新静态缓存状态,保证多线程可见
sIsCharging = currentCharging; sIsCharging = currentCharging;
sLastBatteryLevel = currentBatteryLevel; sLastBatteryLevel = currentBatteryLevel;
handleNotifyAppConfigUpdate(service);
LogUtils.d(TAG, "handleBatteryStateChanged: 电池状态处理成功 | 缓存电量=" + sLastBatteryLevel + "% | 缓存充电状态=" + sIsCharging); // 4. 同步缓存状态到配置
handleNotifyAppConfigUpdate(service);
LogUtils.d(TAG, String.format("handleBatteryStateChanged: 处理成功 | 缓存电量=%d%% | 缓存充电状态=%b", sLastBatteryLevel, sIsCharging));
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "handleBatteryStateChanged: 处理失败", e); LogUtils.e(TAG, "handleBatteryStateChanged: 处理失败", e);
} }
@@ -125,23 +126,24 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
* @param service 控制中心服务实例 * @param service 控制中心服务实例
*/ */
private void handleNotifyAppConfigUpdate(ControlCenterService service) { private void handleNotifyAppConfigUpdate(ControlCenterService service) {
LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 同步缓存状态到配置 | service=" + service); LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 同步缓存状态到配置");
try { try {
// 加载最新配置 // 加载最新配置
AppConfigBean latestConfig = AppConfigUtils.getInstance(service).loadAppConfig(); AppConfigBean latestConfig = AppConfigUtils.getInstance(service).loadAppConfig();
if (latestConfig == null) { // 新增:配置空指针防护 if (latestConfig == null) {
LogUtils.e(TAG, "handleNotifyAppConfigUpdate: 最新配置为空,终止处理"); LogUtils.e(TAG, "handleNotifyAppConfigUpdate: 最新配置为空,终止处理");
return; return;
} }
LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 加载最新配置 | 充电阈值=" + latestConfig.getChargeReminderValue() + " | 耗电阈值=" + latestConfig.getUsageReminderValue()); LogUtils.d(TAG, String.format("handleNotifyAppConfigUpdate: 加载最新配置 | 充电阈值=%d | 耗电阈值=%d",
latestConfig.getChargeReminderValue(), latestConfig.getUsageReminderValue()));
// 同步缓存的电池状态到配置 // 同步缓存的电池状态到配置
latestConfig.setCurrentBatteryValue(sLastBatteryLevel); latestConfig.setCurrentBatteryValue(sLastBatteryLevel);
latestConfig.setIsCharging(sIsCharging); latestConfig.setIsCharging(sIsCharging);
service.notifyAppConfigUpdate(latestConfig); service.notifyAppConfigUpdate(latestConfig);
LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 配置同步成功 | 缓存电量=" + sLastBatteryLevel + "% | 充电状态=" + sIsCharging); LogUtils.d(TAG, String.format("handleNotifyAppConfigUpdate: 配置同步成功 | 缓存电量=%d%% | 充电状态=%b", sLastBatteryLevel, sIsCharging));
LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 配置更新广播处理完成"); // 新增:标记配置广播处理终点 LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 配置更新广播处理完成");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "handleNotifyAppConfigUpdate: 处理失败", e); LogUtils.e(TAG, "handleNotifyAppConfigUpdate: 处理失败", e);
} }
@@ -152,32 +154,32 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
* @param service 控制中心服务实例 * @param service 控制中心服务实例
*/ */
private void handleUpdateForegroundNotification(ControlCenterService service) { private void handleUpdateForegroundNotification(ControlCenterService service) {
LogUtils.d(TAG, "handleUpdateForegroundNotification: 更新前台通知 | service=" + service); LogUtils.d(TAG, "handleUpdateForegroundNotification: 更新前台通知");
try { try {
NotificationManagerUtils notifyUtils = service.getNotificationManager(); NotificationManagerUtils notifyUtils = service.getNotificationManager();
NotificationMessage notifyMsg = service.getForegroundNotifyMsg(); NotificationMessage notifyMsg = service.getForegroundNotifyMsg();
// 非空校验,避免空指针 // 非空校验,避免空指针
if (notifyUtils == null || notifyMsg == null) { if (notifyUtils == null || notifyMsg == null) {
LogUtils.e(TAG, "handleUpdateForegroundNotification: 通知工具类或消息为空notifyUtils=" + notifyUtils + " | notifyMsg=" + notifyMsg + ""); LogUtils.e(TAG, String.format("handleUpdateForegroundNotification: 通知工具类或消息为空notifyUtils=%s | notifyMsg=%s", notifyUtils, notifyMsg));
return; return;
} }
notifyUtils.updateForegroundServiceNotify(notifyMsg); notifyUtils.updateForegroundServiceNotify(notifyMsg);
LogUtils.d(TAG, "handleUpdateForegroundNotification: 前台通知更新成功 | 通知标题=" + notifyMsg.getTitle()); LogUtils.d(TAG, String.format("handleUpdateForegroundNotification: 前台通知更新成功 | 标题=%s", notifyMsg.getTitle()));
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "handleUpdateForegroundNotification: 处理失败", e); LogUtils.e(TAG, "handleUpdateForegroundNotification: 处理失败", e);
} }
} }
// ================================== 广播注册/注销(强化容错,避免重复操作)================================= // ====================== 广播注册/注销(强化容错,避免重复操作) ======================
/** /**
* 注册广播接收器 * 注册广播接收器
* @param context 上下文 * @param context 上下文
*/ */
public void registerAction(Context context) { public void registerAction(Context context) {
LogUtils.d(TAG, "registerAction: 注册广播接收器 | context=" + context); LogUtils.d(TAG, "registerAction: 注册广播接收器");
if (context == null || isRegistered) { // 新增:已注册则跳过 if (context == null || isRegistered) {
LogUtils.e(TAG, "registerAction: 上下文为空或已注册,注册失败"); LogUtils.e(TAG, "registerAction: 上下文为空或已注册,注册失败");
return; return;
} }
@@ -190,8 +192,8 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
filter.setPriority(BROADCAST_PRIORITY); filter.setPriority(BROADCAST_PRIORITY);
context.registerReceiver(this, filter); context.registerReceiver(this, filter);
isRegistered = true; // 标记为已注册 isRegistered = true;
LogUtils.d(TAG, "registerAction: 广播注册成功 | 优先级=" + BROADCAST_PRIORITY); LogUtils.d(TAG, String.format("registerAction: 广播注册成功 | 优先级=%d", BROADCAST_PRIORITY));
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "registerAction: 注册失败", e); LogUtils.e(TAG, "registerAction: 注册失败", e);
} }
@@ -202,15 +204,15 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
* @param context 上下文 * @param context 上下文
*/ */
public void unregisterAction(Context context) { public void unregisterAction(Context context) {
LogUtils.d(TAG, "unregisterAction: 注销广播接收器 | context=" + context); LogUtils.d(TAG, "unregisterAction: 注销广播接收器");
if (context == null || !isRegistered) { // 新增:未注册则跳过 if (context == null || !isRegistered) {
LogUtils.e(TAG, "unregisterAction: 上下文为空或未注册,注销失败"); LogUtils.e(TAG, "unregisterAction: 上下文为空或未注册,注销失败");
return; return;
} }
try { try {
context.unregisterReceiver(this); context.unregisterReceiver(this);
isRegistered = false; // 标记为未注册 isRegistered = false;
LogUtils.d(TAG, "unregisterAction: 广播注销成功"); LogUtils.d(TAG, "unregisterAction: 广播注销成功");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LogUtils.w(TAG, "unregisterAction: 广播未注册,跳过注销"); LogUtils.w(TAG, "unregisterAction: 广播未注册,跳过注销");
@@ -219,7 +221,7 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
} }
} }
// ================================== 资源释放与Getter方法按需开放防泄漏================================= // ====================== 资源释放与Getter方法按需开放防泄漏 ======================
/** /**
* 主动释放资源,避免内存泄漏 * 主动释放资源,避免内存泄漏
*/ */
@@ -253,3 +255,4 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver {
return sIsCharging; return sIsCharging;
} }
} }

View File

@@ -18,110 +18,113 @@ import cc.winboll.studio.powerbell.utils.BatteryUtils;
* 适配Java7 | API30 | 内存泄漏防护 * 适配Java7 | API30 | 内存泄漏防护
*/ */
public class GlobalApplicationReceiver extends BroadcastReceiver { public class GlobalApplicationReceiver extends BroadcastReceiver {
// ================================== 静态常量区(置顶归类,消除魔法值)================================= // ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "GlobalApplicationReceiver"; public static final String TAG = "GlobalApplicationReceiver";
private static final int BATTERY_LEVEL_MIN = 0; private static final int BATTERY_LEVEL_MIN = 0;
private static final int BATTERY_LEVEL_MAX = 100; private static final int BATTERY_LEVEL_MAX = 100;
// ================================== 静态成员变量(线程安全,volatile保证多线程可见性================================= // ====================== 静态状态标记(volatile保证多线程可见性 ======================
private static volatile int sLastBatteryLevel = -1; // 历史电量0-100 private static volatile int sLastBatteryLevel = -1; // 历史电量0-100
private static volatile boolean sLastIsCharging = false; // 历史充电状态 private static volatile boolean sLastIsCharging = false; // 历史充电状态
// ================================== 成员变量区(按功能分层)================================= // ====================== 成员变量区按功能分层移除冗余的mCurrentReceiver ======================
private App mGlobalApplication; private App mGlobalApplication;
private AppConfigUtils mAppConfigUtils; private AppConfigUtils mAppConfigUtils;
private GlobalApplicationReceiver mCurrentReceiver;
// ================================== 构造方法(强化参数校验,初始化核心依赖)================================= // ====================== 构造方法(强化参数校验,初始化核心依赖) ======================
public GlobalApplicationReceiver(App globalApplication) { public GlobalApplicationReceiver(App globalApplication) {
LogUtils.d(TAG, "构造接收器 | App=" + globalApplication); LogUtils.d(TAG, String.format("构造接收器 | App实例:%s", globalApplication));
if (globalApplication == null) { if (globalApplication == null) {
LogUtils.e(TAG, "构造失败App实例为空"); LogUtils.e(TAG, "构造失败App实例为空");
throw new IllegalArgumentException("App cannot be null"); throw new IllegalArgumentException("App cannot be null");
} }
this.mCurrentReceiver = this;
this.mGlobalApplication = globalApplication; this.mGlobalApplication = globalApplication;
this.mAppConfigUtils = App.getAppConfigUtils(mGlobalApplication); this.mAppConfigUtils = App.getAppConfigUtils(mGlobalApplication);
LogUtils.d(TAG, "构造完成AppConfigUtils=" + mAppConfigUtils); LogUtils.d(TAG, String.format("构造完成 | AppConfigUtils%s", mAppConfigUtils));
} }
// ================================== 广播核心接收逻辑(入口方法,过滤电池状态广播)================================= // ====================== 广播核心接收逻辑(入口方法,过滤电池状态广播) ======================
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
LogUtils.d(TAG, "onReceive: 接收广播 | context=" + context + " | intent=" + intent + " | action=" + (intent != null ? intent.getAction() : "null")); String action = intent != null ? intent.getAction() : "null";
LogUtils.d(TAG, String.format("onReceive: 接收广播 | 上下文:%s | Action%s", context, action));
// 基础参数校验 // 基础参数校验
if (context == null || intent == null || intent.getAction() == null) { if (context == null || intent == null || action == null) {
LogUtils.e(TAG, "onReceive: 参数无效,终止处理"); LogUtils.e(TAG, "onReceive: 参数无效,终止处理");
return; return;
} }
// 仅处理电池状态变化广播 // 仅处理电池状态变化广播
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
handleBatteryStateChanged(context, intent); handleBatteryStateChanged(context, intent);
} }
LogUtils.d(TAG, "onReceive: 广播处理完成"); LogUtils.d(TAG, "onReceive: 广播处理完成");
} }
// ================================== 业务逻辑方法(处理电池状态变化,同步配置+通知页面)================================= // ====================== 业务逻辑方法(处理电池状态变化,同步配置+通知页面) ======================
/** /**
* 处理电池状态变化广播 * 处理电池状态变化广播
* @param context 上下文 * @param context 上下文
* @param intent 电池状态广播意图 * @param intent 电池状态广播意图
*/ */
private void handleBatteryStateChanged(Context context, Intent intent) { private void handleBatteryStateChanged(Context context, Intent intent) {
LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态 | intent=" + intent); LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态");
// 1. 解析当前电池状态(复用工具类,二次校验电量范围) try {
boolean currentIsCharging = BatteryUtils.isCharging(intent); // 1. 解析当前电池状态(复用工具类,二次校验电量范围)
int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent); boolean currentIsCharging = BatteryUtils.isCharging(intent);
currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX); int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent);
LogUtils.d(TAG, "handleBatteryStateChanged: 当前状态 | 充电=" + currentIsCharging + " | 电量=" + currentBatteryLevel + "%"); currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX);
LogUtils.d(TAG, String.format("handleBatteryStateChanged: 当前状态 | 充电=%b | 电量=%d%%", currentIsCharging, currentBatteryLevel));
// 2. 状态无变化则跳过,减少无效运算 // 2. 状态无变化则跳过,减少无效运算
if (currentIsCharging == sLastIsCharging && currentBatteryLevel == sLastBatteryLevel) { if (currentIsCharging == sLastIsCharging && currentBatteryLevel == sLastBatteryLevel) {
LogUtils.d(TAG, "handleBatteryStateChanged: 状态无变化,跳过处理"); LogUtils.d(TAG, "handleBatteryStateChanged: 状态无变化,跳过处理");
return; return;
}
// 3. 同步最新状态到配置工具类
if (mAppConfigUtils != null) {
if (currentIsCharging != sLastIsCharging) {
mAppConfigUtils.setCharging(currentIsCharging);
LogUtils.d(TAG, "handleBatteryStateChanged: 同步充电状态 | " + currentIsCharging);
} }
if (currentBatteryLevel != sLastBatteryLevel) {
mAppConfigUtils.setCurrentBatteryValue(currentBatteryLevel); // 3. 同步最新状态到配置工具类
LogUtils.d(TAG, "handleBatteryStateChanged: 同步电量 | " + currentBatteryLevel + "%"); if (mAppConfigUtils != null) {
if (currentIsCharging != sLastIsCharging) {
mAppConfigUtils.setCharging(currentIsCharging);
LogUtils.d(TAG, String.format("handleBatteryStateChanged: 同步充电状态 | %b", currentIsCharging));
}
if (currentBatteryLevel != sLastBatteryLevel) {
mAppConfigUtils.setCurrentBatteryValue(currentBatteryLevel);
LogUtils.d(TAG, String.format("handleBatteryStateChanged: 同步电量 | %d%%", currentBatteryLevel));
}
} else {
LogUtils.e(TAG, "handleBatteryStateChanged: AppConfigUtils为空同步失败");
} }
} else {
LogUtils.e(TAG, "handleBatteryStateChanged: AppConfigUtils为空同步失败");
}
// 4. 执行状态变化后的业务逻辑 // 4. 执行状态变化后的业务逻辑
// 记录电量变化时间 // 记录电量变化时间
if (App.getAppCacheUtils(context) != null) { if (App.getAppCacheUtils(context) != null) {
App.getAppCacheUtils(context).addChangingTime(currentBatteryLevel); App.getAppCacheUtils(context).addChangingTime(currentBatteryLevel);
LogUtils.d(TAG, "handleBatteryStateChanged: 记录电量变化时间"); LogUtils.d(TAG, "handleBatteryStateChanged: 记录电量变化时间");
} }
// 通知MainActivity更新电量 // 通知MainActivity更新电量
MainActivity.sendCurrentBatteryValueMessage(currentBatteryLevel); MainActivity.sendCurrentBatteryValueMessage(currentBatteryLevel);
LogUtils.d(TAG, "handleBatteryStateChanged: 发送电量更新消息到MainActivity"); LogUtils.d(TAG, String.format("handleBatteryStateChanged: 发送电量更新消息到MainActivity | %d%%", currentBatteryLevel));
// 5. 更新历史状态缓存 // 5. 更新历史状态缓存
sLastIsCharging = currentIsCharging; sLastIsCharging = currentIsCharging;
sLastBatteryLevel = currentBatteryLevel; sLastBatteryLevel = currentBatteryLevel;
LogUtils.d(TAG, "handleBatteryStateChanged: 更新历史状态完成"); LogUtils.d(TAG, "handleBatteryStateChanged: 更新历史状态完成");
} catch (Exception e) {
LogUtils.e(TAG, "handleBatteryStateChanged: 处理失败", e);
}
} }
// ================================== 广播注册/注销(强化容错,避免重复操作)================================= // ====================== 广播注册/注销(强化容错,避免重复操作) ======================
/** /**
* 注册广播接收器 * 注册广播接收器
*/ */
public void registerAction() { public void registerAction() {
LogUtils.d(TAG, "registerAction: 注册广播"); LogUtils.d(TAG, "registerAction: 注册广播");
if (mGlobalApplication == null || mCurrentReceiver == null) { if (mGlobalApplication == null) {
LogUtils.e(TAG, "注册失败App或Receiver实例为空"); LogUtils.e(TAG, "注册失败App实例为空");
return; return;
} }
@@ -130,7 +133,7 @@ public class GlobalApplicationReceiver extends BroadcastReceiver {
unregisterAction(); unregisterAction();
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mGlobalApplication.registerReceiver(mCurrentReceiver, filter); mGlobalApplication.registerReceiver(this, filter);
LogUtils.d(TAG, "registerAction: 广播注册成功"); LogUtils.d(TAG, "registerAction: 广播注册成功");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "registerAction: 注册失败", e); LogUtils.e(TAG, "registerAction: 注册失败", e);
@@ -142,13 +145,13 @@ public class GlobalApplicationReceiver extends BroadcastReceiver {
*/ */
public void unregisterAction() { public void unregisterAction() {
LogUtils.d(TAG, "unregisterAction: 注销广播"); LogUtils.d(TAG, "unregisterAction: 注销广播");
if (mGlobalApplication == null || mCurrentReceiver == null) { if (mGlobalApplication == null) {
LogUtils.e(TAG, "注销失败App或Receiver实例为空"); LogUtils.e(TAG, "注销失败App实例为空");
return; return;
} }
try { try {
mGlobalApplication.unregisterReceiver(mCurrentReceiver); mGlobalApplication.unregisterReceiver(this);
LogUtils.d(TAG, "unregisterAction: 广播注销成功"); LogUtils.d(TAG, "unregisterAction: 广播注销成功");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LogUtils.w(TAG, "unregisterAction: 广播未注册,跳过注销"); LogUtils.w(TAG, "unregisterAction: 广播未注册,跳过注销");
@@ -157,7 +160,7 @@ public class GlobalApplicationReceiver extends BroadcastReceiver {
} }
} }
// ================================== 资源释放方法(主动释放,彻底避免内存泄漏)================================= // ====================== 资源释放方法(主动释放,彻底避免内存泄漏) ======================
/** /**
* 释放接收器资源供App销毁时调用 * 释放接收器资源供App销毁时调用
*/ */
@@ -168,7 +171,6 @@ public class GlobalApplicationReceiver extends BroadcastReceiver {
// 置空引用帮助GC回收 // 置空引用帮助GC回收
mGlobalApplication = null; mGlobalApplication = null;
mAppConfigUtils = null; mAppConfigUtils = null;
mCurrentReceiver = null;
// 重置静态状态缓存 // 重置静态状态缓存
sLastBatteryLevel = -1; sLastBatteryLevel = -1;
sLastIsCharging = false; sLastIsCharging = false;

View File

@@ -1,10 +1,5 @@
package cc.winboll.studio.powerbell.receivers; package cc.winboll.studio.powerbell.receivers;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/06 15:01:39
* @Describe 应用广播消息接收类
*/
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -14,30 +9,84 @@ import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.services.ControlCenterService; import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.utils.ServiceUtils; import cc.winboll.studio.powerbell.utils.ServiceUtils;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/06 15:01:39
* @Describe 应用核心广播接收器
* 功能:监听开机完成广播,实现服务开机自启
* 适配Java7 | API30 | 服务启动兼容性处理
*/
public class MainReceiver extends BroadcastReceiver { public class MainReceiver extends BroadcastReceiver {
// ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "MainReceiver"; public static final String TAG = "MainReceiver";
// 系统广播Action常量
private static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
// API版本常量适配前台服务启动要求
private static final int API_LEVEL_26 = 26;
static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED"; // ====================== 静态状态标记volatile保证多线程可见性 ======================
// 存储电量指示值, // 历史电量值,用于校验电量变化(暂未使用,保留扩展能力)
// 用于校验电量消息时的电量变化 private static volatile int sLastBatteryLevel = -1;
static volatile int _mnTheQuantityOfElectricityOld = -1;
// ====================== 广播核心接收逻辑入口方法分Action处理 ======================
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String szAction = intent.getAction(); // 基础参数校验
if (szAction.equals(ACTION_BOOT_COMPLETED)) { if (context == null || intent == null) {
boolean isEnableService = App.getAppConfigUtils(context).isServiceEnabled(); LogUtils.e(TAG, "onReceive: 上下文或意图为空,终止处理");
if (isEnableService) { return;
if (ServiceUtils.isServiceAlive(context.getApplicationContext(), ControlCenterService.class.getName()) == false) { }
LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService");
if (Build.VERSION.SDK_INT >= 26) { String action = intent.getAction();
context.startForegroundService(new Intent(context, ControlCenterService.class)); LogUtils.d(TAG, String.format("onReceive: 接收广播 | Action%s", action));
} else {
context.startService(new Intent(context, ControlCenterService.class)); // 仅处理开机完成广播
} if (ACTION_BOOT_COMPLETED.equals(action)) {
} handleBootCompleted(context);
} else {
LogUtils.w(TAG, String.format("onReceive: 忽略未知Action%s", action));
}
}
// ====================== 业务处理方法(处理开机完成广播,实现服务自启) ======================
/**
* 处理开机完成广播,自动启动控制中心服务
* @param context 上下文
*/
private void handleBootCompleted(Context context) {
LogUtils.d(TAG, "handleBootCompleted: 开始处理开机完成广播");
try {
// 1. 校验服务启用状态
boolean isServiceEnabled = App.getAppConfigUtils(context).isServiceEnabled();
LogUtils.d(TAG, String.format("handleBootCompleted: 服务启用状态:%b", isServiceEnabled));
if (!isServiceEnabled) {
LogUtils.d(TAG, "handleBootCompleted: 服务未启用,跳过自启");
return;
} }
// 2. 校验服务是否已运行
String serviceClassName = ControlCenterService.class.getName();
boolean isServiceAlive = ServiceUtils.isServiceAlive(context.getApplicationContext(), serviceClassName);
LogUtils.d(TAG, String.format("handleBootCompleted: 服务运行状态:%b", isServiceAlive));
if (isServiceAlive) {
LogUtils.d(TAG, "handleBootCompleted: 服务已运行,无需重复启动");
return;
}
// 3. 按API版本启动服务适配前台服务要求
Intent serviceIntent = new Intent(context, ControlCenterService.class);
if (Build.VERSION.SDK_INT >= API_LEVEL_26) {
context.startForegroundService(serviceIntent);
LogUtils.d(TAG, "handleBootCompleted: 启动前台服务API >= 26");
} else {
context.startService(serviceIntent);
LogUtils.d(TAG, "handleBootCompleted: 启动普通服务API < 26");
}
LogUtils.d(TAG, "handleBootCompleted: 服务自启处理完成");
} catch (Exception e) {
LogUtils.e(TAG, "handleBootCompleted: 服务自启失败", e);
} }
} }
} }

View File

@@ -16,21 +16,25 @@ import cc.winboll.studio.powerbell.utils.ServiceUtils;
* 电池提醒核心服务进程守护类 * 电池提醒核心服务进程守护类
* 功能:监听主服务 {@link ControlCenterService} 存活状态,异常断开时自动重启并绑定 * 功能:监听主服务 {@link ControlCenterService} 存活状态,异常断开时自动重启并绑定
* 适配Java7 | API30 | 前台服务启动规则 | 服务绑定稳定性保障 * 适配Java7 | API30 | 前台服务启动规则 | 服务绑定稳定性保障
* @Author ZhanGSKen<zhangsken@qq.com>
* @Describe 守护服务保障ControlCenterService持续运行
*/ */
public class AssistantService extends Service { public class AssistantService extends Service {
// ================================== 静态常量区(置顶归类,消除魔法值)================================= // ====================== 静态常量区(置顶归类,消除魔法值) ======================
private static final String TAG = "AssistantService"; private static final String TAG = "AssistantService";
// 服务返回策略常量(统一定义,避免魔法值) // 服务返回策略常量
private static final int SERVICE_RETURN_STICKY = START_STICKY; private static final int SERVICE_RETURN_STICKY = START_STICKY;
// 服务绑定标记常量 // 服务绑定标记常量
private static final int BIND_FLAG = Context.BIND_IMPORTANT; private static final int BIND_FLAG = Context.BIND_IMPORTANT;
// API版本常量适配前台服务启动要求
private static final int API_LEVEL_26 = Build.VERSION_CODES.O;
// ================================== 成员变量区按功能分层volatile保证多线程可见性================================= // ====================== 成员变量区按功能分层volatile保证多线程可见性 ======================
private AppConfigUtils mAppConfigUtils; private AppConfigUtils mAppConfigUtils;
private MyServiceConnection mMyServiceConnection; private MyServiceConnection mMyServiceConnection;
private volatile boolean mIsThreadAlive; private volatile boolean mIsThreadAlive;
// ================================== 内部类(服务连接状态监听,前置定义便于引用)================================= // ====================== 内部类(服务连接状态监听,前置定义便于引用) ======================
/** /**
* 服务连接状态监听器 * 服务连接状态监听器
* 主服务连接成功时记录状态,断开时自动重连 * 主服务连接成功时记录状态,断开时自动重连
@@ -38,12 +42,14 @@ public class AssistantService extends Service {
private class MyServiceConnection implements ServiceConnection { private class MyServiceConnection implements ServiceConnection {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.d(TAG, "onServiceConnected: 主服务连接成功 | 组件名=" + name.getClassName() + " | Binder=" + service); String className = name != null ? name.getClassName() : "null";
LogUtils.d(TAG, String.format("onServiceConnected: 主服务连接成功 | 组件名=%s | Binder=%s", className, service));
} }
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
LogUtils.d(TAG, "onServiceDisconnected: 主服务连接断开 | 组件名=" + name.getClassName()); String className = name != null ? name.getClassName() : "null";
LogUtils.d(TAG, String.format("onServiceDisconnected: 主服务连接断开 | 组件名=%s", className));
// 主服务断开且配置启用时,重新唤醒绑定 // 主服务断开且配置启用时,重新唤醒绑定
if (mAppConfigUtils != null && mAppConfigUtils.isServiceEnabled()) { if (mAppConfigUtils != null && mAppConfigUtils.isServiceEnabled()) {
LogUtils.d(TAG, "onServiceDisconnected: 配置启用,尝试重新唤醒并绑定主服务"); LogUtils.d(TAG, "onServiceDisconnected: 配置启用,尝试重新唤醒并绑定主服务");
@@ -52,11 +58,11 @@ public class AssistantService extends Service {
} }
} }
// ================================== 服务生命周期方法按执行顺序排列onCreate→onStartCommand→onBind→onDestroy================================= // ====================== 服务生命周期方法按执行顺序排列onCreate→onStartCommand→onBind→onDestroy ======================
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
LogUtils.d(TAG, "onCreate: 守护服务启动 | 进程ID=" + android.os.Process.myPid()); LogUtils.d(TAG, String.format("onCreate: 守护服务启动 | 进程ID=%d", android.os.Process.myPid()));
// 初始化配置工具类,添加空指针防护 // 初始化配置工具类,添加空指针防护
mAppConfigUtils = App.getAppConfigUtils(this); mAppConfigUtils = App.getAppConfigUtils(this);
@@ -75,12 +81,12 @@ public class AssistantService extends Service {
// 初始化运行状态,执行核心守护逻辑 // 初始化运行状态,执行核心守护逻辑
mIsThreadAlive = false; mIsThreadAlive = false;
run(); run();
LogUtils.d(TAG, "onCreate: 守护服务初始化完成 | 服务启用状态=" + mAppConfigUtils.isServiceEnabled()); LogUtils.d(TAG, String.format("onCreate: 守护服务初始化完成 | 服务启用状态=%b", mAppConfigUtils.isServiceEnabled()));
} }
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand: 守护服务触发重启 | flags=" + flags + " | startId=" + startId); LogUtils.d(TAG, String.format("onStartCommand: 守护服务触发重启 | flags=%d | startId=%d", flags, startId));
// 配置工具类为空时,直接返回非粘性策略 // 配置工具类为空时,直接返回非粘性策略
if (mAppConfigUtils == null) { if (mAppConfigUtils == null) {
LogUtils.e(TAG, "onStartCommand: AppConfigUtils未初始化终止服务"); LogUtils.e(TAG, "onStartCommand: AppConfigUtils未初始化终止服务");
@@ -90,13 +96,13 @@ public class AssistantService extends Service {
run(); run();
int returnFlag = mAppConfigUtils.isServiceEnabled() ? SERVICE_RETURN_STICKY : super.onStartCommand(intent, flags, startId); int returnFlag = mAppConfigUtils.isServiceEnabled() ? SERVICE_RETURN_STICKY : super.onStartCommand(intent, flags, startId);
LogUtils.d(TAG, "onStartCommand: 处理完成 | 返回策略=" + (returnFlag == SERVICE_RETURN_STICKY ? "START_STICKY" : "DEFAULT")); LogUtils.d(TAG, String.format("onStartCommand: 处理完成 | 返回策略=%s", returnFlag == SERVICE_RETURN_STICKY ? "START_STICKY" : "DEFAULT"));
return returnFlag; return returnFlag;
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
LogUtils.d(TAG, "onBind: 服务绑定请求 | intent=" + intent); LogUtils.d(TAG, String.format("onBind: 服务绑定请求 | intent=%s", intent));
return null; return null;
} }
@@ -116,14 +122,15 @@ public class AssistantService extends Service {
LogUtils.d(TAG, "onDestroy: 守护服务销毁完成"); LogUtils.d(TAG, "onDestroy: 守护服务销毁完成");
} }
// ================================== 核心业务逻辑(守护主服务存活)================================= // ====================== 核心业务逻辑(守护主服务存活) ======================
/** /**
* 执行守护逻辑:检查主服务状态,按需唤醒并绑定 * 执行守护逻辑:检查主服务状态,按需唤醒并绑定
* 前置条件mAppConfigUtils 必须初始化完成 * 前置条件mAppConfigUtils 必须初始化完成
*/ */
private void run() { private void run() {
LogUtils.d(TAG, "run: 执行守护逻辑 | 配置启用=" + mAppConfigUtils.isServiceEnabled() + " | 线程存活=" + mIsThreadAlive); boolean isServiceEnabled = mAppConfigUtils.isServiceEnabled();
if (mAppConfigUtils.isServiceEnabled()) { LogUtils.d(TAG, String.format("run: 执行守护逻辑 | 配置启用=%b | 线程存活=%b", isServiceEnabled, mIsThreadAlive));
if (isServiceEnabled) {
if (!mIsThreadAlive) { if (!mIsThreadAlive) {
mIsThreadAlive = true; mIsThreadAlive = true;
wakeupAndBindMain(); wakeupAndBindMain();
@@ -141,13 +148,14 @@ public class AssistantService extends Service {
*/ */
private void wakeupAndBindMain() { private void wakeupAndBindMain() {
// 检查主服务存活状态 // 检查主服务存活状态
boolean isMainServiceAlive = ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName()); String mainServiceName = ControlCenterService.class.getName();
LogUtils.d(TAG, "wakeupAndBindMain: 主服务存活状态=" + isMainServiceAlive); boolean isMainServiceAlive = ServiceUtils.isServiceAlive(getApplicationContext(), mainServiceName);
LogUtils.d(TAG, String.format("wakeupAndBindMain: 主服务存活状态=%b", isMainServiceAlive));
// 主服务未存活时按需启动区分API版本 // 主服务未存活时按需启动区分API版本
if (!isMainServiceAlive) { if (!isMainServiceAlive) {
Intent mainServiceIntent = new Intent(AssistantService.this, ControlCenterService.class); Intent mainServiceIntent = new Intent(AssistantService.this, ControlCenterService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= API_LEVEL_26) {
startForegroundService(mainServiceIntent); startForegroundService(mainServiceIntent);
LogUtils.d(TAG, "wakeupAndBindMain: API26+ 以前台服务方式启动主服务"); LogUtils.d(TAG, "wakeupAndBindMain: API26+ 以前台服务方式启动主服务");
} else { } else {
@@ -159,10 +167,10 @@ public class AssistantService extends Service {
// 绑定主服务,监听连接状态,添加结果日志 // 绑定主服务,监听连接状态,添加结果日志
Intent bindIntent = new Intent(AssistantService.this, ControlCenterService.class); Intent bindIntent = new Intent(AssistantService.this, ControlCenterService.class);
boolean bindResult = bindService(bindIntent, mMyServiceConnection, BIND_FLAG); boolean bindResult = bindService(bindIntent, mMyServiceConnection, BIND_FLAG);
LogUtils.d(TAG, "wakeupAndBindMain: 绑定主服务结果=" + bindResult + " | 绑定标记=BIND_IMPORTANT"); LogUtils.d(TAG, String.format("wakeupAndBindMain: 绑定主服务结果=%b | 绑定标记=BIND_IMPORTANT", bindResult));
} }
// ================================== 辅助工具方法(拆分独立逻辑,提高可维护性)================================= // ====================== 辅助工具方法(拆分独立逻辑,提高可维护性) ======================
/** /**
* 解绑主服务,包含异常捕获与状态日志 * 解绑主服务,包含异常捕获与状态日志
*/ */
@@ -172,7 +180,7 @@ public class AssistantService extends Service {
unbindService(mMyServiceConnection); unbindService(mMyServiceConnection);
LogUtils.d(TAG, "unbindMainService: 已成功解绑ControlCenterService"); LogUtils.d(TAG, "unbindMainService: 已成功解绑ControlCenterService");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LogUtils.w(TAG, "unbindMainService: 解绑服务失败,服务未绑定 | " + e.getMessage()); LogUtils.w(TAG, String.format("unbindMainService: 解绑服务失败,服务未绑定 | %s", e.getMessage()));
} }
mMyServiceConnection = null; mMyServiceConnection = null;
} }

View File

@@ -24,22 +24,30 @@ import java.util.List;
* 电池提醒核心服务 * 电池提醒核心服务
* 功能:管理前台服务生命周期、控制提醒线程启停、处理配置更新 * 功能:管理前台服务生命周期、控制提醒线程启停、处理配置更新
* 适配Java7 | API30 | 前台服务超时防护 | 电池优化忽略引导 * 适配Java7 | API30 | 前台服务超时防护 | 电池优化忽略引导
* @Author ZhanGSKen<zhangsken@qq.com>
* @Describe 核心服务:实现电池监测、提醒控制与前台服务保活
*/ */
public class ControlCenterService extends Service { public class ControlCenterService extends Service {
// ================================== 静态常量区(置顶归类,消除魔法值)================================= // ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "ControlCenterService"; public static final String TAG = "ControlCenterService";
// 线程与服务常量
private static final long THREAD_STOP_TIMEOUT = 1000L; private static final long THREAD_STOP_TIMEOUT = 1000L;
private static final int SERVICE_RETURN_STICKY = START_STICKY; private static final int SERVICE_RETURN_STICKY = START_STICKY;
private static final int RUNNING_SERVICE_LIST_LIMIT = 100;
// 默认配置常量
private static final int DEFAULT_CHARGE_REMINDER_VALUE = 80; private static final int DEFAULT_CHARGE_REMINDER_VALUE = 80;
private static final int DEFAULT_USAGE_REMINDER_VALUE = 20; private static final int DEFAULT_USAGE_REMINDER_VALUE = 20;
private static final int DEFAULT_BATTERY_DETECT_INTERVAL = 1000; private static final int DEFAULT_BATTERY_DETECT_INTERVAL = 1000;
private static final int RUNNING_SERVICE_LIST_LIMIT = 100; // API版本常量
private static final int API_LEVEL_26 = Build.VERSION_CODES.O;
private static final int API_LEVEL_30 = Build.VERSION_CODES.R;
private static final int API_LEVEL_23 = Build.VERSION_CODES.M;
// ================================== 静态状态标记volatile保证多线程可见性================================= // ====================== 静态状态标记volatile保证多线程可见性 ======================
private static volatile boolean isServiceRunning = false; private static volatile boolean isServiceRunning = false;
private static volatile boolean mIsDestroyed = true; private static volatile boolean mIsDestroyed = true;
// ================================== 成员变量区(按功能分层:配置→核心组件→通知相关)================================= // ====================== 成员变量区(按功能分层:配置→核心组件→通知相关) ======================
// 服务控制配置 // 服务控制配置
private ControlCenterServiceBean mServiceControlBean; private ControlCenterServiceBean mServiceControlBean;
private AppConfigBean mCurrentConfigBean; private AppConfigBean mCurrentConfigBean;
@@ -50,31 +58,33 @@ public class ControlCenterService extends Service {
private NotificationManagerUtils mNotificationManager; private NotificationManagerUtils mNotificationManager;
private NotificationMessage mForegroundNotifyMsg; private NotificationMessage mForegroundNotifyMsg;
// ================================== 服务生命周期方法按执行顺序onCreate→onStartCommand→onBind→onDestroy================================= // ====================== 服务生命周期方法按执行顺序onCreate→onStartCommand→onBind→onDestroy ======================
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
LogUtils.d(TAG, "onCreate执行 | 线程=" + Thread.currentThread().getName() + " | 进程ID=" + android.os.Process.myPid()); LogUtils.d(TAG, String.format("onCreate执行 | 线程=%s | 进程ID=%d", Thread.currentThread().getName(), android.os.Process.myPid()));
runCoreServiceLogic(); runCoreServiceLogic();
LogUtils.d(TAG, "onCreate完成 | 前台状态=" + isServiceRunning + " | 服务启用=" + (mServiceControlBean != null && mServiceControlBean.isEnableService())); boolean serviceEnabled = mServiceControlBean != null && mServiceControlBean.isEnableService();
LogUtils.d(TAG, String.format("onCreate完成 | 前台状态=%b | 服务启用=%b", isServiceRunning, serviceEnabled));
} }
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand执行 | startId=" + startId + " | action=" + (intent != null ? intent.getAction() : "null")); String action = intent != null ? intent.getAction() : "null";
LogUtils.d(TAG, String.format("onStartCommand执行 | startId=%d | action=%s", startId, action));
loadLatestServiceControlConfig(); loadLatestServiceControlConfig();
runCoreServiceLogic(); runCoreServiceLogic();
int returnFlag = (mServiceControlBean != null && mServiceControlBean.isEnableService()) int returnFlag = (mServiceControlBean != null && mServiceControlBean.isEnableService())
? SERVICE_RETURN_STICKY ? SERVICE_RETURN_STICKY
: super.onStartCommand(intent, flags, startId); : super.onStartCommand(intent, flags, startId);
LogUtils.d(TAG, "onStartCommand完成 | 返回策略=" + (returnFlag == SERVICE_RETURN_STICKY ? "START_STICKY" : "DEFAULT")); LogUtils.d(TAG, String.format("onStartCommand完成 | 返回策略=%s", returnFlag == SERVICE_RETURN_STICKY ? "START_STICKY" : "DEFAULT"));
return returnFlag; return returnFlag;
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
LogUtils.d(TAG, "onBind执行 | intent=" + intent); LogUtils.d(TAG, String.format("onBind执行 | intent=%s", intent));
return null; return null;
} }
@@ -101,7 +111,7 @@ public class ControlCenterService extends Service {
LogUtils.d(TAG, "onDestroy完成服务销毁完成"); LogUtils.d(TAG, "onDestroy完成服务销毁完成");
} }
// ================================== 核心业务逻辑(独立抽取,统一调用)================================= // ====================== 核心业务逻辑(独立抽取,统一调用) ======================
/** /**
* 服务核心运行逻辑在onCreate/onStartCommand复用 * 服务核心运行逻辑在onCreate/onStartCommand复用
* 避免重复初始化,保证前台服务优先启动 * 避免重复初始化,保证前台服务优先启动
@@ -111,7 +121,7 @@ public class ControlCenterService extends Service {
loadLatestServiceControlConfig(); loadLatestServiceControlConfig();
boolean serviceEnabled = mServiceControlBean != null && mServiceControlBean.isEnableService(); boolean serviceEnabled = mServiceControlBean != null && mServiceControlBean.isEnableService();
LogUtils.d(TAG, "runCoreServiceLogic服务启用=" + serviceEnabled + " | 已运行=" + isServiceRunning + " | 已销毁=" + mIsDestroyed); LogUtils.d(TAG, String.format("runCoreServiceLogic服务启用=%b | 已运行=%b | 已销毁=%b", serviceEnabled, isServiceRunning, mIsDestroyed));
if (serviceEnabled && !isServiceRunning) { if (serviceEnabled && !isServiceRunning) {
isServiceRunning = true; isServiceRunning = true;
@@ -131,7 +141,7 @@ public class ControlCenterService extends Service {
} }
} }
// ================================== 前台通知管理优先执行防止API26+前台服务5秒超时================================= // ====================== 前台通知管理优先执行防止API26+前台服务5秒超时 ======================
/** /**
* 立即初始化前台通知防止API26+前台服务超时异常 * 立即初始化前台通知防止API26+前台服务超时异常
* @return true=成功 false=失败 * @return true=成功 false=失败
@@ -154,7 +164,7 @@ public class ControlCenterService extends Service {
mNotificationManager.startForegroundServiceNotify(this, mForegroundNotifyMsg); mNotificationManager.startForegroundServiceNotify(this, mForegroundNotifyMsg);
ToastUtils.show("电池监测服务已启动"); ToastUtils.show("电池监测服务已启动");
LogUtils.d(TAG, "initForegroundNotificationImmediately前台通知发送成功 | ID=" + NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE); LogUtils.d(TAG, String.format("initForegroundNotificationImmediately前台通知发送成功 | ID=%d", NotificationManagerUtils.NOTIFY_ID_FOREGROUND_SERVICE));
return true; return true;
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "initForegroundNotificationImmediately通知初始化异常", e); LogUtils.e(TAG, "initForegroundNotificationImmediately通知初始化异常", e);
@@ -175,7 +185,7 @@ public class ControlCenterService extends Service {
} }
} }
// ================================== 配置管理(本地持久化+内存同步)================================= // ====================== 配置管理(本地持久化+内存同步) ======================
/** /**
* 加载本地最新服务控制配置 * 加载本地最新服务控制配置
*/ */
@@ -184,7 +194,7 @@ public class ControlCenterService extends Service {
ControlCenterServiceBean latestBean = ControlCenterServiceBean.loadBean(this, ControlCenterServiceBean.class); ControlCenterServiceBean latestBean = ControlCenterServiceBean.loadBean(this, ControlCenterServiceBean.class);
if (latestBean != null) { if (latestBean != null) {
mServiceControlBean = latestBean; mServiceControlBean = latestBean;
LogUtils.d(TAG, "loadLatestServiceControlConfig配置读取成功 | 启用=" + mServiceControlBean.isEnableService()); LogUtils.d(TAG, String.format("loadLatestServiceControlConfig配置读取成功 | 启用=%b", mServiceControlBean.isEnableService()));
} else { } else {
LogUtils.w(TAG, "loadLatestServiceControlConfig本地无配置沿用内存配置"); LogUtils.w(TAG, "loadLatestServiceControlConfig本地无配置沿用内存配置");
} }
@@ -202,13 +212,14 @@ public class ControlCenterService extends Service {
mCurrentConfigBean.setEnableUsageReminder(true); mCurrentConfigBean.setEnableUsageReminder(true);
mCurrentConfigBean.setUsageReminderValue(DEFAULT_USAGE_REMINDER_VALUE); mCurrentConfigBean.setUsageReminderValue(DEFAULT_USAGE_REMINDER_VALUE);
mCurrentConfigBean.setBatteryDetectInterval(DEFAULT_BATTERY_DETECT_INTERVAL); mCurrentConfigBean.setBatteryDetectInterval(DEFAULT_BATTERY_DETECT_INTERVAL);
LogUtils.d(TAG, "loadDefaultConfig默认配置加载完成 | 充电阈值=" + DEFAULT_CHARGE_REMINDER_VALUE + " | 耗电阈值=" + DEFAULT_USAGE_REMINDER_VALUE + " | 检测间隔=" + DEFAULT_BATTERY_DETECT_INTERVAL + "ms"); LogUtils.d(TAG, String.format("loadDefaultConfig默认配置加载完成 | 充电阈值=%d | 耗电阈值=%d | 检测间隔=%dms",
DEFAULT_CHARGE_REMINDER_VALUE, DEFAULT_USAGE_REMINDER_VALUE, DEFAULT_BATTERY_DETECT_INTERVAL));
} else { } else {
LogUtils.d(TAG, "loadDefaultConfig内存已有配置无需加载"); LogUtils.d(TAG, "loadDefaultConfig内存已有配置无需加载");
} }
} }
// ================================== 业务组件初始化与销毁Handler/广播/线程等)================================= // ====================== 业务组件初始化与销毁Handler/广播/线程等) ======================
/** /**
* 初始化Handler等核心业务组件 * 初始化Handler等核心业务组件
*/ */
@@ -225,7 +236,7 @@ public class ControlCenterService extends Service {
if (mControlCenterServiceReceiver == null) { if (mControlCenterServiceReceiver == null) {
mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this); mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this);
mControlCenterServiceReceiver.registerAction(this); mControlCenterServiceReceiver.registerAction(this);
LogUtils.d(TAG, "initServiceBusinessLogic广播接收器初始化并注册完成 | 接收器=" + mControlCenterServiceReceiver); LogUtils.d(TAG, "initServiceBusinessLogic广播接收器初始化并注册完成");
} else { } else {
LogUtils.d(TAG, "initServiceBusinessLogic广播接收器已存在"); LogUtils.d(TAG, "initServiceBusinessLogic广播接收器已存在");
} }
@@ -283,13 +294,13 @@ public class ControlCenterService extends Service {
LogUtils.d(TAG, "clearAllReferences引用清理完成"); LogUtils.d(TAG, "clearAllReferences引用清理完成");
} }
// ================================== 外部调用接口(静态方法,提供服务启停/配置更新入口)================================= // ====================== 外部调用接口(静态方法,提供服务启停/配置更新入口) ======================
/** /**
* 外部启动服务的统一入口 * 外部启动服务的统一入口
* @param context 上下文 * @param context 上下文
*/ */
public static void startControlCenterService(Context context) { public static void startControlCenterService(Context context) {
LogUtils.d(TAG, "startControlCenterService执行 | context=" + context); LogUtils.d(TAG, String.format("startControlCenterService执行 | context=%s", context));
if (context == null) { if (context == null) {
LogUtils.e(TAG, "startControlCenterServiceContext为空启动失败"); LogUtils.e(TAG, "startControlCenterServiceContext为空启动失败");
return; return;
@@ -298,11 +309,11 @@ public class ControlCenterService extends Service {
// 保存启用配置 // 保存启用配置
ControlCenterServiceBean controlBean = new ControlCenterServiceBean(true); ControlCenterServiceBean controlBean = new ControlCenterServiceBean(true);
ControlCenterServiceBean.saveBean(context, controlBean); ControlCenterServiceBean.saveBean(context, controlBean);
LogUtils.d(TAG, "startControlCenterService服务启用配置已保存 | 配置=" + controlBean); LogUtils.d(TAG, "startControlCenterService服务启用配置已保存");
// 启动服务区分API版本 // 启动服务区分API版本
Intent intent = new Intent(context, ControlCenterService.class); Intent intent = new Intent(context, ControlCenterService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= API_LEVEL_26) {
context.startForegroundService(intent); context.startForegroundService(intent);
LogUtils.d(TAG, "startControlCenterService以前台服务方式启动API26+"); LogUtils.d(TAG, "startControlCenterService以前台服务方式启动API26+");
} else { } else {
@@ -316,7 +327,7 @@ public class ControlCenterService extends Service {
* @param context 上下文 * @param context 上下文
*/ */
public static void stopControlCenterService(Context context) { public static void stopControlCenterService(Context context) {
LogUtils.d(TAG, "stopControlCenterService执行 | context=" + context); LogUtils.d(TAG, String.format("stopControlCenterService执行 | context=%s", context));
if (context == null) { if (context == null) {
LogUtils.e(TAG, "stopControlCenterServiceContext为空停止失败"); LogUtils.e(TAG, "stopControlCenterServiceContext为空停止失败");
return; return;
@@ -325,7 +336,7 @@ public class ControlCenterService extends Service {
// 保存停用配置 // 保存停用配置
ControlCenterServiceBean controlBean = new ControlCenterServiceBean(false); ControlCenterServiceBean controlBean = new ControlCenterServiceBean(false);
ControlCenterServiceBean.saveBean(context, controlBean); ControlCenterServiceBean.saveBean(context, controlBean);
LogUtils.d(TAG, "stopControlCenterService服务停用配置已保存 | 配置=" + controlBean); LogUtils.d(TAG, "stopControlCenterService服务停用配置已保存");
// 停止服务 // 停止服务
Intent intent = new Intent(context, ControlCenterService.class); Intent intent = new Intent(context, ControlCenterService.class);
@@ -337,28 +348,26 @@ public class ControlCenterService extends Service {
* 外部更新配置并触发线程重启 * 外部更新配置并触发线程重启
* @param context 上下文 * @param context 上下文
*/ */
public static void sendAppConfigStatusUpdateMessage(Context context) { public static void sendAppConfigStatusUpdateMessage(Context context) {
LogUtils.d(TAG, "sendAppConfigStatusUpdateMessage执行 | context=" + context); LogUtils.d(TAG, String.format("sendAppConfigStatusUpdateMessage执行 | context=%s", context));
if (context == null) { if (context == null) {
LogUtils.e(TAG, "sendAppConfigStatusUpdateMessage参数为空更新失败"); LogUtils.e(TAG, "sendAppConfigStatusUpdateMessage参数为空更新失败");
return; return;
} }
Intent intent = new Intent(ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED); Intent intent = new Intent(ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED);
intent.setPackage(context.getPackageName()); intent.setPackage(context.getPackageName());
// 新增:发送广播并记录结果 context.sendBroadcast(intent);
context.sendBroadcast(intent); LogUtils.d(TAG, String.format("sendAppConfigStatusUpdateMessage配置更新广播发送 | action=%s", ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED));
LogUtils.d(TAG, "sendAppConfigStatusUpdateMessage配置更新广播发送 action=" + ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED); }
}
/** /**
* 检查并引导用户开启忽略电池优化API23+ * 检查并引导用户开启忽略电池优化API23+
* @param context 上下文 * @param context 上下文
*/ */
public static void checkIgnoreBatteryOptimization(Context context) { public static void checkIgnoreBatteryOptimization(Context context) {
LogUtils.d(TAG, "checkIgnoreBatteryOptimization执行 | context=" + context); LogUtils.d(TAG, String.format("checkIgnoreBatteryOptimization执行 | context=%s", context));
if (context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (context == null || Build.VERSION.SDK_INT < API_LEVEL_23) {
LogUtils.w(TAG, "checkIgnoreBatteryOptimization无需检查Context为空或API<23"); LogUtils.w(TAG, "checkIgnoreBatteryOptimization无需检查Context为空或API<23");
return; return;
} }
@@ -371,14 +380,14 @@ public class ControlCenterService extends Service {
String packageName = context.getPackageName(); String packageName = context.getPackageName();
boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(packageName); boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(packageName);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization已忽略电池优化=" + isIgnored); LogUtils.d(TAG, String.format("checkIgnoreBatteryOptimization已忽略电池优化=%b", isIgnored));
if (!isIgnored) { if (!isIgnored) {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName)); intent.setData(Uri.parse("package:" + packageName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent); context.startActivity(intent);
LogUtils.d(TAG, "checkIgnoreBatteryOptimization已跳转至系统设置页 | package=" + packageName); LogUtils.d(TAG, String.format("checkIgnoreBatteryOptimization已跳转至系统设置页 | package=%s", packageName));
} }
} }
@@ -389,7 +398,7 @@ public class ControlCenterService extends Service {
* @return true=运行中 false=未运行 * @return true=运行中 false=未运行
*/ */
private static boolean isServiceRunning(Context context, Class<?> serviceClass) { private static boolean isServiceRunning(Context context, Class<?> serviceClass) {
LogUtils.d(TAG, "isServiceRunning执行 | context=" + context + " | service=" + (serviceClass != null ? serviceClass.getName() : "null")); LogUtils.d(TAG, String.format("isServiceRunning执行 | context=%s | service=%s", context, serviceClass != null ? serviceClass.getName() : "null"));
if (context == null || serviceClass == null) { if (context == null || serviceClass == null) {
LogUtils.e(TAG, "isServiceRunning参数为空"); LogUtils.e(TAG, "isServiceRunning参数为空");
return false; return false;
@@ -405,7 +414,7 @@ public class ControlCenterService extends Service {
String packageName = context.getPackageName(); String packageName = context.getPackageName();
String serviceClassName = serviceClass.getName(); String serviceClassName = serviceClass.getName();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= API_LEVEL_30) {
// API30+ 禁止获取其他应用服务,通过进程状态判断 // API30+ 禁止获取其他应用服务,通过进程状态判断
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses(); List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
if (processes != null) { if (processes != null) {
@@ -418,7 +427,7 @@ public class ControlCenterService extends Service {
} }
} }
} }
LogUtils.d(TAG, "isServiceRunningAPI30+ 判断结果=" + isRunning); LogUtils.d(TAG, String.format("isServiceRunningAPI30+ 判断结果=%b", isRunning));
} else { } else {
// API30- 通过服务列表判断 // API30- 通过服务列表判断
List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(RUNNING_SERVICE_LIST_LIMIT); List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(RUNNING_SERVICE_LIST_LIMIT);
@@ -430,13 +439,13 @@ public class ControlCenterService extends Service {
} }
} }
} }
LogUtils.d(TAG, "isServiceRunningAPI30- 判断结果=" + isRunning); LogUtils.d(TAG, String.format("isServiceRunningAPI30- 判断结果=%b", isRunning));
} }
// 兜底判断:配置启用状态 // 兜底判断:配置启用状态
if (!isRunning) { if (!isRunning) {
isRunning = isServiceStarted(context, serviceClass); isRunning = isServiceStarted(context, serviceClass);
LogUtils.d(TAG, "isServiceRunning兜底判断结果=" + isRunning); LogUtils.d(TAG, String.format("isServiceRunning兜底判断结果=%b", isRunning));
} }
return isRunning; return isRunning;
} }
@@ -455,23 +464,25 @@ public class ControlCenterService extends Service {
} }
} }
// ================================== 业务方法(配置更新/电池状态回调)================================= // ====================== 业务方法(配置更新/电池状态回调) ======================
/** /**
* 接收外部配置更新,同步到提醒线程 * 接收外部配置更新,同步到提醒线程
* @param latestConfig 最新配置 * @param latestConfig 最新配置
*/ */
public void notifyAppConfigUpdate(AppConfigBean latestConfig) { public void notifyAppConfigUpdate(AppConfigBean latestConfig) {
LogUtils.d(TAG, "notifyAppConfigUpdate执行 | 充电阈值=" + (latestConfig != null ? latestConfig.getChargeReminderValue() : null) + " | 耗电阈值=" + (latestConfig != null ? latestConfig.getUsageReminderValue() : null)); int chargeThreshold = latestConfig != null ? latestConfig.getChargeReminderValue() : -1;
int usageThreshold = latestConfig != null ? latestConfig.getUsageReminderValue() : -1;
LogUtils.d(TAG, String.format("notifyAppConfigUpdate执行 | 充电阈值=%d | 耗电阈值=%d", chargeThreshold, usageThreshold));
if (latestConfig != null && mServiceHandler != null) { if (latestConfig != null && mServiceHandler != null) {
mCurrentConfigBean = latestConfig; mCurrentConfigBean = latestConfig;
RemindThread.startRemindThreadWithAppConfig(this, mServiceHandler, latestConfig); RemindThread.startRemindThreadWithAppConfig(this, mServiceHandler, latestConfig);
LogUtils.d(TAG, "notifyAppConfigUpdate配置已同步到提醒线程"); LogUtils.d(TAG, "notifyAppConfigUpdate配置已同步到提醒线程");
} else { } else {
LogUtils.e(TAG, "notifyAppConfigUpdate参数为空同步失败 | latestConfig=" + latestConfig + " | mServiceHandler=" + mServiceHandler); LogUtils.e(TAG, String.format("notifyAppConfigUpdate参数为空同步失败 | latestConfig=%s | mServiceHandler=%s", latestConfig, mServiceHandler));
} }
} }
// ================================== Getter 方法按需开放避免冗余Setter================================= // ====================== Getter 方法按需开放避免冗余Setter ======================
public ControlCenterServiceBean getServiceControlBean() { public ControlCenterServiceBean getServiceControlBean() {
return mServiceControlBean; return mServiceControlBean;
} }

View File

@@ -9,14 +9,16 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* 提醒线程(多实例列表管理) * 电量通知提醒线程(多实例列表管理)
* 功能:管理充电/耗电提醒逻辑触发条件时向Handler发送提醒消息 * 功能:管理充电/耗电提醒逻辑触发条件时向Handler发送提醒消息
* 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步 * 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步
* 对外接口:{@link #startRemindThreadWithAppConfig(Context, ControlCenterServiceHandler, AppConfigBean)}、{ * 对外接口:{@link #startRemindThreadWithAppConfig(Context, ControlCenterServiceHandler, AppConfigBean)}、
* @link #startRemindThreadWithBatteryInfo(Context, ControlCenterServiceHandler, boolean, int)}、{@link #stopRemindThread()} * {@link #startRemindThreadWithBatteryInfo(Context, ControlCenterServiceHandler, boolean, int)}、{@link #stopRemindThread()}
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Describe 电量通知提醒线程
*/ */
public class RemindThread extends Thread { public class RemindThread extends Thread {
// ================================== 静态常量区(置顶归类,消除魔法值)================================= // ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "RemindThread"; public static final String TAG = "RemindThread";
// 时间常量 (ms) // 时间常量 (ms)
@@ -32,10 +34,10 @@ public class RemindThread extends Thread {
private static final String REMIND_TYPE_CHARGE = "+"; private static final String REMIND_TYPE_CHARGE = "+";
private static final String REMIND_TYPE_USAGE = "-"; private static final String REMIND_TYPE_USAGE = "-";
// ================================== 静态成员(多实例列表管理)================================= // ====================== 静态成员(多实例列表管理) ======================
private static volatile ArrayList<RemindThread> sRemindThreadList; private static volatile ArrayList<RemindThread> sRemindThreadList;
// ================================== 成员变量区按功能分层volatile保证多线程可见性================================= // ====================== 成员变量区按功能分层volatile保证多线程可见性 ======================
// 并发安全锁(保护线程状态变更) // 并发安全锁(保护线程状态变更)
private final Object mRemindLock = new Object(); private final Object mRemindLock = new Object();
@@ -56,16 +58,16 @@ public class RemindThread extends Thread {
private volatile int quantityOfElectricity; private volatile int quantityOfElectricity;
private volatile boolean isCharging; private volatile boolean isCharging;
// ================================== 私有构造器(禁止外部实例化)================================= // ====================== 私有构造器(禁止外部实例化) ======================
private RemindThread(Context context, ControlCenterServiceHandler handler) { private RemindThread(Context context, ControlCenterServiceHandler handler) {
LogUtils.d(TAG, "构造器调用 | context=" + context + " | handler=" + handler); LogUtils.d(TAG, String.format("构造器调用 | context=%s | handler=%s", context, handler));
this.mContext = context.getApplicationContext(); this.mContext = context.getApplicationContext();
this.mwrControlCenterServiceHandler = new WeakReference<>(handler); this.mwrControlCenterServiceHandler = new WeakReference<>(handler);
resetThreadStateInternal(); resetThreadStateInternal();
LogUtils.d(TAG, "构造完成 | threadId=" + getId() + " | 初始状态重置成功"); LogUtils.d(TAG, String.format("构造完成 | threadId=%d | 初始状态重置成功", getId()));
} }
// ================================== 对外公开静态接口(多实例列表管理)================================= // ====================== 对外公开静态接口(多实例列表管理) ======================
/** /**
* 启动提醒线程,同步最新配置 * 启动提醒线程,同步最新配置
* 逻辑:停止所有旧线程 → 创建新线程 → 加入列表管理 * 逻辑:停止所有旧线程 → 创建新线程 → 加入列表管理
@@ -75,15 +77,15 @@ public class RemindThread extends Thread {
* @return true: 启动成功false: 入参非法 * @return true: 启动成功false: 入参非法
*/ */
public static boolean startRemindThreadWithAppConfig(Context context, ControlCenterServiceHandler handler, AppConfigBean config) { public static boolean startRemindThreadWithAppConfig(Context context, ControlCenterServiceHandler handler, AppConfigBean config) {
LogUtils.d(TAG, "startRemindThreadWithAppConfig调用 | context=" + context + " | handler=" + handler + " | config=" + config); LogUtils.d(TAG, String.format("startRemindThreadWithAppConfig调用 | context=%s | handler=%s | config=%s", context, handler, config));
// 入参严格校验 // 入参严格校验
if (context == null || handler == null || config == null) { if (context == null || handler == null || config == null) {
LogUtils.e(TAG, "启动失败:入参为空 | context=" + context + " | handler=" + handler + " | config=" + config); LogUtils.e(TAG, String.format("启动失败:入参为空 | context=%s | handler=%s | config=%s", context, handler, config));
return false; return false;
} }
// 初始化线程列表 // 初始化线程列表(双重校验锁)
if (sRemindThreadList == null) { if (sRemindThreadList == null) {
synchronized (RemindThread.class) { synchronized (RemindThread.class) {
if (sRemindThreadList == null) { if (sRemindThreadList == null) {
@@ -102,7 +104,7 @@ public class RemindThread extends Thread {
newRemindThread.isExist = false; newRemindThread.isExist = false;
newRemindThread.start(); newRemindThread.start();
sRemindThreadList.add(newRemindThread); sRemindThreadList.add(newRemindThread);
LogUtils.d(TAG, "新线程启动成功 | threadId=" + newRemindThread.getId() + " | 列表大小=" + sRemindThreadList.size()); LogUtils.d(TAG, String.format("新线程启动成功 | threadId=%d | 列表大小=%d", newRemindThread.getId(), sRemindThreadList.size()));
return true; return true;
} }
@@ -116,15 +118,15 @@ public class RemindThread extends Thread {
* @return true: 启动成功false: 入参非法 * @return true: 启动成功false: 入参非法
*/ */
public static boolean startRemindThreadWithBatteryInfo(Context context, ControlCenterServiceHandler handler, boolean isCharging, int batteryLevel) { public static boolean startRemindThreadWithBatteryInfo(Context context, ControlCenterServiceHandler handler, boolean isCharging, int batteryLevel) {
LogUtils.d(TAG, "startRemindThreadWithBatteryInfo调用 | context=" + context + " | handler=" + handler + " | isCharging=" + isCharging + " | batteryLevel=" + batteryLevel); LogUtils.d(TAG, String.format("startRemindThreadWithBatteryInfo调用 | context=%s | handler=%s | isCharging=%b | batteryLevel=%d", context, handler, isCharging, batteryLevel));
// 入参严格校验 // 入参严格校验
if (context == null || handler == null) { if (context == null || handler == null) {
LogUtils.e(TAG, "启动失败:入参为空 | context=" + context + " | handler=" + handler); LogUtils.e(TAG, String.format("启动失败:入参为空 | context=%s | handler=%s", context, handler));
return false; return false;
} }
// 初始化线程列表 // 初始化线程列表(双重校验锁)
if (sRemindThreadList == null) { if (sRemindThreadList == null) {
synchronized (RemindThread.class) { synchronized (RemindThread.class) {
if (sRemindThreadList == null) { if (sRemindThreadList == null) {
@@ -139,13 +141,13 @@ public class RemindThread extends Thread {
// 创建并启动新线程 // 创建并启动新线程
RemindThread newRemindThread = new RemindThread(context, handler); RemindThread newRemindThread = new RemindThread(context, handler);
// 同步电池状态 // 同步电池状态(范围校验)
newRemindThread.isCharging = isCharging; newRemindThread.isCharging = isCharging;
newRemindThread.quantityOfElectricity = Math.min(Math.max(batteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX); newRemindThread.quantityOfElectricity = Math.min(Math.max(batteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX);
newRemindThread.isExist = false; newRemindThread.isExist = false;
newRemindThread.start(); newRemindThread.start();
sRemindThreadList.add(newRemindThread); sRemindThreadList.add(newRemindThread);
LogUtils.d(TAG, "新线程启动成功 | threadId=" + newRemindThread.getId() + " | 电池状态同步完成"); LogUtils.d(TAG, String.format("新线程启动成功 | threadId=%d | 电池状态同步完成(电量=%d充电=%b", newRemindThread.getId(), newRemindThread.quantityOfElectricity, newRemindThread.isCharging));
return true; return true;
} }
@@ -153,7 +155,8 @@ public class RemindThread extends Thread {
* 安全停止所有线程,清空列表 * 安全停止所有线程,清空列表
*/ */
public static void stopRemindThread() { public static void stopRemindThread() {
LogUtils.d(TAG, "stopRemindThread调用 | 列表存在=" + (sRemindThreadList != null) + " | 列表大小=" + (sRemindThreadList != null ? sRemindThreadList.size() : 0)); int listSize = sRemindThreadList != null ? sRemindThreadList.size() : 0;
LogUtils.d(TAG, String.format("stopRemindThread调用 | 列表存在=%b | 列表大小=%d", sRemindThreadList != null, listSize));
if (sRemindThreadList == null || sRemindThreadList.isEmpty()) { if (sRemindThreadList == null || sRemindThreadList.isEmpty()) {
LogUtils.w(TAG, "停止失败:线程列表为空"); LogUtils.w(TAG, "停止失败:线程列表为空");
return; return;
@@ -162,14 +165,14 @@ public class RemindThread extends Thread {
// 标记所有线程退出 // 标记所有线程退出
for (RemindThread remindThread : sRemindThreadList) { for (RemindThread remindThread : sRemindThreadList) {
remindThread.isExist = true; remindThread.isExist = true;
LogUtils.d(TAG, "标记线程退出 | threadId=" + remindThread.getId()); LogUtils.d(TAG, String.format("标记线程退出 | threadId=%d", remindThread.getId()));
} }
// 清空列表 // 清空列表
sRemindThreadList.clear(); sRemindThreadList.clear();
LogUtils.d(TAG, "所有线程已标记退出,列表已清空"); LogUtils.d(TAG, "所有线程已标记退出,列表已清空");
} }
// ================================== 私有静态辅助方法(多实例管理)================================= // ====================== 私有静态辅助方法(多实例管理) ======================
/** /**
* 停止所有旧线程并清空列表 * 停止所有旧线程并清空列表
*/ */
@@ -181,29 +184,29 @@ public class RemindThread extends Thread {
// 标记所有旧线程退出 // 标记所有旧线程退出
for (RemindThread remindThread : sRemindThreadList) { for (RemindThread remindThread : sRemindThreadList) {
remindThread.isExist = true; remindThread.isExist = true;
LogUtils.d(TAG, "标记旧线程退出 | threadId=" + remindThread.getId()); LogUtils.d(TAG, String.format("标记旧线程退出 | threadId=%d", remindThread.getId()));
} }
// 清空旧线程列表 // 清空旧线程列表
sRemindThreadList.clear(); sRemindThreadList.clear();
LogUtils.d(TAG, "旧线程已全部标记退出,列表已清空"); LogUtils.d(TAG, "旧线程已全部标记退出,列表已清空");
} }
// ================================== 线程核心运行逻辑================================= // ====================== 线程核心运行逻辑 ======================
@Override @Override
public void run() { public void run() {
LogUtils.d(TAG, "run执行 | threadId=" + getId() + " | 状态=" + getState()); LogUtils.d(TAG, String.format("run执行 | threadId=%d | 状态=%s", getId(), getState()));
// 初始化提醒状态(加锁保护,避免多线程竞争) // 初始化提醒状态(加锁保护,避免多线程竞争)
synchronized (mRemindLock) { synchronized (mRemindLock) {
if (isReminding) { if (isReminding) {
LogUtils.w(TAG, "线程已在提醒状态,退出运行 | threadId=" + getId()); LogUtils.w(TAG, String.format("线程已在提醒状态,退出运行 | threadId=%d", getId()));
return; return;
} }
isReminding = true; isReminding = true;
} }
// 核心电量检测循环 // 核心电量检测循环
LogUtils.d(TAG, "进入电量检测循环 | 休眠时间=" + sleepTime + "ms | threadId=" + getId()); LogUtils.d(TAG, String.format("进入电量检测循环 | 休眠时间=%dms | threadId=%d", sleepTime, getId()));
while (!isExist) { while (!isExist) {
try { try {
// 快速退出判断 // 快速退出判断
@@ -211,37 +214,40 @@ public class RemindThread extends Thread {
// 电量有效性校验非0-100视为无效退出电量提醒线程 // 电量有效性校验非0-100视为无效退出电量提醒线程
if (quantityOfElectricity < BATTERY_LEVEL_MIN || quantityOfElectricity > BATTERY_LEVEL_MAX) { if (quantityOfElectricity < BATTERY_LEVEL_MIN || quantityOfElectricity > BATTERY_LEVEL_MAX) {
LogUtils.w(TAG, "电量无效,退出电量提醒线程 | 当前电量=" + quantityOfElectricity + " | threadId=" + getId()); LogUtils.w(TAG, String.format("电量无效,退出电量提醒线程 | 当前电量=%d | threadId=%d", quantityOfElectricity, getId()));
break; break;
} }
// 充电/耗电提醒触发逻辑 // 充电/耗电提醒触发逻辑
if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) { boolean chargeRemindTrigger = isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue;
LogUtils.d(TAG, "触发充电提醒 | 当前电量=" + quantityOfElectricity + " ≥ 阈值=" + chargeReminderValue + " | threadId=" + getId()); boolean usageRemindTrigger = !isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue;
if (chargeRemindTrigger) {
LogUtils.d(TAG, String.format("触发充电提醒 | 当前电量=%d ≥ 阈值=%d | threadId=%d", quantityOfElectricity, chargeReminderValue, getId()));
sendNotificationMessageInternal(REMIND_TYPE_CHARGE, quantityOfElectricity, isCharging); sendNotificationMessageInternal(REMIND_TYPE_CHARGE, quantityOfElectricity, isCharging);
} else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { } else if (usageRemindTrigger) {
LogUtils.d(TAG, "触发耗电提醒 | 当前电量=" + quantityOfElectricity + " ≤ 阈值=" + usageReminderValue + " | threadId=" + getId()); LogUtils.d(TAG, String.format("触发耗电提醒 | 当前电量=%d ≤ 阈值=%d | threadId=%d", quantityOfElectricity, usageReminderValue, getId()));
sendNotificationMessageInternal(REMIND_TYPE_USAGE, quantityOfElectricity, isCharging); sendNotificationMessageInternal(REMIND_TYPE_USAGE, quantityOfElectricity, isCharging);
} else { } else {
// 未有合适类型提醒,退出提醒线程 LogUtils.d(TAG, String.format("未有合适类型提醒,退出提醒线程 | threadId=%d", getId()));
LogUtils.d(TAG, "未有合适类型提醒,退出提醒线程"); break;
break; }
}
// 安全休眠,保留中断标记 // 安全休眠,保留中断标记
safeSleepInternal(sleepTime); safeSleepInternal(sleepTime);
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "循环运行异常,退出电量提醒线程 | 当前电量=" + quantityOfElectricity + " | threadId=" + getId(), e); LogUtils.e(TAG, String.format("循环运行异常,退出电量提醒线程 | 当前电量=%d | threadId=%d", quantityOfElectricity, getId()), e);
break; break;
} }
} }
// 循环退出,清理状态 // 循环退出,清理状态
cleanThreadStateInternal(); cleanThreadStateInternal();
LogUtils.d(TAG, "run结束 | threadId=" + getId()); LogUtils.d(TAG, String.format("run结束 | threadId=%d", getId()));
} }
// ================================== 内部业务辅助方法================================= // ====================== 内部业务辅助方法 ======================
/** /**
* 发送提醒消息到Handler弱引用避免内存泄漏 * 发送提醒消息到Handler弱引用避免内存泄漏
* @param type 提醒类型:+充电/-耗电 * @param type 提醒类型:+充电/-耗电
@@ -249,20 +255,21 @@ public class RemindThread extends Thread {
* @param isCharging 充电状态 * @param isCharging 充电状态
*/ */
private void sendNotificationMessageInternal(String type, int battery, boolean isCharging) { private void sendNotificationMessageInternal(String type, int battery, boolean isCharging) {
LogUtils.d(TAG, "sendNotificationMessageInternal调用 | 类型=" + type + " | 电量=" + battery + " | isCharging=" + isCharging + " | threadId=" + getId()); LogUtils.d(TAG, String.format("sendNotificationMessageInternal调用 | 类型=%s | 电量=%d | isCharging=%b | threadId=%d", type, battery, isCharging, getId()));
// 前置状态校验 // 前置状态校验
if (isExist || !isReminding) { if (isExist || !isReminding) {
LogUtils.d(TAG, "消息发送跳过:线程已退出或提醒关闭 | threadId=" + getId()); LogUtils.d(TAG, String.format("消息发送跳过:线程已退出或提醒关闭 | threadId=%d", getId()));
return; return;
} }
// 获取弱引用的Handler // 获取弱引用的Handler(校验有效性)
ControlCenterServiceHandler handler = mwrControlCenterServiceHandler.get(); ControlCenterServiceHandler handler = mwrControlCenterServiceHandler.get();
if (handler == null) { if (handler == null) {
LogUtils.w(TAG, "消息发送失败Handler已被回收 | threadId=" + getId()); LogUtils.w(TAG, String.format("消息发送失败Handler已被回收 | threadId=%d", getId()));
return; return;
} }
// 构建并发送消息
Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT); Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT);
message.obj = type; message.obj = type;
message.arg1 = battery; message.arg1 = battery;
@@ -270,9 +277,9 @@ public class RemindThread extends Thread {
try { try {
handler.sendMessage(message); handler.sendMessage(message);
LogUtils.d(TAG, "提醒消息发送成功 | 类型=" + type + " | 电量=" + battery + " | threadId=" + getId()); LogUtils.d(TAG, String.format("提醒消息发送成功 | 类型=%s | 电量=%d | threadId=%d", type, battery, getId()));
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "消息发送异常 | threadId=" + getId(), e); LogUtils.e(TAG, String.format("消息发送异常 | threadId=%d", getId()), e);
// 异常时回收Message避免内存泄漏 // 异常时回收Message避免内存泄漏
if (message != null) { if (message != null) {
message.recycle(); message.recycle();
@@ -285,12 +292,12 @@ public class RemindThread extends Thread {
* @param millis 休眠时长(ms) * @param millis 休眠时长(ms)
*/ */
private void safeSleepInternal(long millis) { private void safeSleepInternal(long millis) {
LogUtils.d(TAG, "safeSleepInternal调用 | 休眠时长=" + millis + "ms | threadId=" + getId()); LogUtils.d(TAG, String.format("safeSleepInternal调用 | 休眠时长=%dms | threadId=%d", millis, getId()));
try { try {
Thread.sleep(millis); Thread.sleep(millis);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
LogUtils.w(TAG, "休眠被中断,线程准备退出 | threadId=" + getId()); LogUtils.w(TAG, String.format("休眠被中断,线程准备退出 | threadId=%d", getId()));
} }
} }
@@ -298,7 +305,7 @@ public class RemindThread extends Thread {
* 重置线程初始状态(构造器专用) * 重置线程初始状态(构造器专用)
*/ */
private void resetThreadStateInternal() { private void resetThreadStateInternal() {
LogUtils.d(TAG, "resetThreadStateInternal调用 | threadId=" + getId()); LogUtils.d(TAG, String.format("resetThreadStateInternal调用 | threadId=%d", getId()));
// 状态标记初始化 // 状态标记初始化
isExist = false; isExist = false;
isReminding = false; isReminding = false;
@@ -310,22 +317,23 @@ public class RemindThread extends Thread {
usageReminderValue = -1; usageReminderValue = -1;
quantityOfElectricity = INVALID_BATTERY_VALUE; quantityOfElectricity = INVALID_BATTERY_VALUE;
isCharging = false; isCharging = false;
LogUtils.d(TAG, "线程初始状态重置完成 | threadId=" + getId()); LogUtils.d(TAG, String.format("线程初始状态重置完成 | threadId=%d", getId()));
} }
/** /**
* 清理线程运行状态(循环退出时调用) * 清理线程运行状态(循环退出时调用)
*/ */
private void cleanThreadStateInternal() { private void cleanThreadStateInternal() {
LogUtils.d(TAG, "cleanThreadStateInternal调用 | threadId=" + getId()); LogUtils.d(TAG, String.format("cleanThreadStateInternal调用 | threadId=%d", getId()));
isReminding = false; isReminding = false;
isExist = true; isExist = true;
quantityOfElectricity = INVALID_BATTERY_VALUE; quantityOfElectricity = INVALID_BATTERY_VALUE;
// 中断当前线程(如果存活) // 中断当前线程(如果存活)
if (isAlive()) { if (isAlive()) {
interrupt(); interrupt();
LogUtils.d(TAG, String.format("线程已中断 | threadId=%d", getId()));
} }
LogUtils.d(TAG, "线程运行状态清理完成 | threadId=" + getId()); LogUtils.d(TAG, String.format("线程运行状态清理完成 | threadId=%d", getId()));
} }
/** /**
@@ -333,9 +341,9 @@ public class RemindThread extends Thread {
* @param config 应用配置Bean * @param config 应用配置Bean
*/ */
public void setAppConfigBean(AppConfigBean config) { public void setAppConfigBean(AppConfigBean config) {
LogUtils.d(TAG, "setAppConfigBean调用 | config=" + config + " | threadId=" + getId()); LogUtils.d(TAG, String.format("setAppConfigBean调用 | config=%s | threadId=%d", config, getId()));
if (config == null) { if (config == null) {
LogUtils.e(TAG, "配置同步失败配置Bean为空 | threadId=" + getId()); LogUtils.e(TAG, String.format("配置同步失败配置Bean为空 | threadId=%d", getId()));
quantityOfElectricity = INVALID_BATTERY_VALUE; quantityOfElectricity = INVALID_BATTERY_VALUE;
return; return;
} }
@@ -350,7 +358,8 @@ public class RemindThread extends Thread {
? config.getCurrentBatteryValue() : INVALID_BATTERY_VALUE; ? config.getCurrentBatteryValue() : INVALID_BATTERY_VALUE;
isCharging = config.isCharging(); isCharging = config.isCharging();
LogUtils.d(TAG, "配置同步完成 | 休眠时间=" + sleepTime + "ms | 提醒开启=" + isReminding + " | 当前电量=" + quantityOfElectricity + " | 充电阈值=" + chargeReminderValue + " | 耗电阈值=" + usageReminderValue + " | threadId=" + getId()); LogUtils.d(TAG, String.format("配置同步完成 | 休眠时间=%dms | 充电提醒=%b | 耗电提醒=%b | 当前电量=%d | 充电阈值=%d | 耗电阈值=%d | threadId=%d",
sleepTime, isEnableChargeReminder, isEnableUsageReminder, quantityOfElectricity, chargeReminderValue, usageReminderValue, getId()));
} }
/** /**
@@ -359,13 +368,13 @@ public class RemindThread extends Thread {
*/ */
private boolean isRunning() { private boolean isRunning() {
boolean running = !isExist && isAlive(); boolean running = !isExist && isAlive();
LogUtils.d(TAG, "isRunning调用 | 运行中=" + running + " | 退出标记=" + isExist + " | 存活=" + isAlive() + " | threadId=" + getId()); LogUtils.d(TAG, String.format("isRunning调用 | 运行中=%b | 退出标记=%b | 存活=%b | threadId=%d", running, isExist, isAlive(), getId()));
return running; return running;
} }
// ================================== Getter/Setter按需开放================================= // ====================== Getter/Setter按需开放 ======================
public void setIsExist(boolean isExist) { public void setIsExist(boolean isExist) {
LogUtils.d(TAG, "setIsExist调用 | isExist=" + isExist + " | threadId=" + getId()); LogUtils.d(TAG, String.format("setIsExist调用 | isExist=%b | threadId=%d", isExist, getId()));
this.isExist = isExist; this.isExist = isExist;
} }
@@ -373,7 +382,7 @@ public class RemindThread extends Thread {
return isExist; return isExist;
} }
// ================================== 调试辅助方法================================= // ====================== 调试辅助方法 ======================
@Override @Override
public String toString() { public String toString() {
return "RemindThread{" + return "RemindThread{" +

View File

@@ -16,32 +16,35 @@ import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.models.BackgroundBean; import cc.winboll.studio.powerbell.models.BackgroundBean;
import cc.winboll.studio.powerbell.utils.FileUtils; import cc.winboll.studio.powerbell.utils.FileUtils;
import cc.winboll.studio.powerbell.utils.ImageCropUtils; import cc.winboll.studio.powerbell.utils.ImageCropUtils;
import cc.winboll.studio.powerbell.views.BackgroundView;
import cc.winboll.studio.powerbell.views.MemoryCachedBackgroundView; import cc.winboll.studio.powerbell.views.MemoryCachedBackgroundView;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * 单元测试页面2内存缓存背景视图专用
* @Date 2025/12/22 08:31 * 功能测试MemoryCachedBackgroundView加载、图片裁剪、双重刷新预览等功能
* @Describe MainUnitTest2Activity * 适配Java7 | API30 | 私有目录文件操作 | 无Uri冲突 | 内存缓存视图
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Describe 单元测试页2验证带内存缓存的背景视图相关逻辑
*/ */
public class MainUnitTest2Activity extends AppCompatActivity { public class MainUnitTest2Activity extends AppCompatActivity {
// ====================== 常量定义 ====================== // ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "MainUnitTest2Activity"; public static final String TAG = "MainUnitTest2Activity";
public static final int REQUEST_CROP_IMAGE = 0; public static final int REQUEST_CROP_IMAGE = 0;
private static final String ASSETS_TEST_IMAGE_PATH = "unittest/unittest-miku.png"; private static final String ASSETS_TEST_IMAGE_PATH = "unittest/unittest-miku.png";
private static final long FILE_MIN_SIZE = 100L;
private static final long DOUBLE_REFRESH_DELAY = 200L;
// ====================== 成员变量移除所有Uri相关 ====================== // ====================== 成员变量区(按功能分层,移除所有Uri相关 ======================
private MemoryCachedBackgroundView mMemoryCachedBackgroundView; private MemoryCachedBackgroundView mMemoryCachedBackgroundView;
private LinearLayout mllBackgroundView;
private String mAppPrivateDirPath; private String mAppPrivateDirPath;
private File mPrivateTestImageFile; // 仅用File不用Uri private File mPrivateTestImageFile;
private File mPrivateCropImageFile; private File mPrivateCropImageFile;
BackgroundBean mPreviewBackgroundBean; private BackgroundBean mPreviewBackgroundBean;
LinearLayout mllBackgroundView;
// ====================== 生命周期方法 ====================== // ====================== 生命周期方法按执行顺序onCreate→onActivityResult ======================
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -50,65 +53,66 @@ public class MainUnitTest2Activity extends AppCompatActivity {
initBaseParams(); initBaseParams();
initViewAndEvent(); initViewAndEvent();
copyAssetsTestImageToPrivateDir(); copyAssetsTestImageToPrivateDir();
//loadBackgroundByFile(); // 直接用File加载 initBackgroundBean();
mPreviewBackgroundBean = new BackgroundBean(); doubleRefreshPreview();
mPreviewBackgroundBean.setBackgroundFileName(mPrivateTestImageFile.getName());
mPreviewBackgroundBean.setBackgroundFilePath(mPrivateTestImageFile.getAbsolutePath());
mPreviewBackgroundBean.setBackgroundScaledCompressFileName(mPrivateCropImageFile.getName());
mPreviewBackgroundBean.setBackgroundScaledCompressFilePath(mPrivateCropImageFile.getAbsolutePath());
mPreviewBackgroundBean.setIsUseBackgroundFile(true);
doubleRefreshPreview();
ToastUtils.show("单元测试页面启动完成"); ToastUtils.show("单元测试页面2启动完成");
LogUtils.d(TAG, "=== 页面 onCreate 初始化结束 ==="); LogUtils.d(TAG, "=== 页面 onCreate 初始化结束 ===");
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
LogUtils.d(TAG, "=== onActivityResult 回调 ==="); LogUtils.d(TAG, String.format("=== onActivityResult 回调 | requestCode=%d | resultCode=%d ===", requestCode, resultCode));
if (requestCode == REQUEST_CROP_IMAGE) { if (requestCode == REQUEST_CROP_IMAGE) {
handleCropResult(resultCode); handleCropResult(resultCode);
} }
} }
// ====================== 初始化相关方法 ====================== // ====================== 初始化相关方法基础参数→视图→背景Bean ======================
/**
* 初始化基础参数:私有目录、测试文件
*/
private void initBaseParams() { private void initBaseParams() {
LogUtils.d(TAG, "初始化基础参数:工具类+私有目录+File"); LogUtils.d(TAG, "initBaseParams初始化基础参数");
// 初始化私有目录无需权限无UID冲突
// 私有目录无需权限无UID冲突
mAppPrivateDirPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBellTest/"; mAppPrivateDirPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBellTest/";
File privateDir = new File(mAppPrivateDirPath); File privateDir = new File(mAppPrivateDirPath);
if (!privateDir.exists()) { if (!privateDir.exists()) {
privateDir.mkdirs(); boolean isDirCreated = privateDir.mkdirs();
LogUtils.d(TAG, "创建私有目录:" + mAppPrivateDirPath); LogUtils.d(TAG, String.format("initBaseParams创建私有目录 | 路径=%s | 结果=%b", mAppPrivateDirPath, isDirCreated));
} }
// 初始化File无Uri // 初始化测试文件与裁剪文件无Uri
File refFile = new File(ASSETS_TEST_IMAGE_PATH); File refFile = new File(ASSETS_TEST_IMAGE_PATH);
String uniqueTestName = FileUtils.createUniqueFileName(refFile) + ".png"; String uniqueTestName = FileUtils.createUniqueFileName(refFile) + ".png";
String uniqueCropName = uniqueTestName.replace(".png", "_crop.png"); String uniqueCropName = uniqueTestName.replace(".png", "_crop.png");
mPrivateTestImageFile = new File(mAppPrivateDirPath, uniqueTestName); mPrivateTestImageFile = new File(mAppPrivateDirPath, uniqueTestName);
mPrivateCropImageFile = new File(mAppPrivateDirPath, uniqueCropName); mPrivateCropImageFile = new File(mAppPrivateDirPath, uniqueCropName);
LogUtils.d(TAG, "测试图File路径" + mPrivateTestImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("initBaseParams测试图路径=%s", mPrivateTestImageFile.getAbsolutePath()));
LogUtils.d(TAG, String.format("initBaseParams裁剪图路径=%s", mPrivateCropImageFile.getAbsolutePath()));
} }
/**
* 初始化布局与控件事件(含单例视图创建)
*/
private void initViewAndEvent() { private void initViewAndEvent() {
LogUtils.d(TAG, "初始化布局与控件事件"); LogUtils.d(TAG, "initViewAndEvent初始化布局与控件事件");
setContentView(R.layout.activity_mainunittest2); setContentView(R.layout.activity_mainunittest2);
mllBackgroundView = (LinearLayout) findViewById(R.id.ll_backgroundview); mllBackgroundView = (LinearLayout) findViewById(R.id.ll_backgroundview);
mMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, "", false);
mllBackgroundView.addView(mMemoryCachedBackgroundView); // 创建MemoryCachedBackgroundView单例并添加到布局
mMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, "", false);
//mMemoryCachedBackgroundView = (BackgroundView) findViewById(R.id.backgroundview); mllBackgroundView.addView(mMemoryCachedBackgroundView);
LogUtils.d(TAG, "initViewAndEvent内存缓存背景视图实例创建并添加完成");
// 跳转主页面按钮 // 跳转主页面按钮
Button btnMain = (Button) findViewById(R.id.btn_main_activity); Button btnMain = (Button) findViewById(R.id.btn_main_activity);
btnMain.setOnClickListener(new View.OnClickListener() { btnMain.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.d(TAG, "点击按钮跳转主页面"); LogUtils.d(TAG, "initViewAndEvent点击按钮跳转主页面");
startActivity(new Intent(MainUnitTest2Activity.this, MainActivity.class)); startActivity(new Intent(MainUnitTest2Activity.this, MainActivity.class));
} }
}); });
@@ -118,11 +122,11 @@ public class MainUnitTest2Activity extends AppCompatActivity {
btnCrop.setOnClickListener(new View.OnClickListener() { btnCrop.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.d(TAG, "点击按钮启动裁剪File路径版"); LogUtils.d(TAG, "initViewAndEvent点击按钮启动裁剪File路径版");
ToastUtils.show("准备启动图片裁剪"); ToastUtils.show("准备启动图片裁剪");
if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { if (isFileValid(mPrivateTestImageFile)) {
startCropTestByFile(); // 直接传File startCropTestByFile();
} else { } else {
ToastUtils.show("测试图片未准备好,重新拷贝"); ToastUtils.show("测试图片未准备好,重新拷贝");
copyAssetsTestImageToPrivateDir(); copyAssetsTestImageToPrivateDir();
@@ -131,11 +135,28 @@ public class MainUnitTest2Activity extends AppCompatActivity {
}); });
} }
// 从assets拷贝图片不变确保File存在 /**
* 初始化背景Bean
*/
private void initBackgroundBean() {
LogUtils.d(TAG, "initBackgroundBean初始化背景Bean");
mPreviewBackgroundBean = new BackgroundBean();
mPreviewBackgroundBean.setBackgroundFileName(mPrivateTestImageFile.getName());
mPreviewBackgroundBean.setBackgroundFilePath(mPrivateTestImageFile.getAbsolutePath());
mPreviewBackgroundBean.setBackgroundScaledCompressFileName(mPrivateCropImageFile.getName());
mPreviewBackgroundBean.setBackgroundScaledCompressFilePath(mPrivateCropImageFile.getAbsolutePath());
mPreviewBackgroundBean.setIsUseBackgroundFile(true);
LogUtils.d(TAG, "initBackgroundBean背景Bean初始化完成");
}
// ====================== 核心业务方法(文件拷贝→裁剪→结果处理→预览刷新) ======================
/**
* 从assets拷贝图片到私有目录
*/
private void copyAssetsTestImageToPrivateDir() { private void copyAssetsTestImageToPrivateDir() {
LogUtils.d(TAG, "开始拷贝assets图片到私有目录"); LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir开始拷贝assets图片到私有目录");
if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { if (isFileValid(mPrivateTestImageFile)) {
LogUtils.d(TAG, "图片已存在,无需拷贝"); LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir图片已存在,无需拷贝");
return; return;
} }
@@ -143,97 +164,87 @@ public class MainUnitTest2Activity extends AppCompatActivity {
try { try {
inputStream = getAssets().open(ASSETS_TEST_IMAGE_PATH); inputStream = getAssets().open(ASSETS_TEST_IMAGE_PATH);
FileUtils.copyStreamToFile(inputStream, mPrivateTestImageFile); FileUtils.copyStreamToFile(inputStream, mPrivateTestImageFile);
LogUtils.d(TAG, "图片拷贝成功,大小:" + mPrivateTestImageFile.length() + "字节"); LogUtils.d(TAG, String.format("copyAssetsTestImageToPrivateDir图片拷贝成功 | 大小=%d字节", mPrivateTestImageFile.length()));
} catch (IOException e) { } catch (IOException e) {
LogUtils.e(TAG, "图片拷贝失败:" + e.getMessage(), e); LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir图片拷贝失败 | %s", e.getMessage()), e);
ToastUtils.show("图片准备失败"); ToastUtils.show("图片准备失败");
} finally { } finally {
if (inputStream != null) { if (inputStream != null) {
try { try {
inputStream.close(); inputStream.close();
} catch (IOException e) { } catch (IOException e) {
LogUtils.e(TAG, "关闭流失败:" + e.getMessage()); LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir关闭流失败 | %s", e.getMessage()));
} }
} }
} }
} }
// ====================== 核心业务方法全改为File路径 ====================== /**
/** 直接用File路径加载背景图无Uri无冲突 */ * 直接用File启动裁剪关键调用ImageCropUtils的File重载方法
// private void loadBackgroundByFile() { */
// LogUtils.d(TAG, "开始加载背景图File路径版");
// if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) {
// mBackgroundView.loadImage(mPrivateTestImageFile.getAbsolutePath()); // 直接传路径
// LogUtils.d(TAG, "背景图加载成功:" + mPrivateTestImageFile.getAbsolutePath());
// ToastUtils.show("背景图加载成功");
// } else {
// LogUtils.e(TAG, "背景图加载失败:文件无效");
// ToastUtils.show("背景图加载失败");
// }
// }
/** 直接用File启动裁剪关键调用ImageCropUtils的File重载方法 */
private void startCropTestByFile() { private void startCropTestByFile() {
LogUtils.d(TAG, "启动裁剪File路径版原图" + mPrivateTestImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("startCropTestByFile启动裁剪 | 原图=%s", mPrivateTestImageFile.getAbsolutePath()));
// 确保输出目录存在 // 确保输出目录存在
File cropParent = mPrivateCropImageFile.getParentFile(); File cropParent = mPrivateCropImageFile.getParentFile();
if (!cropParent.exists()) { if (!cropParent.exists()) {
cropParent.mkdirs(); boolean isDirCreated = cropParent.mkdirs();
LogUtils.d(TAG, String.format("startCropTestByFile创建裁剪目录 | 路径=%s | 结果=%b", cropParent.getAbsolutePath(), isDirCreated));
} }
// 调用ImageCropUtils的File参数方法核心绕开Uri // 调用ImageCropUtils的File参数方法核心绕开Uri
ImageCropUtils.startImageCrop( ImageCropUtils.startImageCrop(
this, this,
mPrivateTestImageFile, // 原图File mPrivateTestImageFile,
mPrivateCropImageFile, // 输出File mPrivateCropImageFile,
0, 0,
0, 0,
true, true,
REQUEST_CROP_IMAGE REQUEST_CROP_IMAGE
); );
LogUtils.d(TAG, "裁剪请求已发送输出路径" + mPrivateCropImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("startCropTestByFile裁剪请求已发送 | 输出路径=%s", mPrivateCropImageFile.getAbsolutePath()));
ToastUtils.show("已启动图片裁剪"); ToastUtils.show("已启动图片裁剪");
} }
/** 处理裁剪结果直接校验输出File */ /**
* 处理裁剪结果直接校验输出File
* @param resultCode 裁剪结果码
*/
private void handleCropResult(int resultCode) { private void handleCropResult(int resultCode) {
LogUtils.d(TAG, "裁剪回调处理resultCode=" + resultCode); LogUtils.d(TAG, String.format("handleCropResult裁剪回调处理 | resultCode=%d", resultCode));
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
if (mPrivateCropImageFile.exists() && mPrivateCropImageFile.length() > 100) { if (isFileValid(mPrivateCropImageFile)) {
mMemoryCachedBackgroundView.loadImage(mPrivateCropImageFile.getAbsolutePath()); mMemoryCachedBackgroundView.loadImage(mPrivateCropImageFile.getAbsolutePath());
LogUtils.d(TAG, "裁剪成功加载裁剪图" + mPrivateCropImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("handleCropResult裁剪成功 | 加载裁剪图=%s", mPrivateCropImageFile.getAbsolutePath()));
ToastUtils.show("裁剪成功"); ToastUtils.show("裁剪成功");
mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true); mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true);
doubleRefreshPreview(); doubleRefreshPreview();
} else { } else {
LogUtils.e(TAG, "裁剪成功但输出文件无效"); LogUtils.e(TAG, "handleCropResult裁剪成功但输出文件无效");
ToastUtils.show("裁剪失败:输出文件无效"); ToastUtils.show("裁剪失败:输出文件无效");
} }
} else if (resultCode == RESULT_CANCELED) { } else if (resultCode == RESULT_CANCELED) {
LogUtils.d(TAG, "裁剪取消"); LogUtils.d(TAG, "handleCropResult裁剪取消");
ToastUtils.show("裁剪已取消"); ToastUtils.show("裁剪已取消");
} else { } else {
LogUtils.e(TAG, "裁剪失败resultCode异常"); LogUtils.e(TAG, String.format("handleCropResult裁剪失败 | resultCode异常=%d", resultCode));
ToastUtils.show("裁剪失败"); ToastUtils.show("裁剪失败");
} }
} }
/** /**
* 双重刷新预览,确保背景加载最新数据 * 双重刷新预览,确保背景加载最新数据
* 移除:缓存清空逻辑
*/ */
private void doubleRefreshPreview() { private void doubleRefreshPreview() {
LogUtils.d(TAG, "doubleRefreshPreview执行双重刷新预览");
// 第一重刷新 // 第一重刷新
try { try {
mMemoryCachedBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); mMemoryCachedBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true);
mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor());
LogUtils.d(TAG, "【双重刷新】第一重完成"); LogUtils.d(TAG, "doubleRefreshPreview【双重刷新】第一重完成");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "【双重刷新】第一重异常:" + e.getMessage()); LogUtils.e(TAG, String.format("doubleRefreshPreview【双重刷新】第一重异常 | %s", e.getMessage()));
return; return;
} }
@@ -245,13 +256,25 @@ public class MainUnitTest2Activity extends AppCompatActivity {
try { try {
mMemoryCachedBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); mMemoryCachedBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true);
mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor());
LogUtils.d(TAG, "【双重刷新】第二重完成"); LogUtils.d(TAG, "doubleRefreshPreview【双重刷新】第二重完成");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "【双重刷新】第二重异常:" + e.getMessage()); LogUtils.e(TAG, String.format("doubleRefreshPreview【双重刷新】第二重异常 | %s", e.getMessage()));
} }
} }
} }
}, 200); }, DOUBLE_REFRESH_DELAY);
}
// ====================== 工具辅助方法(文件校验) ======================
/**
* 校验文件是否有效(存在且大小达标)
* @param file 待校验文件
* @return true=有效 false=无效
*/
private boolean isFileValid(File file) {
boolean isValid = file != null && file.exists() && file.length() > FILE_MIN_SIZE;
LogUtils.d(TAG, String.format("isFileValid文件校验 | 路径=%s | 结果=%b", file != null ? file.getAbsolutePath() : "null", isValid));
return isValid;
} }
} }

View File

@@ -12,32 +12,37 @@ import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.models.BackgroundBean;
import cc.winboll.studio.powerbell.utils.FileUtils; import cc.winboll.studio.powerbell.utils.FileUtils;
import cc.winboll.studio.powerbell.utils.ImageCropUtils; import cc.winboll.studio.powerbell.utils.ImageCropUtils;
import cc.winboll.studio.powerbell.views.BackgroundView; import cc.winboll.studio.powerbell.views.BackgroundView;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import cc.winboll.studio.powerbell.models.BackgroundBean;
/** /**
* 终极修复版放弃FileProvider直接用私有目录File路径彻底解决UID冲突 * 单元测试页面
* 功能:测试背景图加载、图片裁剪、双重刷新预览等功能
* 适配Java7 | API30 | 私有目录文件操作 | 无Uri冲突
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Describe 单元测试页:验证图片处理与背景预览相关逻辑
*/ */
public class MainUnitTestActivity extends AppCompatActivity { public class MainUnitTestActivity extends AppCompatActivity {
// ====================== 常量定义 ====================== // ====================== 静态常量区(置顶归类,消除魔法值) ======================
public static final String TAG = "MainUnitTestActivity"; public static final String TAG = "MainUnitTestActivity";
public static final int REQUEST_CROP_IMAGE = 0; public static final int REQUEST_CROP_IMAGE = 0;
private static final String ASSETS_TEST_IMAGE_PATH = "unittest/unittest-miku.png"; private static final String ASSETS_TEST_IMAGE_PATH = "unittest/unittest-miku.png";
private static final long FILE_MIN_SIZE = 100L;
private static final long DOUBLE_REFRESH_DELAY = 200L;
// ====================== 成员变量移除所有Uri相关 ====================== // ====================== 成员变量区(按功能分层,移除所有Uri相关 ======================
private BackgroundView mBackgroundView; private BackgroundView mBackgroundView;
private String mAppPrivateDirPath; private String mAppPrivateDirPath;
private File mPrivateTestImageFile; // 仅用File不用Uri private File mPrivateTestImageFile; // 仅用File不用Uri
private File mPrivateCropImageFile; private File mPrivateCropImageFile;
BackgroundBean mPreviewBackgroundBean; private BackgroundBean mPreviewBackgroundBean;
// ====================== 生命周期方法 ====================== // ====================== 生命周期方法按执行顺序onCreate→onActivityResult ======================
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -46,15 +51,9 @@ public class MainUnitTestActivity extends AppCompatActivity {
initBaseParams(); initBaseParams();
initViewAndEvent(); initViewAndEvent();
copyAssetsTestImageToPrivateDir(); copyAssetsTestImageToPrivateDir();
//loadBackgroundByFile(); // 直接用File加载 initBackgroundBean();
mPreviewBackgroundBean = new BackgroundBean(); doubleRefreshPreview();
mPreviewBackgroundBean.setBackgroundFileName(mPrivateTestImageFile.getName());
mPreviewBackgroundBean.setBackgroundFilePath(mPrivateTestImageFile.getAbsolutePath());
mPreviewBackgroundBean.setBackgroundScaledCompressFileName(mPrivateCropImageFile.getName());
mPreviewBackgroundBean.setBackgroundScaledCompressFilePath(mPrivateCropImageFile.getAbsolutePath());
mPreviewBackgroundBean.setIsUseBackgroundFile(true);
doubleRefreshPreview();
ToastUtils.show("单元测试页面启动完成"); ToastUtils.show("单元测试页面启动完成");
LogUtils.d(TAG, "=== 页面 onCreate 初始化结束 ==="); LogUtils.d(TAG, "=== 页面 onCreate 初始化结束 ===");
} }
@@ -62,36 +61,42 @@ public class MainUnitTestActivity extends AppCompatActivity {
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
LogUtils.d(TAG, "=== onActivityResult 回调 ==="); LogUtils.d(TAG, String.format("=== onActivityResult 回调 | requestCode=%d | resultCode=%d ===", requestCode, resultCode));
if (requestCode == REQUEST_CROP_IMAGE) { if (requestCode == REQUEST_CROP_IMAGE) {
handleCropResult(resultCode); handleCropResult(resultCode);
} }
} }
// ====================== 初始化相关方法 ====================== // ====================== 初始化相关方法基础参数→视图→背景Bean ======================
/**
* 初始化基础参数:私有目录、测试文件
*/
private void initBaseParams() { private void initBaseParams() {
LogUtils.d(TAG, "初始化基础参数:工具类+私有目录+File"); LogUtils.d(TAG, "initBaseParams初始化基础参数");
// 初始化私有目录无需权限无UID冲突
// 私有目录无需权限无UID冲突
mAppPrivateDirPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBellTest/"; mAppPrivateDirPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBellTest/";
File privateDir = new File(mAppPrivateDirPath); File privateDir = new File(mAppPrivateDirPath);
if (!privateDir.exists()) { if (!privateDir.exists()) {
privateDir.mkdirs(); boolean isDirCreated = privateDir.mkdirs();
LogUtils.d(TAG, "创建私有目录:" + mAppPrivateDirPath); LogUtils.d(TAG, String.format("initBaseParams创建私有目录 | 路径=%s | 结果=%b", mAppPrivateDirPath, isDirCreated));
} }
// 初始化File无Uri // 初始化测试文件与裁剪文件无Uri
File refFile = new File(ASSETS_TEST_IMAGE_PATH); File refFile = new File(ASSETS_TEST_IMAGE_PATH);
String uniqueTestName = FileUtils.createUniqueFileName(refFile) + ".png"; String uniqueTestName = FileUtils.createUniqueFileName(refFile) + ".png";
String uniqueCropName = uniqueTestName.replace(".png", "_crop.png"); String uniqueCropName = uniqueTestName.replace(".png", "_crop.png");
mPrivateTestImageFile = new File(mAppPrivateDirPath, uniqueTestName); mPrivateTestImageFile = new File(mAppPrivateDirPath, uniqueTestName);
mPrivateCropImageFile = new File(mAppPrivateDirPath, uniqueCropName); mPrivateCropImageFile = new File(mAppPrivateDirPath, uniqueCropName);
LogUtils.d(TAG, "测试图File路径" + mPrivateTestImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("initBaseParams测试图路径=%s", mPrivateTestImageFile.getAbsolutePath()));
LogUtils.d(TAG, String.format("initBaseParams裁剪图路径=%s", mPrivateCropImageFile.getAbsolutePath()));
} }
/**
* 初始化布局与控件事件
*/
private void initViewAndEvent() { private void initViewAndEvent() {
LogUtils.d(TAG, "初始化布局与控件事件"); LogUtils.d(TAG, "initViewAndEvent初始化布局与控件事件");
setContentView(R.layout.activity_mainunittest); setContentView(R.layout.activity_mainunittest);
mBackgroundView = (BackgroundView) findViewById(R.id.backgroundview); mBackgroundView = (BackgroundView) findViewById(R.id.backgroundview);
@@ -100,7 +105,7 @@ public class MainUnitTestActivity extends AppCompatActivity {
btnMain.setOnClickListener(new View.OnClickListener() { btnMain.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.d(TAG, "点击按钮跳转主页面"); LogUtils.d(TAG, "initViewAndEvent点击按钮跳转主页面");
startActivity(new Intent(MainUnitTestActivity.this, MainActivity.class)); startActivity(new Intent(MainUnitTestActivity.this, MainActivity.class));
} }
}); });
@@ -110,11 +115,11 @@ public class MainUnitTestActivity extends AppCompatActivity {
btnCrop.setOnClickListener(new View.OnClickListener() { btnCrop.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
LogUtils.d(TAG, "点击按钮启动裁剪File路径版"); LogUtils.d(TAG, "initViewAndEvent点击按钮启动裁剪File路径版");
ToastUtils.show("准备启动图片裁剪"); ToastUtils.show("准备启动图片裁剪");
if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { if (isFileValid(mPrivateTestImageFile)) {
startCropTestByFile(); // 直接传File startCropTestByFile();
} else { } else {
ToastUtils.show("测试图片未准备好,重新拷贝"); ToastUtils.show("测试图片未准备好,重新拷贝");
copyAssetsTestImageToPrivateDir(); copyAssetsTestImageToPrivateDir();
@@ -123,11 +128,28 @@ public class MainUnitTestActivity extends AppCompatActivity {
}); });
} }
// 从assets拷贝图片不变确保File存在 /**
* 初始化背景Bean
*/
private void initBackgroundBean() {
LogUtils.d(TAG, "initBackgroundBean初始化背景Bean");
mPreviewBackgroundBean = new BackgroundBean();
mPreviewBackgroundBean.setBackgroundFileName(mPrivateTestImageFile.getName());
mPreviewBackgroundBean.setBackgroundFilePath(mPrivateTestImageFile.getAbsolutePath());
mPreviewBackgroundBean.setBackgroundScaledCompressFileName(mPrivateCropImageFile.getName());
mPreviewBackgroundBean.setBackgroundScaledCompressFilePath(mPrivateCropImageFile.getAbsolutePath());
mPreviewBackgroundBean.setIsUseBackgroundFile(true);
LogUtils.d(TAG, "initBackgroundBean背景Bean初始化完成");
}
// ====================== 核心业务方法(文件拷贝→裁剪→结果处理→预览刷新) ======================
/**
* 从assets拷贝图片到私有目录
*/
private void copyAssetsTestImageToPrivateDir() { private void copyAssetsTestImageToPrivateDir() {
LogUtils.d(TAG, "开始拷贝assets图片到私有目录"); LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir开始拷贝assets图片到私有目录");
if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { if (isFileValid(mPrivateTestImageFile)) {
LogUtils.d(TAG, "图片已存在,无需拷贝"); LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir图片已存在,无需拷贝");
return; return;
} }
@@ -135,97 +157,87 @@ public class MainUnitTestActivity extends AppCompatActivity {
try { try {
inputStream = getAssets().open(ASSETS_TEST_IMAGE_PATH); inputStream = getAssets().open(ASSETS_TEST_IMAGE_PATH);
FileUtils.copyStreamToFile(inputStream, mPrivateTestImageFile); FileUtils.copyStreamToFile(inputStream, mPrivateTestImageFile);
LogUtils.d(TAG, "图片拷贝成功,大小:" + mPrivateTestImageFile.length() + "字节"); LogUtils.d(TAG, String.format("copyAssetsTestImageToPrivateDir图片拷贝成功 | 大小=%d字节", mPrivateTestImageFile.length()));
} catch (IOException e) { } catch (IOException e) {
LogUtils.e(TAG, "图片拷贝失败:" + e.getMessage(), e); LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir图片拷贝失败 | %s", e.getMessage()), e);
ToastUtils.show("图片准备失败"); ToastUtils.show("图片准备失败");
} finally { } finally {
if (inputStream != null) { if (inputStream != null) {
try { try {
inputStream.close(); inputStream.close();
} catch (IOException e) { } catch (IOException e) {
LogUtils.e(TAG, "关闭流失败:" + e.getMessage()); LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir关闭流失败 | %s", e.getMessage()));
} }
} }
} }
} }
// ====================== 核心业务方法全改为File路径 ====================== /**
/** 直接用File路径加载背景图无Uri无冲突 */ * 直接用File启动裁剪关键调用ImageCropUtils的File重载方法
// private void loadBackgroundByFile() { */
// LogUtils.d(TAG, "开始加载背景图File路径版");
// if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) {
// mBackgroundView.loadImage(mPrivateTestImageFile.getAbsolutePath()); // 直接传路径
// LogUtils.d(TAG, "背景图加载成功:" + mPrivateTestImageFile.getAbsolutePath());
// ToastUtils.show("背景图加载成功");
// } else {
// LogUtils.e(TAG, "背景图加载失败:文件无效");
// ToastUtils.show("背景图加载失败");
// }
// }
/** 直接用File启动裁剪关键调用ImageCropUtils的File重载方法 */
private void startCropTestByFile() { private void startCropTestByFile() {
LogUtils.d(TAG, "启动裁剪File路径版原图" + mPrivateTestImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("startCropTestByFile启动裁剪 | 原图=%s", mPrivateTestImageFile.getAbsolutePath()));
// 确保输出目录存在 // 确保输出目录存在
File cropParent = mPrivateCropImageFile.getParentFile(); File cropParent = mPrivateCropImageFile.getParentFile();
if (!cropParent.exists()) { if (!cropParent.exists()) {
cropParent.mkdirs(); boolean isDirCreated = cropParent.mkdirs();
LogUtils.d(TAG, String.format("startCropTestByFile创建裁剪目录 | 路径=%s | 结果=%b", cropParent.getAbsolutePath(), isDirCreated));
} }
// 调用ImageCropUtils的File参数方法核心绕开Uri // 调用ImageCropUtils的File参数方法核心绕开Uri
ImageCropUtils.startImageCrop( ImageCropUtils.startImageCrop(
this, this,
mPrivateTestImageFile, // 原图File mPrivateTestImageFile, // 原图File
mPrivateCropImageFile, // 输出File mPrivateCropImageFile, // 输出File
0, 0,
0, 0,
true, true,
REQUEST_CROP_IMAGE REQUEST_CROP_IMAGE
); );
LogUtils.d(TAG, "裁剪请求已发送输出路径" + mPrivateCropImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("startCropTestByFile裁剪请求已发送 | 输出路径=%s", mPrivateCropImageFile.getAbsolutePath()));
ToastUtils.show("已启动图片裁剪"); ToastUtils.show("已启动图片裁剪");
} }
/** 处理裁剪结果直接校验输出File */ /**
* 处理裁剪结果直接校验输出File
* @param resultCode 裁剪结果码
*/
private void handleCropResult(int resultCode) { private void handleCropResult(int resultCode) {
LogUtils.d(TAG, "裁剪回调处理resultCode=" + resultCode); LogUtils.d(TAG, String.format("handleCropResult裁剪回调处理 | resultCode=%d", resultCode));
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
if (mPrivateCropImageFile.exists() && mPrivateCropImageFile.length() > 100) { if (isFileValid(mPrivateCropImageFile)) {
mBackgroundView.loadImage(mPrivateCropImageFile.getAbsolutePath()); mBackgroundView.loadImage(mPrivateCropImageFile.getAbsolutePath());
LogUtils.d(TAG, "裁剪成功加载裁剪图" + mPrivateCropImageFile.getAbsolutePath()); LogUtils.d(TAG, String.format("handleCropResult裁剪成功 | 加载裁剪图=%s", mPrivateCropImageFile.getAbsolutePath()));
ToastUtils.show("裁剪成功"); ToastUtils.show("裁剪成功");
mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true); mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true);
doubleRefreshPreview(); doubleRefreshPreview();
} else { } else {
LogUtils.e(TAG, "裁剪成功但输出文件无效"); LogUtils.e(TAG, "handleCropResult裁剪成功但输出文件无效");
ToastUtils.show("裁剪失败:输出文件无效"); ToastUtils.show("裁剪失败:输出文件无效");
} }
} else if (resultCode == RESULT_CANCELED) { } else if (resultCode == RESULT_CANCELED) {
LogUtils.d(TAG, "裁剪取消"); LogUtils.d(TAG, "handleCropResult裁剪取消");
ToastUtils.show("裁剪已取消"); ToastUtils.show("裁剪已取消");
} else { } else {
LogUtils.e(TAG, "裁剪失败resultCode异常"); LogUtils.e(TAG, String.format("handleCropResult裁剪失败 | resultCode异常=%d", resultCode));
ToastUtils.show("裁剪失败"); ToastUtils.show("裁剪失败");
} }
} }
/** /**
* 双重刷新预览,确保背景加载最新数据 * 双重刷新预览,确保背景加载最新数据
* 移除:缓存清空逻辑
*/ */
private void doubleRefreshPreview() { private void doubleRefreshPreview() {
LogUtils.d(TAG, "doubleRefreshPreview执行双重刷新预览");
// 第一重刷新 // 第一重刷新
try { try {
mBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); mBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true);
mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor());
LogUtils.d(TAG, "【双重刷新】第一重完成"); LogUtils.d(TAG, "doubleRefreshPreview【双重刷新】第一重完成");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "【双重刷新】第一重异常:" + e.getMessage()); LogUtils.e(TAG, String.format("doubleRefreshPreview【双重刷新】第一重异常 | %s", e.getMessage()));
return; return;
} }
@@ -237,13 +249,25 @@ public class MainUnitTestActivity extends AppCompatActivity {
try { try {
mBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); mBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true);
mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor());
LogUtils.d(TAG, "【双重刷新】第二重完成"); LogUtils.d(TAG, "doubleRefreshPreview【双重刷新】第二重完成");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "【双重刷新】第二重异常:" + e.getMessage()); LogUtils.e(TAG, String.format("doubleRefreshPreview【双重刷新】第二重异常 | %s", e.getMessage()));
} }
} }
} }
}, 200); }, DOUBLE_REFRESH_DELAY);
}
// ====================== 工具辅助方法(文件校验) ======================
/**
* 校验文件是否有效(存在且大小达标)
* @param file 待校验文件
* @return true=有效 false=无效
*/
private boolean isFileValid(File file) {
boolean isValid = file != null && file.exists() && file.length() > FILE_MIN_SIZE;
LogUtils.d(TAG, String.format("isFileValid文件校验 | 路径=%s | 结果=%b", file != null ? file.getAbsolutePath() : "null", isValid));
return isValid;
} }
} }

View File

@@ -11,19 +11,18 @@ import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * 应用图标切换工具类(启用组件时创建对应快捷方式)
* @Date 2025/11/26 15:54
* @Describe 应用图标切换工具类(启用组件时创建对应快捷方式)
* 适配Java7 | API30 | 高低版本快捷方式创建兼容 * 适配Java7 | API30 | 高低版本快捷方式创建兼容
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Describe 应用启动器组件切换与桌面快捷方式创建工具,支持多组件管理与版本兼容
*/ */
public class APPPlusUtils { public class APPPlusUtils {
// ======================== 静态常量区 ======================== // ======================== 静态常量区(魔法值与标签管理)========================
public static final String TAG = "APPPlusUtils"; public static final String TAG = "APPPlusUtils";
// 快捷方式配置(名称+图标,需与实际资源匹配,预留扩展) private static final int SHORTCUT_ICON_DEFAULT = R.drawable.ic_launcher; // 默认快捷方式图标
// private static final String PLUS_SHORTCUT_NAME = "位置服务-Laojun"; private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; // 旧版快捷方式广播Action
// private static final int PLUS_SHORTCUT_ICON = R.mipmap.ic_launcher; // Laojun 图标资源
// ======================== 公共业务方法区 ======================== // ======================== 公共业务方法区(对外核心接口)========================
/** /**
* 切换应用启动器组件(禁用其他组件,启用目标组件) * 切换应用启动器组件(禁用其他组件,启用目标组件)
* @param context 上下文 * @param context 上下文
@@ -31,9 +30,15 @@ public class APPPlusUtils {
* @return 切换是否成功 * @return 切换是否成功
*/ */
public static boolean switchAppLauncherToComponent(Context context, String componentName) { public static boolean switchAppLauncherToComponent(Context context, String componentName) {
LogUtils.d(TAG, "switchAppLauncherToComponent() 调用,传入组件名" + componentName); LogUtils.d(TAG, String.format("switchAppLauncherToComponent调用 | 传入组件名=%s", componentName));
// 参数校验
if (context == null) { if (context == null) {
LogUtils.e(TAG, "switchAppLauncherToComponent() 切换失败:上下文为空"); LogUtils.e(TAG, "switchAppLauncherToComponent失败上下文为空");
return false;
}
if (componentName == null || componentName.isEmpty()) {
LogUtils.e(TAG, "switchAppLauncherToComponent失败组件名为空");
return false; return false;
} }
@@ -51,18 +56,61 @@ public class APPPlusUtils {
// 启用目标组件 // 启用目标组件
enableComponent(pm, targetComponent); enableComponent(pm, targetComponent);
LogUtils.d(TAG, "switchAppLauncherToComponent() 图标切换成功,目标组件" + componentName); LogUtils.d(TAG, String.format("switchAppLauncherToComponent成功 | 目标组件=%s", componentName));
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换成功", Toast.LENGTH_SHORT).show(); Toast.makeText(context, context.getString(R.string.app_name) + "图标切换成功", Toast.LENGTH_SHORT).show();
return true; return true;
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "switchAppLauncherToComponent() 图标切换失败:" + e.getMessage(), e); LogUtils.e(TAG, String.format("switchAppLauncherToComponent失败 | 异常信息=%s", e.getMessage()), e);
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
return false; return false;
} }
} }
// ======================== 私有辅助方法区(快捷方式创建) ======================== // ======================== 私有辅助方法区(组件状态控制)========================
/**
* 启用组件(带状态检查,避免重复操作)
* @param pm 包管理器
* @param component 目标组件
*/
private static void enableComponent(PackageManager pm, ComponentName component) {
int currentState = pm.getComponentEnabledSetting(component);
String componentName = component.getClassName();
if (currentState != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
LogUtils.d(TAG, String.format("enableComponent成功 | 组件=%s", componentName));
} else {
LogUtils.d(TAG, String.format("enableComponent无需操作 | 组件已启用=%s", componentName));
}
}
/**
* 禁用组件(带状态检查,避免重复操作)
* @param pm 包管理器
* @param component 目标组件
*/
private static void disableComponent(PackageManager pm, ComponentName component) {
int currentState = pm.getComponentEnabledSetting(component);
String componentName = component.getClassName();
if (currentState != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
LogUtils.d(TAG, String.format("disableComponent成功 | 组件=%s", componentName));
} else {
LogUtils.d(TAG, String.format("disableComponent无需操作 | 组件已禁用=%s", componentName));
}
}
// ======================== 私有辅助方法区(快捷方式创建)========================
/** /**
* 创建指定组件的桌面快捷方式(自动去重,兼容 Android 8.0+ * 创建指定组件的桌面快捷方式(自动去重,兼容 Android 8.0+
* @param context 上下文 * @param context 上下文
@@ -72,26 +120,31 @@ public class APPPlusUtils {
* @return 是否创建成功 * @return 是否创建成功
*/ */
private static boolean createComponentShortcut(Context context, ComponentName component, String name, int iconRes) { private static boolean createComponentShortcut(Context context, ComponentName component, String name, int iconRes) {
LogUtils.d(TAG, "createComponentShortcut() 调用,组件:" + component.getClassName() + ",名称:" + name); // 参数校验
if (context == null || component == null || name == null || iconRes == 0) { String componentName = component != null ? component.getClassName() : "null";
LogUtils.e(TAG, "createComponentShortcut() 快捷方式创建失败:参数为空"); LogUtils.d(TAG, String.format("createComponentShortcut调用 | 组件=%s | 名称=%s", componentName, name));
if (context == null || component == null || name == null || name.isEmpty()) {
LogUtils.e(TAG, "createComponentShortcut失败上下文、组件或名称为空");
return false; return false;
} }
// 图标资源默认值补全
int finalIconRes = iconRes != 0 ? iconRes : SHORTCUT_ICON_DEFAULT;
// Android 8.0+API 26+):使用 ShortcutManager系统推荐 // Android 8.0+API 26+):使用 ShortcutManager系统推荐
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try { try {
PackageManager pm = context.getPackageManager();
android.content.pm.ShortcutManager shortcutManager = context.getSystemService(android.content.pm.ShortcutManager.class); android.content.pm.ShortcutManager shortcutManager = context.getSystemService(android.content.pm.ShortcutManager.class);
if (shortcutManager == null || !shortcutManager.isRequestPinShortcutSupported()) { if (shortcutManager == null || !shortcutManager.isRequestPinShortcutSupported()) {
LogUtils.w(TAG, "createComponentShortcut() 系统不支持创建快捷方式"); LogUtils.w(TAG, "createComponentShortcut系统不支持创建快捷方式");
return false; return false;
} }
// 检查是否已存在该组件的快捷方式(去重) // 检查是否已存在该组件的快捷方式(去重)
for (android.content.pm.ShortcutInfo info : shortcutManager.getPinnedShortcuts()) { for (android.content.pm.ShortcutInfo info : shortcutManager.getPinnedShortcuts()) {
if (component.getClassName().equals(info.getIntent().getComponent().getClassName())) { if (component.getClassName().equals(info.getIntent().getComponent().getClassName())) {
LogUtils.d(TAG, "createComponentShortcut() 快捷方式已存在" + component.getClassName()); LogUtils.d(TAG, String.format("createComponentShortcut快捷方式已存在=%s", componentName));
return true; return true;
} }
} }
@@ -106,17 +159,17 @@ public class APPPlusUtils {
android.content.pm.ShortcutInfo shortcutInfo = new android.content.pm.ShortcutInfo.Builder(context, component.getClassName()) android.content.pm.ShortcutInfo shortcutInfo = new android.content.pm.ShortcutInfo.Builder(context, component.getClassName())
.setShortLabel(name) .setShortLabel(name)
.setLongLabel(name) .setLongLabel(name)
.setIcon(android.graphics.drawable.Icon.createWithResource(context, iconRes)) .setIcon(android.graphics.drawable.Icon.createWithResource(context, finalIconRes))
.setIntent(launchIntent) .setIntent(launchIntent)
.build(); .build();
// 请求创建快捷方式(需用户确认) // 请求创建快捷方式(需用户确认)
shortcutManager.requestPinShortcut(shortcutInfo, null); shortcutManager.requestPinShortcut(shortcutInfo, null);
LogUtils.d(TAG, "createComponentShortcut() Android O+ 快捷方式创建请求已发送"); LogUtils.d(TAG, "createComponentShortcutAndroid O+ 快捷方式创建请求已发送");
return true; return true;
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "createComponentShortcut() Android O+ 快捷方式创建失败:" + e.getMessage(), e); LogUtils.e(TAG, String.format("createComponentShortcut失败 | Android O+ 异常=%s", e.getMessage()), e);
return false; return false;
} }
} else { } else {
@@ -129,61 +182,22 @@ public class APPPlusUtils {
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 构建创建快捷方式的广播意图 // 构建创建快捷方式的广播意图
Intent installIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT"); Intent installIntent = new Intent(ACTION_INSTALL_SHORTCUT);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent); installIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); installIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, installIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(context, iconRes)); Intent.ShortcutIconResource.fromContext(context, finalIconRes));
installIntent.putExtra("duplicate", false); // 禁止重复创建 installIntent.putExtra("duplicate", false); // 禁止重复创建
context.sendBroadcast(installIntent); context.sendBroadcast(installIntent);
LogUtils.d(TAG, "createComponentShortcut() Android O- 快捷方式创建广播已发送"); LogUtils.d(TAG, "createComponentShortcutAndroid O- 快捷方式创建广播已发送");
return true; return true;
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "createComponentShortcut() Android O- 快捷方式创建失败:" + e.getMessage(), e); LogUtils.e(TAG, String.format("createComponentShortcut失败 | Android O- 异常=%s", e.getMessage()), e);
return false; return false;
} }
} }
} }
// ======================== 私有辅助方法区(组件状态控制) ========================
/**
* 启用组件(带状态检查,避免重复操作)
* @param pm 包管理器
* @param component 目标组件
*/
private static void enableComponent(PackageManager pm, ComponentName component) {
int currentState = pm.getComponentEnabledSetting(component);
if (currentState != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
LogUtils.d(TAG, "enableComponent() 组件已启用:" + component.getClassName());
} else {
LogUtils.d(TAG, "enableComponent() 组件无需操作,已启用:" + component.getClassName());
}
}
/**
* 禁用组件(带状态检查,避免重复操作)
* @param pm 包管理器
* @param component 目标组件
*/
private static void disableComponent(PackageManager pm, ComponentName component) {
int currentState = pm.getComponentEnabledSetting(component);
if (currentState != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
LogUtils.d(TAG, "disableComponent() 组件已禁用:" + component.getClassName());
} else {
LogUtils.d(TAG, "disableComponent() 组件无需操作,已禁用:" + component.getClassName());
}
}
} }

View File

@@ -8,68 +8,76 @@ import java.util.ArrayList;
/** /**
* 应用缓存工具类适配Android API 30基于Java 7编写 * 应用缓存工具类适配Android API 30基于Java 7编写
* 负责电池信息的缓存、持久化与管理 * 负责电池信息的缓存、持久化与管理
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Describe 电池信息缓存工具:实现电量变化记录、持久化存储与缓存限制
*/ */
public class AppCacheUtils { public class AppCacheUtils {
// ===================== 常量定义区 ===================== // ===================== 静态常量区(置顶归类,消除魔法值) =====================
public static final String TAG = "AppCacheUtils"; public static final String TAG = "AppCacheUtils";
private static final int MAX_BATTERY_RECORD_COUNT = 180; // 电池记录最大条数限制
// ===================== 静态属性区 ===================== // ===================== 静态成员区(单例相关) =====================
// 单例实例
private static AppCacheUtils sInstance; private static AppCacheUtils sInstance;
// ===================== 成员属性区 ===================== // ===================== 成员变量区(按功能分层) =====================
// 上下文环境(使用ApplicationContext避免内存泄漏 private Context mContext; // ApplicationContext避免内存泄漏
private Context mContext; private ArrayList<BatteryInfoBean> mBatteryInfoList; // 电池信息缓存列表
// 电池信息缓存列表
private ArrayList<BatteryInfoBean> mBatteryInfoList;
// ===================== 单例方法区 ===================== // ===================== 单例方法区(线程安全) =====================
/** /**
* 获取单例实例 * 获取单例实例
* @param context 上下文内部会转换为ApplicationContext * @param context 上下文内部会转换为ApplicationContext
* @return 唯一AppCacheUtils实例 * @return 唯一AppCacheUtils实例
*/ */
public static synchronized AppCacheUtils getInstance(Context context) { public static synchronized AppCacheUtils getInstance(Context context) {
LogUtils.d(TAG, "getInstance() 调用传入Context类型" + (context != null ? context.getClass().getSimpleName() : "null")); String contextType = context != null ? context.getClass().getSimpleName() : "null";
LogUtils.d(TAG, String.format("getInstance调用 | 传入Context类型=%s", contextType));
if (sInstance == null) { if (sInstance == null) {
if (context == null) {
LogUtils.e(TAG, "getInstance失败传入Context为null");
throw new IllegalArgumentException("Context cannot be null");
}
sInstance = new AppCacheUtils(context.getApplicationContext()); sInstance = new AppCacheUtils(context.getApplicationContext());
LogUtils.d(TAG, "getInstance():单例实例初始化"); LogUtils.d(TAG, "getInstance单例实例初始化完成");
} }
return sInstance; return sInstance;
} }
// ===================== 构造方法区(私有 ===================== // ===================== 私有构造方法区(禁止外部实例化 =====================
/** /**
* 私有构造方法,禁止外部实例化 * 私有构造方法,初始化缓存列表并加载持久化数据
* @param context ApplicationContext * @param context ApplicationContext
*/ */
private AppCacheUtils(Context context) { private AppCacheUtils(Context context) {
LogUtils.d(TAG, "AppCacheUtils() 构造方法调用"); LogUtils.d(TAG, "AppCacheUtils构造方法调用");
mContext = context; mContext = context;
mBatteryInfoList = new ArrayList<BatteryInfoBean>(); mBatteryInfoList = new ArrayList<BatteryInfoBean>();
loadAppCacheData(); loadAppCacheData();
LogUtils.d(TAG, "AppCacheUtils() 构造完成初始电池信息数量" + mBatteryInfoList.size()); LogUtils.d(TAG, String.format("AppCacheUtils构造完成 | 初始电池信息数量=%d", mBatteryInfoList.size()));
} }
// ===================== 公共业务方法区 ===================== // ===================== 公共业务方法区(对外暴露接口) =====================
/** /**
* 添加电池电量变化记录(仅当电量变化时添加) * 添加电池电量变化记录(仅当电量变化时添加)
* @param batteryValue 电池电量值 * @param batteryValue 电池电量值
*/ */
public void addChangingTime(int batteryValue) { public void addChangingTime(int batteryValue) {
LogUtils.d(TAG, "addChangingTime() 调用,传入电量值" + batteryValue); LogUtils.d(TAG, String.format("addChangingTime调用 | 传入电量值=%d", batteryValue));
if (mBatteryInfoList.isEmpty()) { if (mBatteryInfoList.isEmpty()) {
addChangingTimeToList(batteryValue); addChangingTimeToList(batteryValue);
LogUtils.d(TAG, "addChangingTime():缓存列表为空,直接添加记录"); LogUtils.d(TAG, "addChangingTime缓存列表为空直接添加记录");
return; return;
} }
// 对比最后一条记录的电量值,避免重复添加 // 对比最后一条记录的电量值,避免重复添加
int lastBatteryValue = mBatteryInfoList.get(mBatteryInfoList.size() - 1).getBatteryValue(); int lastBatteryValue = mBatteryInfoList.get(mBatteryInfoList.size() - 1).getBatteryValue();
if (lastBatteryValue != batteryValue) { if (lastBatteryValue != batteryValue) {
addChangingTimeToList(batteryValue); addChangingTimeToList(batteryValue);
LogUtils.d(TAG, "addChangingTime():电量变化,添加新记录(原电量:" + lastBatteryValue + ",新电量:" + batteryValue + ""); LogUtils.d(TAG, String.format("addChangingTime电量变化添加新记录 | 原电量=%d | 新电量=%d", lastBatteryValue, batteryValue));
} else { } else {
LogUtils.d(TAG, "addChangingTime():电量未变化,跳过添加"); LogUtils.d(TAG, "addChangingTime电量未变化跳过添加");
} }
} }
@@ -78,7 +86,7 @@ public class AppCacheUtils {
* @return 完整的电池信息列表 * @return 完整的电池信息列表
*/ */
public ArrayList<BatteryInfoBean> getArrayListBatteryInfo() { public ArrayList<BatteryInfoBean> getArrayListBatteryInfo() {
LogUtils.d(TAG, "getArrayListBatteryInfo() 调用,当前缓存数量" + mBatteryInfoList.size()); LogUtils.d(TAG, String.format("getArrayListBatteryInfo调用 | 当前缓存数量=%d", mBatteryInfoList.size()));
loadAppCacheData(); loadAppCacheData();
return mBatteryInfoList; return mBatteryInfoList;
} }
@@ -87,27 +95,29 @@ public class AppCacheUtils {
* 清除所有电池历史记录 * 清除所有电池历史记录
*/ */
public void clearBatteryHistory() { public void clearBatteryHistory() {
LogUtils.d(TAG, "clearBatteryHistory() 调用,清除前缓存数量" + mBatteryInfoList.size()); LogUtils.d(TAG, String.format("clearBatteryHistory调用 | 清除前缓存数量=%d", mBatteryInfoList.size()));
mBatteryInfoList.clear(); mBatteryInfoList.clear();
saveAppCacheData(); saveAppCacheData();
LogUtils.d(TAG, "clearBatteryHistory() 完成,缓存已清空"); LogUtils.d(TAG, "clearBatteryHistory完成 | 缓存已清空");
} }
// ===================== 私有辅助方法区 ===================== // ===================== 私有辅助方法区(内部业务逻辑) =====================
/** /**
* 内部方法:添加电量记录到列表并持久化 * 内部方法:添加电量记录到列表并持久化
* @param batteryValue 电池电量值 * @param batteryValue 电池电量值
*/ */
private void addChangingTimeToList(int batteryValue) { private void addChangingTimeToList(int batteryValue) {
LogUtils.d(TAG, "addChangingTimeToList() 调用,传入电量值" + batteryValue); LogUtils.d(TAG, String.format("addChangingTimeToList调用 | 传入电量值=%d", batteryValue));
// 限制列表最大长度为180条避免内存溢出
if (mBatteryInfoList.size() > 180) { // 限制列表最大长度,避免内存溢出
if (mBatteryInfoList.size() >= MAX_BATTERY_RECORD_COUNT) {
mBatteryInfoList.remove(0); mBatteryInfoList.remove(0);
LogUtils.d(TAG, "addChangingTimeToList():列表超过180条,移除最旧记录"); LogUtils.d(TAG, String.format("addChangingTimeToList列表超过%d条,移除最旧记录", MAX_BATTERY_RECORD_COUNT));
} }
BatteryInfoBean batteryInfo = new BatteryInfoBean(System.currentTimeMillis(), batteryValue); BatteryInfoBean batteryInfo = new BatteryInfoBean(System.currentTimeMillis(), batteryValue);
mBatteryInfoList.add(batteryInfo); mBatteryInfoList.add(batteryInfo);
LogUtils.d(TAG, "addChangingTimeToList():添加新记录 - 电量" + batteryInfo.getBatteryValue() + ",时间戳:" + batteryInfo.getTimeStamp()); LogUtils.d(TAG, String.format("addChangingTimeToList添加新记录 | 电量=%d | 时间戳=%d", batteryInfo.getBatteryValue(), batteryInfo.getTimeStamp()));
saveAppCacheData(); saveAppCacheData();
} }
@@ -115,19 +125,19 @@ public class AppCacheUtils {
* 从文件加载缓存数据 * 从文件加载缓存数据
*/ */
private void loadAppCacheData() { private void loadAppCacheData() {
LogUtils.d(TAG, "loadAppCacheData() 调用,开始加载持久化数据"); LogUtils.d(TAG, "loadAppCacheData调用 | 开始加载持久化数据");
mBatteryInfoList.clear(); mBatteryInfoList.clear();
BatteryInfoBean.loadBeanList(mContext, mBatteryInfoList, BatteryInfoBean.class); BatteryInfoBean.loadBeanList(mContext, mBatteryInfoList, BatteryInfoBean.class);
LogUtils.d(TAG, "loadAppCacheData() 完成,加载数据数量" + mBatteryInfoList.size()); LogUtils.d(TAG, String.format("loadAppCacheData完成 | 加载数据数量=%d", mBatteryInfoList.size()));
} }
/** /**
* 保存缓存数据到文件 * 保存缓存数据到文件
*/ */
private void saveAppCacheData() { private void saveAppCacheData() {
LogUtils.d(TAG, "saveAppCacheData() 调用,保存数据数量" + mBatteryInfoList.size()); LogUtils.d(TAG, String.format("saveAppCacheData调用 | 保存数据数量=%d", mBatteryInfoList.size()));
BatteryInfoBean.saveBeanList(mContext, mBatteryInfoList, BatteryInfoBean.class); BatteryInfoBean.saveBeanList(mContext, mBatteryInfoList, BatteryInfoBean.class);
LogUtils.d(TAG, "saveAppCacheData() 完成,数据已持久化"); LogUtils.d(TAG, "saveAppCacheData完成 | 数据已持久化");
} }
} }

View File

@@ -7,10 +7,10 @@ import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean; import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等)
* @Date 2025/12/17 13:59
* @Describe 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等)
* 适配Java7 | API30 | 小米手机,单例模式,线程安全,配置持久化 * 适配Java7 | API30 | 小米手机,单例模式,线程安全,配置持久化
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Describe 应用配置全量管理工具,支持配置持久化、自动校准、线程安全访问
*/ */
public class AppConfigUtils { public class AppConfigUtils {
// ======================== 静态常量区(魔法值统一管理)======================== // ======================== 静态常量区(魔法值统一管理)========================
@@ -21,68 +21,75 @@ public class AppConfigUtils {
private static final int MIN_INTERVAL_TIME = 1000; // 最小提醒间隔ms private static final int MIN_INTERVAL_TIME = 1000; // 最小提醒间隔ms
private static final int MIN_DETECT_INTERVAL = 500; // 最小电量检测间隔ms private static final int MIN_DETECT_INTERVAL = 500; // 最小电量检测间隔ms
// ======================== 静态属性区(单例实例)======================== // ======================== 静态成员区(单例实例)========================
private static AppConfigUtils sInstance; // 单例实例(私有,禁止外部直接创建 private static volatile AppConfigUtils sInstance; // 单例实例(volatile保障双重校验锁有效性
// ======================== 成员属性区(按依赖优先级排序)======================== // ======================== 成员变量区(按依赖优先级排序final/volatile保障线程安全========================
private final Context mContext; // 应用上下文(避免内存泄漏final保障 private final Context mContext; // 应用上下文(ApplicationContext避免内存泄漏)
private App mApplication; // 应用Application实例 private final App mApplication; // 应用Application实例final保障不可变
public volatile AppConfigBean mAppConfigBean; // 应用配置Bean持久化核心volatile保障线程安全 public volatile AppConfigBean mAppConfigBean; // 应用配置Bean持久化核心volatile保障线程安全
private volatile boolean mIsServiceEnabled = false; // 服务开关缓存状态减少Bean读取次数 private volatile boolean mIsServiceEnabled = false; // 服务开关缓存状态减少Bean读取次数
// ======================== 单例相关方法区(构造+获取======================== // ======================== 单例相关方法区(双重校验锁+构造方法========================
/**
* 私有构造方法,禁止外部实例化
* @param context 上下文内部转换为ApplicationContext
*/
private AppConfigUtils(Context context) {
LogUtils.d(TAG, "AppConfigUtils() 构造方法调用");
this.mContext = context.getApplicationContext(); // 强制取应用上下文,杜绝内存泄漏
this.mApplication = (App) context.getApplicationContext();
mAppConfigBean = new AppConfigBean();
loadAppConfig(); // 加载持久化配置
LogUtils.d(TAG, "AppConfigUtils() 构造完成,配置初始化成功");
}
/** /**
* 双重校验锁单例获取方法,线程安全 * 双重校验锁单例获取方法,线程安全
* @param context 上下文不可为null * @param context 上下文不可为null
* @return 单例实例 * @return 单例实例
*/ */
public static AppConfigUtils getInstance(Context context) { public static AppConfigUtils getInstance(Context context) {
LogUtils.d(TAG, "getInstance() 调用传入Context类型" + (context != null ? context.getClass().getSimpleName() : "null")); String contextType = context != null ? context.getClass().getSimpleName() : "null";
LogUtils.d(TAG, String.format("getInstance调用 | 传入Context类型=%s", contextType));
if (context == null) { if (context == null) {
LogUtils.e(TAG, "getInstance() Context不能为空,获取实例失败"); LogUtils.e(TAG, "getInstance失败:Context不能为空");
throw new IllegalArgumentException("Context cannot be null"); throw new IllegalArgumentException("Context cannot be null");
} }
if (sInstance == null) { if (sInstance == null) {
synchronized (AppConfigUtils.class) { synchronized (AppConfigUtils.class) {
if (sInstance == null) { if (sInstance == null) {
sInstance = new AppConfigUtils(context); sInstance = new AppConfigUtils(context);
LogUtils.d(TAG, "getInstance() 单例实例创建成功"); LogUtils.d(TAG, "getInstance单例实例创建成功");
} }
} }
} }
LogUtils.d(TAG, "getInstance() 单例实例获取成功");
LogUtils.d(TAG, "getInstance单例实例获取成功");
return sInstance; return sInstance;
} }
/**
* 私有构造方法,禁止外部实例化
* @param context 上下文内部转换为ApplicationContext
*/
private AppConfigUtils(Context context) {
LogUtils.d(TAG, "AppConfigUtils构造方法调用");
this.mContext = context.getApplicationContext();
this.mApplication = (App) context.getApplicationContext();
mAppConfigBean = new AppConfigBean();
loadAppConfig(); // 加载持久化配置
LogUtils.d(TAG, "AppConfigUtils构造完成配置初始化成功");
}
// ======================== 核心配置持久化方法区(加载+保存)======================== // ======================== 核心配置持久化方法区(加载+保存)========================
/** /**
* 加载应用配置(初始化/重载通用入口) * 加载应用配置(初始化/重载通用入口)
* @return 加载后的应用配置Bean * @return 加载后的应用配置Bean
*/ */
public AppConfigBean loadAppConfig() { public AppConfigBean loadAppConfig() {
LogUtils.d(TAG, "loadAppConfig() 开始加载应用配置"); LogUtils.d(TAG, "loadAppConfig调用:开始加载应用配置");
AppConfigBean savedAppBean = (AppConfigBean) AppConfigBean.loadBean(mContext, AppConfigBean.class); AppConfigBean savedAppBean = (AppConfigBean) AppConfigBean.loadBean(mContext, AppConfigBean.class);
if (savedAppBean != null) { if (savedAppBean != null) {
mAppConfigBean = savedAppBean; mAppConfigBean = savedAppBean;
LogUtils.d(TAG, "loadAppConfig() 应用配置加载成功,阈值:充电" + mAppConfigBean.getChargeReminderValue() + "%,耗电" + mAppConfigBean.getUsageReminderValue() + "%"); LogUtils.d(TAG, String.format("loadAppConfig成功 | 充电阈值=%d%% | 耗电阈值=%d%%",
mAppConfigBean.getChargeReminderValue(), mAppConfigBean.getUsageReminderValue()));
} else { } else {
mAppConfigBean = new AppConfigBean(); mAppConfigBean = new AppConfigBean();
AppConfigBean.saveBean(mContext, mAppConfigBean); AppConfigBean.saveBean(mContext, mAppConfigBean);
LogUtils.d(TAG, "loadAppConfig() 无已保存配置,使用默认值并持久化"); LogUtils.d(TAG, "loadAppConfig无已保存配置,使用默认值并持久化");
} }
return mAppConfigBean; return mAppConfigBean;
} }
@@ -91,7 +98,7 @@ public class AppConfigUtils {
*/ */
private void saveAppConfig() { private void saveAppConfig() {
AppConfigBean.saveBean(mContext, mAppConfigBean); AppConfigBean.saveBean(mContext, mAppConfigBean);
LogUtils.d(TAG, "saveAppConfig() 应用配置保存成功"); LogUtils.d(TAG, "saveAppConfig应用配置保存成功");
} }
// ======================== 充电提醒配置方法区(开关+阈值)======================== // ======================== 充电提醒配置方法区(开关+阈值)========================
@@ -100,14 +107,16 @@ public class AppConfigUtils {
* @param isEnabled 目标状态true=开启false=关闭) * @param isEnabled 目标状态true=开启false=关闭)
*/ */
public void setChargeReminderEnabled(final boolean isEnabled) { public void setChargeReminderEnabled(final boolean isEnabled) {
LogUtils.d(TAG, "setChargeReminderEnabled() 调用,传入状态" + isEnabled); LogUtils.d(TAG, String.format("setChargeReminderEnabled调用 | 传入状态=%b", isEnabled));
if (isEnabled == mAppConfigBean.isEnableChargeReminder()) { if (isEnabled == mAppConfigBean.isEnableChargeReminder()) {
LogUtils.d(TAG, "setChargeReminderEnabled() 充电提醒状态无变化,无需操作"); LogUtils.d(TAG, "setChargeReminderEnabled充电提醒状态无变化,无需操作");
return; return;
} }
mAppConfigBean.setEnableChargeReminder(isEnabled); mAppConfigBean.setEnableChargeReminder(isEnabled);
saveAppConfig(); saveAppConfig();
LogUtils.d(TAG, "setChargeReminderEnabled() 充电提醒状态更新为:" + (isEnabled ? "开启" : "关闭")); LogUtils.d(TAG, String.format("setChargeReminderEnabled成功 | 充电提醒状态=%s", isEnabled ? "开启" : "关闭"));
} }
/** /**
@@ -116,7 +125,7 @@ public class AppConfigUtils {
*/ */
public boolean isChargeReminderEnabled() { public boolean isChargeReminderEnabled() {
boolean isEnabled = mAppConfigBean.isEnableChargeReminder(); boolean isEnabled = mAppConfigBean.isEnableChargeReminder();
LogUtils.d(TAG, "isChargeReminderEnabled() 获取充电提醒状态" + (isEnabled ? "开启" : "关闭")); LogUtils.d(TAG, String.format("isChargeReminderEnabled获取充电提醒状态=%s", isEnabled ? "开启" : "关闭"));
return isEnabled; return isEnabled;
} }
@@ -125,15 +134,17 @@ public class AppConfigUtils {
* @param value 目标阈值 * @param value 目标阈值
*/ */
public void setChargeReminderValue(final int value) { public void setChargeReminderValue(final int value) {
LogUtils.d(TAG, "setChargeReminderValue() 调用,传入阈值" + value); LogUtils.d(TAG, String.format("setChargeReminderValue调用 | 传入阈值=%d", value));
final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE); final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getChargeReminderValue()) { if (calibratedValue == mAppConfigBean.getChargeReminderValue()) {
LogUtils.d(TAG, "setChargeReminderValue() 充电提醒阈值无变化,无需操作"); LogUtils.d(TAG, "setChargeReminderValue充电提醒阈值无变化,无需操作");
return; return;
} }
mAppConfigBean.setChargeReminderValue(calibratedValue); mAppConfigBean.setChargeReminderValue(calibratedValue);
saveAppConfig(); saveAppConfig();
LogUtils.d(TAG, "setChargeReminderValue() 充电提醒阈值更新为:" + calibratedValue + "%"); LogUtils.d(TAG, String.format("setChargeReminderValue成功 | 充电提醒阈值=%d%%", calibratedValue));
} }
/** /**
@@ -142,7 +153,7 @@ public class AppConfigUtils {
*/ */
public int getChargeReminderValue() { public int getChargeReminderValue() {
int value = mAppConfigBean.getChargeReminderValue(); int value = mAppConfigBean.getChargeReminderValue();
LogUtils.d(TAG, "getChargeReminderValue() 获取充电提醒阈值" + value + "%"); LogUtils.d(TAG, String.format("getChargeReminderValue获取充电提醒阈值=%d%%", value));
return value; return value;
} }
@@ -152,14 +163,16 @@ public class AppConfigUtils {
* @param isEnabled 目标状态true=开启false=关闭) * @param isEnabled 目标状态true=开启false=关闭)
*/ */
public void setUsageReminderEnabled(final boolean isEnabled) { public void setUsageReminderEnabled(final boolean isEnabled) {
LogUtils.d(TAG, "setUsageReminderEnabled() 调用,传入状态" + isEnabled); LogUtils.d(TAG, String.format("setUsageReminderEnabled调用 | 传入状态=%b", isEnabled));
if (isEnabled == mAppConfigBean.isEnableUsageReminder()) { if (isEnabled == mAppConfigBean.isEnableUsageReminder()) {
LogUtils.d(TAG, "setUsageReminderEnabled() 耗电提醒状态无变化,无需操作"); LogUtils.d(TAG, "setUsageReminderEnabled耗电提醒状态无变化,无需操作");
return; return;
} }
mAppConfigBean.setEnableUsageReminder(isEnabled); mAppConfigBean.setEnableUsageReminder(isEnabled);
saveAppConfig(); saveAppConfig();
LogUtils.d(TAG, "setUsageReminderEnabled() 耗电提醒状态更新为:" + (isEnabled ? "开启" : "关闭")); LogUtils.d(TAG, String.format("setUsageReminderEnabled成功 | 耗电提醒状态=%s", isEnabled ? "开启" : "关闭"));
} }
/** /**
@@ -168,7 +181,7 @@ public class AppConfigUtils {
*/ */
public boolean isUsageReminderEnabled() { public boolean isUsageReminderEnabled() {
boolean isEnabled = mAppConfigBean.isEnableUsageReminder(); boolean isEnabled = mAppConfigBean.isEnableUsageReminder();
LogUtils.d(TAG, "isUsageReminderEnabled() 获取耗电提醒状态" + (isEnabled ? "开启" : "关闭")); LogUtils.d(TAG, String.format("isUsageReminderEnabled获取耗电提醒状态=%s", isEnabled ? "开启" : "关闭"));
return isEnabled; return isEnabled;
} }
@@ -177,15 +190,17 @@ public class AppConfigUtils {
* @param value 目标阈值 * @param value 目标阈值
*/ */
public void setUsageReminderValue(final int value) { public void setUsageReminderValue(final int value) {
LogUtils.d(TAG, "setUsageReminderValue() 调用,传入阈值" + value); LogUtils.d(TAG, String.format("setUsageReminderValue调用 | 传入阈值=%d", value));
final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE); final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getUsageReminderValue()) { if (calibratedValue == mAppConfigBean.getUsageReminderValue()) {
LogUtils.d(TAG, "setUsageReminderValue() 耗电提醒阈值无变化,无需操作"); LogUtils.d(TAG, "setUsageReminderValue耗电提醒阈值无变化,无需操作");
return; return;
} }
mAppConfigBean.setUsageReminderValue(calibratedValue); mAppConfigBean.setUsageReminderValue(calibratedValue);
saveAppConfig(); saveAppConfig();
LogUtils.d(TAG, "setUsageReminderValue() 耗电提醒阈值更新为:" + calibratedValue + "%"); LogUtils.d(TAG, String.format("setUsageReminderValue成功 | 耗电提醒阈值=%d%%", calibratedValue));
} }
/** /**
@@ -194,7 +209,7 @@ public class AppConfigUtils {
*/ */
public int getUsageReminderValue() { public int getUsageReminderValue() {
int value = mAppConfigBean.getUsageReminderValue(); int value = mAppConfigBean.getUsageReminderValue();
LogUtils.d(TAG, "getUsageReminderValue() 获取耗电提醒阈值" + value + "%"); LogUtils.d(TAG, String.format("getUsageReminderValue获取耗电提醒阈值=%d%%", value));
return value; return value;
} }
@@ -204,13 +219,15 @@ public class AppConfigUtils {
* @param isCharging 充电状态true=充电中false=未充电) * @param isCharging 充电状态true=充电中false=未充电)
*/ */
public void setCharging(boolean isCharging) { public void setCharging(boolean isCharging) {
LogUtils.d(TAG, "setCharging() 调用,传入状态" + isCharging); LogUtils.d(TAG, String.format("setCharging调用 | 传入状态=%b", isCharging));
if (isCharging == mAppConfigBean.isCharging()) { if (isCharging == mAppConfigBean.isCharging()) {
LogUtils.d(TAG, "setCharging() 充电状态无变化,无需操作"); LogUtils.d(TAG, "setCharging充电状态无变化,无需操作");
return; return;
} }
mAppConfigBean.setIsCharging(isCharging); mAppConfigBean.setIsCharging(isCharging);
LogUtils.d(TAG, "setCharging() 充电状态更新为:" + (isCharging ? "充电中" : "未充电")); LogUtils.d(TAG, String.format("setCharging成功 | 充电状态=%s", isCharging ? "充电中" : "未充电"));
} }
/** /**
@@ -219,7 +236,7 @@ public class AppConfigUtils {
*/ */
public boolean isCharging() { public boolean isCharging() {
boolean isCharging = mAppConfigBean.isCharging(); boolean isCharging = mAppConfigBean.isCharging();
LogUtils.d(TAG, "isCharging() 获取充电状态" + (isCharging ? "充电中" : "未充电")); LogUtils.d(TAG, String.format("isCharging获取充电状态=%s", isCharging ? "充电中" : "未充电"));
return isCharging; return isCharging;
} }
@@ -228,14 +245,16 @@ public class AppConfigUtils {
* @param value 当前电量 * @param value 当前电量
*/ */
public void setCurrentBatteryValue(int value) { public void setCurrentBatteryValue(int value) {
LogUtils.d(TAG, "setCurrentBatteryValue() 调用,传入电量" + value); LogUtils.d(TAG, String.format("setCurrentBatteryValue调用 | 传入电量=%d", value));
int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE); int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getCurrentBatteryValue()) { if (calibratedValue == mAppConfigBean.getCurrentBatteryValue()) {
LogUtils.d(TAG, "setCurrentBatteryValue() 电池电量无变化,无需操作"); LogUtils.d(TAG, "setCurrentBatteryValue电池电量无变化,无需操作");
return; return;
} }
mAppConfigBean.setCurrentBatteryValue(calibratedValue); mAppConfigBean.setCurrentBatteryValue(calibratedValue);
LogUtils.d(TAG, "setCurrentBatteryValue() 电池电量更新为:" + calibratedValue + "%"); LogUtils.d(TAG, String.format("setCurrentBatteryValue成功 | 电池电量=%d%%", calibratedValue));
} }
/** /**
@@ -244,7 +263,7 @@ public class AppConfigUtils {
*/ */
public int getCurrentBatteryValue() { public int getCurrentBatteryValue() {
int value = mAppConfigBean.getCurrentBatteryValue(); int value = mAppConfigBean.getCurrentBatteryValue();
LogUtils.d(TAG, "getCurrentBatteryValue() 获取电池电量" + value + "%"); LogUtils.d(TAG, String.format("getCurrentBatteryValue获取电池电量=%d%%", value));
return value; return value;
} }
@@ -254,15 +273,17 @@ public class AppConfigUtils {
* @param interval 目标间隔单位ms * @param interval 目标间隔单位ms
*/ */
public void setReminderIntervalTime(final int interval) { public void setReminderIntervalTime(final int interval) {
LogUtils.d(TAG, "setReminderIntervalTime() 调用,传入间隔:" + interval + "ms"); LogUtils.d(TAG, String.format("setReminderIntervalTime调用 | 传入间隔=%dms", interval));
final int calibratedInterval = Math.max(interval, MIN_INTERVAL_TIME); final int calibratedInterval = Math.max(interval, MIN_INTERVAL_TIME);
if (calibratedInterval == mAppConfigBean.getReminderIntervalTime()) { if (calibratedInterval == mAppConfigBean.getReminderIntervalTime()) {
LogUtils.d(TAG, "setReminderIntervalTime() 提醒间隔无变化,无需操作"); LogUtils.d(TAG, "setReminderIntervalTime提醒间隔无变化,无需操作");
return; return;
} }
mAppConfigBean.setReminderIntervalTime(calibratedInterval); mAppConfigBean.setReminderIntervalTime(calibratedInterval);
saveAppConfig(); saveAppConfig();
LogUtils.d(TAG, "setReminderIntervalTime() 提醒间隔更新为:" + calibratedInterval + "ms"); LogUtils.d(TAG, String.format("setReminderIntervalTime成功 | 提醒间隔=%dms", calibratedInterval));
} }
/** /**
@@ -271,7 +292,7 @@ public class AppConfigUtils {
*/ */
public int getReminderIntervalTime() { public int getReminderIntervalTime() {
int interval = mAppConfigBean.getReminderIntervalTime(); int interval = mAppConfigBean.getReminderIntervalTime();
LogUtils.d(TAG, "getReminderIntervalTime() 获取提醒间隔" + interval + "ms"); LogUtils.d(TAG, String.format("getReminderIntervalTime获取提醒间隔=%dms", interval));
return interval; return interval;
} }
@@ -280,15 +301,17 @@ public class AppConfigUtils {
* @param interval 目标间隔单位ms * @param interval 目标间隔单位ms
*/ */
public void setBatteryDetectInterval(final int interval) { public void setBatteryDetectInterval(final int interval) {
LogUtils.d(TAG, "setBatteryDetectInterval() 调用,传入间隔:" + interval + "ms"); LogUtils.d(TAG, String.format("setBatteryDetectInterval调用 | 传入间隔=%dms", interval));
final int calibratedInterval = Math.max(interval, MIN_DETECT_INTERVAL); final int calibratedInterval = Math.max(interval, MIN_DETECT_INTERVAL);
if (calibratedInterval == mAppConfigBean.getBatteryDetectInterval()) { if (calibratedInterval == mAppConfigBean.getBatteryDetectInterval()) {
LogUtils.d(TAG, "setBatteryDetectInterval() 检测间隔无变化,无需操作"); LogUtils.d(TAG, "setBatteryDetectInterval检测间隔无变化,无需操作");
return; return;
} }
mAppConfigBean.setBatteryDetectInterval(calibratedInterval); mAppConfigBean.setBatteryDetectInterval(calibratedInterval);
saveAppConfig(); saveAppConfig();
LogUtils.d(TAG, "setBatteryDetectInterval() 电量检测间隔更新为:" + calibratedInterval + "ms"); LogUtils.d(TAG, String.format("setBatteryDetectInterval成功 | 电量检测间隔=%dms", calibratedInterval));
} }
/** /**
@@ -297,7 +320,7 @@ public class AppConfigUtils {
*/ */
public int getBatteryDetectInterval() { public int getBatteryDetectInterval() {
int interval = mAppConfigBean.getBatteryDetectInterval(); int interval = mAppConfigBean.getBatteryDetectInterval();
LogUtils.d(TAG, "getBatteryDetectInterval() 获取电量检测间隔" + interval + "ms"); LogUtils.d(TAG, String.format("getBatteryDetectInterval获取电量检测间隔=%dms", interval));
return interval; return interval;
} }
@@ -307,15 +330,16 @@ public class AppConfigUtils {
* @return 服务开关状态true=开启false=关闭) * @return 服务开关状态true=开启false=关闭)
*/ */
public boolean isServiceEnabled() { public boolean isServiceEnabled() {
LogUtils.d(TAG, "isServiceEnabled() 开始获取服务开关状态"); LogUtils.d(TAG, "isServiceEnabled调用:开始获取服务开关状态");
ControlCenterServiceBean savedServiceBean = (ControlCenterServiceBean) ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class); ControlCenterServiceBean savedServiceBean = (ControlCenterServiceBean) ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class);
if (savedServiceBean != null) { if (savedServiceBean != null) {
boolean isEnabled = savedServiceBean.isEnableService(); boolean isEnabled = savedServiceBean.isEnableService();
LogUtils.d(TAG, "isServiceEnabled() 服务开关状态" + isEnabled); LogUtils.d(TAG, String.format("isServiceEnabled服务开关状态=%b", isEnabled));
return isEnabled; return isEnabled;
} else { } else {
ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(false)); ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(false));
LogUtils.d(TAG, "isServiceEnabled() 无已保存服务配置,默认关闭并持久化"); LogUtils.d(TAG, "isServiceEnabled无已保存服务配置,默认关闭并持久化");
return false; return false;
} }
} }
@@ -325,9 +349,9 @@ public class AppConfigUtils {
* @param isServiceEnabled 目标状态true=开启false=关闭) * @param isServiceEnabled 目标状态true=开启false=关闭)
*/ */
public void setIsServiceEnabled(boolean isServiceEnabled) { public void setIsServiceEnabled(boolean isServiceEnabled) {
LogUtils.d(TAG, "setIsServiceEnabled() 调用,传入状态" + isServiceEnabled); LogUtils.d(TAG, String.format("setIsServiceEnabled调用 | 传入状态=%b", isServiceEnabled));
ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(isServiceEnabled)); ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(isServiceEnabled));
LogUtils.d(TAG, "setIsServiceEnabled() 服务开关状态更新为:" + isServiceEnabled); LogUtils.d(TAG, String.format("setIsServiceEnabled成功 | 服务开关状态=%b", isServiceEnabled));
} }
} }