diff --git a/powerbell/build.properties b/powerbell/build.properties index e90f1a0..30b7787 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #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 libraryProject= baseVersion=15.14 publishVersion=15.14.23 -buildCount=7 +buildCount=8 baseBetaVersion=15.14.24 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java index 0353ed7..1cbd448 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java @@ -3,12 +3,11 @@ package cc.winboll.studio.powerbell.models; import cc.winboll.studio.libappbase.LogUtils; /** - * 电池报告数据模型 - * 适配 API30,存储当前电量、放电时间、充电时间核心数据 - * 支持参数校验与调试日志输出 * @Author ZhanGSKen * @Date 2025/03/22 14:30:51 * @Describe 电池报告数据模型 + * 适配 API30,存储当前电量、放电时间、充电时间核心数据 + * 支持参数校验与调试日志输出 */ public class BatteryData { // ====================== 静态常量(首屏可见,统一管理) ====================== diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java index 4a211d5..0c2b17c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java @@ -8,11 +8,10 @@ import java.io.IOException; import java.io.Serializable; /** - * 电池信息数据模型 - * 适配 API30,存储电量时间戳与电量值,支持 JSON 序列化/反序列化 - * 修复字段拼写错误,补充数据校验与调试日志 * @Author ZhanGSKen * @Describe 电池信息数据模型 + * 适配 API30,存储电量时间戳与电量值,支持 JSON 序列化/反序列化 + * 修复字段拼写错误,补充数据校验与调试日志 */ public class BatteryInfoBean extends BaseBean implements Serializable { // ====================== 静态常量(首屏可见,统一管理) ====================== diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java index e24b8c5..17ec414 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/ControlCenterServiceBean.java @@ -4,48 +4,44 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.JsonReader; import android.util.JsonWriter; - import java.io.IOException; import java.io.Serializable; - import cc.winboll.studio.libappbase.BaseBean; import cc.winboll.studio.libappbase.LogUtils; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/12/17 15:55 - * @Describe 服务控制参数模型:管理服务启用状态,支持序列化、Parcel传递、JSON解析 + * @Describe 服务控制参数模型 + * 适配 API30,管理服务启用状态,支持 Serializable 持久化、Parcelable 组件传递、JSON 序列化解析 */ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Serializable { - // ================================== 静态常量(置顶统一管理,避免魔法值)================================= + // ====================== 静态常量(置顶统一管理,避免魔法值) ====================== private static final long serialVersionUID = 1L; // Serializable 必备,保障反序列化兼容 private static final String TAG = "ControlCenterServiceBean"; - // JSON 字段常量,避免硬编码,减少拼写错误 - private static final String JSON_FIELD_IS_ENABLE_SERVICE = "isEnableService"; + private static final String JSON_FIELD_IS_ENABLE_SERVICE = "isEnableService"; // JSON 字段常量,避免硬编码 - // ================================== 核心成员变量(私有封装,规范命名)================================= + // ====================== 核心成员变量(私有封装,规范命名) ====================== private boolean isEnableService = false; // 服务启用状态:true=启用,false=禁用 - // ================================== Parcelable 静态创建器(必须 public static final,适配 API30 传递)================================= + // ====================== Parcelable 静态创建器(必须 public static final,适配 API30 组件传递) ====================== public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public ControlCenterServiceBean createFromParcel(Parcel source) { - LogUtils.d(TAG, "Parcelable createFromParcel: 从Parcel反序列化对象"); - // Java7 + API30 适配:Parcel 无直接 writeBoolean,用 byte 存储/读取 boolean isEnable = source.readByte() != 0; ControlCenterServiceBean bean = new ControlCenterServiceBean(isEnable); - LogUtils.d(TAG, "Parcelable createFromParcel: 反序列化完成,isEnableService=" + isEnable); + LogUtils.d(TAG, String.format("createFromParcel: 反序列化完成,isEnableService=%b", isEnable)); return bean; } @Override public ControlCenterServiceBean[] newArray(int size) { - LogUtils.d(TAG, "Parcelable newArray: 创建数组,长度=" + size); + LogUtils.d(TAG, String.format("newArray: 创建数组,长度=%d", size)); return new ControlCenterServiceBean[size]; } }; - // ================================== 构造方法(无参+有参,满足不同初始化场景)================================= + // ====================== 构造方法(无参+有参,满足不同初始化场景) ====================== /** * 无参构造(JSON解析、反射创建必备) */ @@ -60,25 +56,25 @@ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Se */ public ControlCenterServiceBean(boolean isEnableService) { this.isEnableService = isEnableService; - LogUtils.d(TAG, "有参构造:初始化服务状态,isEnableService=" + isEnableService); + LogUtils.d(TAG, String.format("有参构造:初始化服务状态,isEnableService=%b", isEnableService)); } - // ================================== Getter/Setter 方法(封装成员变量,控制访问)================================= + // ====================== Getter/Setter 方法(封装成员变量,控制访问) ====================== public boolean isEnableService() { - LogUtils.d(TAG, "get isEnableService: 当前状态=" + isEnableService); + LogUtils.d(TAG, String.format("isEnableService: 当前状态=%b", isEnableService)); return 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; } - // ================================== 父类 BaseBean 方法重写(核心业务逻辑)================================= + // ====================== 父类 BaseBean 方法重写(核心业务逻辑:JSON 序列化/反序列化) ====================== @Override public String getName() { String className = ControlCenterServiceBean.class.getName(); - LogUtils.d(TAG, "getName: 返回类名=" + className); + LogUtils.d(TAG, String.format("getName: 返回类名=%s", className)); return className; } @@ -87,11 +83,9 @@ public class ControlCenterServiceBean extends BaseBean implements Parcelable, Se */ @Override public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { - LogUtils.d(TAG, "writeThisToJsonWriter: 开始将对象序列化到JSON"); super.writeThisToJsonWriter(jsonWriter); - // 写入服务启用状态字段 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 public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { - LogUtils.d(TAG, "readBeanFromJsonReader: 开始从JSON反序列化对象"); ControlCenterServiceBean bean = new ControlCenterServiceBean(); jsonReader.beginObject(); while (jsonReader.hasNext()) { String fieldName = jsonReader.nextName(); if (JSON_FIELD_IS_ENABLE_SERVICE.equals(fieldName)) { - // 读取并设置服务启用状态 boolean isEnable = jsonReader.nextBoolean(); bean.setIsEnableService(isEnable); - LogUtils.d(TAG, "readBeanFromJsonReader: 读取JSON字段," + fieldName + "=" + isEnable); + LogUtils.d(TAG, String.format("readBeanFromJsonReader: 读取字段,%s=%b", fieldName, isEnable)); } else { - // 跳过未知字段,避免解析异常 jsonReader.skipValue(); - LogUtils.w(TAG, "readBeanFromJsonReader: 跳过未知JSON字段=" + fieldName); + LogUtils.w(TAG, String.format("readBeanFromJsonReader: 跳过未知字段=%s", fieldName)); } } jsonReader.endObject(); - LogUtils.d(TAG, "readBeanFromJsonReader: JSON反序列化完成"); + LogUtils.d(TAG, "readBeanFromJsonReader: 反序列化完成"); return bean; } - // ================================== Parcelable 接口方法实现(适配 Intent 组件间传递)================================= + // ====================== Parcelable 接口方法实现(适配 Intent 组件间传递,Java7 适配) ====================== @Override public int describeContents() { - // 无特殊内容(如文件描述符),返回0即可(API30 标准实现) LogUtils.d(TAG, "describeContents: 返回内容描述符=0"); - return 0; + return 0; // 无特殊内容(如文件描述符),返回0即可(API30 标准实现) } /** - * 序列化对象到 Parcel(Intent 传递必备,Java7 适配) + * 序列化对象到 Parcel(Intent 传递必备,Java7 适配:用 byte 存储 boolean) */ @Override public void writeToParcel(Parcel dest, int flags) { - LogUtils.d(TAG, "writeToParcel: 开始将对象序列化到Parcel,flags=" + flags); - // Java7 + API30 适配:Parcel 无 writeBoolean 方法,用 byte 存储(1=true,0=false) - dest.writeByte((byte) (this.isEnableService ? 1 : 0)); - LogUtils.d(TAG, "writeToParcel: Parcel序列化完成,isEnableService=" + this.isEnableService + "(存储为byte=" + (this.isEnableService ? 1 : 0) + ")"); + byte flag = (byte) (this.isEnableService ? 1 : 0); + dest.writeByte(flag); + LogUtils.d(TAG, String.format("writeToParcel: 序列化完成,isEnableService=%b(存储为byte=%d)", this.isEnableService, flag)); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/NotificationMessage.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/NotificationMessage.java index b1d46d4..fcd8436 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/NotificationMessage.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/NotificationMessage.java @@ -1,36 +1,75 @@ package cc.winboll.studio.powerbell.models; +import cc.winboll.studio.libappbase.LogUtils; + /** - * 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递 + * 通知数据模型 + * 适配 API30,统一存储通知标题、内容、标识信息,支持各组件数据传递 + * @Author ZhanGSKen + * @Describe 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递 */ public class NotificationMessage { + // ====================== 静态常量(统一管理) ====================== + private static final String TAG = "NotificationMessage"; + private static final String EMPTY_STRING = ""; + + // ====================== 核心成员变量(按业务逻辑排序) ====================== private String title; // 通知标题 private String content; // 通知内容 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) { - 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() { return content; } - public void setContent(String content) { - this.content = content; - } - public String getRemindMSG() { return remindMSG; } - - public void setRemindMSG(String remindMSG) { - this.remindMSG = remindMSG; - } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/ControlCenterServiceReceiver.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/ControlCenterServiceReceiver.java index 8ecb73d..78d3521 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/ControlCenterServiceReceiver.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/ControlCenterServiceReceiver.java @@ -14,14 +14,14 @@ import cc.winboll.studio.powerbell.utils.NotificationManagerUtils; import java.lang.ref.WeakReference; /** - * 控制中心广播接收器 - * 功能:监听电池状态变化、前台通知更新、配置变更指令 - * 适配:Java7 | API30 | 内存泄漏防护 | 多线程状态同步 * @Author ZhanGSKen&豆包大模型 * @Date 2025/12/19 20:23 + * @Describe 控制中心广播接收器 + * 功能:监听电池状态变化、前台通知更新、配置变更指令 + * 适配:Java7 | API30 | 内存泄漏防护 | 多线程状态同步 */ public class ControlCenterServiceReceiver extends BroadcastReceiver { - // ================================== 静态常量区(置顶归类,消除魔法值)================================= + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== public static final String TAG = "ControlCenterServiceReceiver"; // 广播Action常量(带包名前缀防冲突) @@ -34,27 +34,28 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { private static final int BATTERY_LEVEL_MIN = 0; private static final int BATTERY_LEVEL_MAX = 100; - // ================================== 静态状态标记(volatile保证多线程可见性)================================= + // ====================== 静态状态标记(volatile保证多线程可见性) ====================== private static volatile int sLastBatteryLevel = -1; // 上次电量(多线程可见) private static volatile boolean sIsCharging = false; // 上次充电状态(多线程可见) - // ================================== 成员变量区(弱引用防泄漏,按功能分层)================================= + // ====================== 成员变量区(弱引用防泄漏,按功能分层) ====================== private WeakReference mwrControlCenterService; - private boolean isRegistered = false; // 新增:标记广播注册状态,避免冗余操作 + private boolean isRegistered = false; // 标记广播注册状态,避免冗余操作 - // ================================== 构造方法(初始化弱引用,避免服务强引用泄漏)================================= + // ====================== 构造方法(初始化弱引用,避免服务强引用泄漏) ====================== 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); } - // ================================== 广播核心接收逻辑(入口方法,分Action分发处理)================================= + // ====================== 广播核心接收逻辑(入口方法,分Action分发处理) ====================== @Override 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 + "),终止处理"); return; } @@ -62,13 +63,12 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { // 弱引用获取服务,双重校验服务有效性 ControlCenterService service = mwrControlCenterService != null ? mwrControlCenterService.get() : null; if (service == null || service.isDestroyed()) { - LogUtils.e(TAG, "onReceive: 服务已销毁或为空(service=" + service + "),注销广播"); + LogUtils.e(TAG, "onReceive: 服务已销毁或为空,注销广播"); unregisterAction(context); return; } // 分Action处理业务逻辑 - String action = intent.getAction(); switch (action) { case Intent.ACTION_BATTERY_CHANGED: handleBatteryStateChanged(service, intent); @@ -77,30 +77,30 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { handleUpdateForegroundNotification(service); break; case ACTION_APPCONFIG_CHANGED: - LogUtils.d(TAG, "onReceive: 开始处理配置更新广播"); // 新增:标记配置广播处理起点 + LogUtils.d(TAG, "onReceive: 开始处理配置更新广播"); handleNotifyAppConfigUpdate(service); break; default: - LogUtils.w(TAG, "onReceive: 未知Action=" + action); + LogUtils.w(TAG, String.format("onReceive: 未知Action=%s", action)); } LogUtils.d(TAG, "onReceive: 广播处理完成"); } - // ================================== 业务处理方法(按功能拆分,强化容错与日志)================================= + // ====================== 业务处理方法(按功能拆分,强化容错与日志) ====================== /** * 处理电池状态变化广播 * @param service 控制中心服务实例 * @param intent 电池状态广播意图 */ private void handleBatteryStateChanged(ControlCenterService service, Intent intent) { - LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态 | service=" + service + " | intent=" + intent); + LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态"); try { // 1. 解析并校验当前电池状态 boolean currentCharging = BatteryUtils.isCharging(intent); int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent); 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. 状态无变化则跳过,减少无效运算 if (currentCharging == sIsCharging && currentBatteryLevel == sLastBatteryLevel) { @@ -108,13 +108,14 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { return; } - // 4. 更新静态缓存状态,保证多线程可见 + // 3. 更新静态缓存状态,保证多线程可见 sIsCharging = currentCharging; 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) { LogUtils.e(TAG, "handleBatteryStateChanged: 处理失败", e); } @@ -125,23 +126,24 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { * @param service 控制中心服务实例 */ private void handleNotifyAppConfigUpdate(ControlCenterService service) { - LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 同步缓存状态到配置 | service=" + service); + LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 同步缓存状态到配置"); try { // 加载最新配置 AppConfigBean latestConfig = AppConfigUtils.getInstance(service).loadAppConfig(); - if (latestConfig == null) { // 新增:配置空指针防护 + if (latestConfig == null) { LogUtils.e(TAG, "handleNotifyAppConfigUpdate: 最新配置为空,终止处理"); 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.setIsCharging(sIsCharging); service.notifyAppConfigUpdate(latestConfig); - LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 配置同步成功 | 缓存电量=" + sLastBatteryLevel + "% | 充电状态=" + sIsCharging); - LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 配置更新广播处理完成"); // 新增:标记配置广播处理终点 + LogUtils.d(TAG, String.format("handleNotifyAppConfigUpdate: 配置同步成功 | 缓存电量=%d%% | 充电状态=%b", sLastBatteryLevel, sIsCharging)); + LogUtils.d(TAG, "handleNotifyAppConfigUpdate: 配置更新广播处理完成"); } catch (Exception e) { LogUtils.e(TAG, "handleNotifyAppConfigUpdate: 处理失败", e); } @@ -152,32 +154,32 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { * @param service 控制中心服务实例 */ private void handleUpdateForegroundNotification(ControlCenterService service) { - LogUtils.d(TAG, "handleUpdateForegroundNotification: 更新前台通知 | service=" + service); + LogUtils.d(TAG, "handleUpdateForegroundNotification: 更新前台通知"); try { NotificationManagerUtils notifyUtils = service.getNotificationManager(); NotificationMessage notifyMsg = service.getForegroundNotifyMsg(); // 非空校验,避免空指针 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; } notifyUtils.updateForegroundServiceNotify(notifyMsg); - LogUtils.d(TAG, "handleUpdateForegroundNotification: 前台通知更新成功 | 通知标题=" + notifyMsg.getTitle()); + LogUtils.d(TAG, String.format("handleUpdateForegroundNotification: 前台通知更新成功 | 标题=%s", notifyMsg.getTitle())); } catch (Exception e) { LogUtils.e(TAG, "handleUpdateForegroundNotification: 处理失败", e); } } - // ================================== 广播注册/注销(强化容错,避免重复操作)================================= + // ====================== 广播注册/注销(强化容错,避免重复操作) ====================== /** * 注册广播接收器 * @param context 上下文 */ public void registerAction(Context context) { - LogUtils.d(TAG, "registerAction: 注册广播接收器 | context=" + context); - if (context == null || isRegistered) { // 新增:已注册则跳过 + LogUtils.d(TAG, "registerAction: 注册广播接收器"); + if (context == null || isRegistered) { LogUtils.e(TAG, "registerAction: 上下文为空或已注册,注册失败"); return; } @@ -190,8 +192,8 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { filter.setPriority(BROADCAST_PRIORITY); context.registerReceiver(this, filter); - isRegistered = true; // 标记为已注册 - LogUtils.d(TAG, "registerAction: 广播注册成功 | 优先级=" + BROADCAST_PRIORITY); + isRegistered = true; + LogUtils.d(TAG, String.format("registerAction: 广播注册成功 | 优先级=%d", BROADCAST_PRIORITY)); } catch (Exception e) { LogUtils.e(TAG, "registerAction: 注册失败", e); } @@ -202,15 +204,15 @@ public class ControlCenterServiceReceiver extends BroadcastReceiver { * @param context 上下文 */ public void unregisterAction(Context context) { - LogUtils.d(TAG, "unregisterAction: 注销广播接收器 | context=" + context); - if (context == null || !isRegistered) { // 新增:未注册则跳过 + LogUtils.d(TAG, "unregisterAction: 注销广播接收器"); + if (context == null || !isRegistered) { LogUtils.e(TAG, "unregisterAction: 上下文为空或未注册,注销失败"); return; } try { context.unregisterReceiver(this); - isRegistered = false; // 标记为未注册 + isRegistered = false; LogUtils.d(TAG, "unregisterAction: 广播注销成功"); } catch (IllegalArgumentException e) { 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; } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/GlobalApplicationReceiver.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/GlobalApplicationReceiver.java index 761edef..7197476 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/GlobalApplicationReceiver.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/GlobalApplicationReceiver.java @@ -18,110 +18,113 @@ import cc.winboll.studio.powerbell.utils.BatteryUtils; * 适配:Java7 | API30 | 内存泄漏防护 */ public class GlobalApplicationReceiver extends BroadcastReceiver { - // ================================== 静态常量区(置顶归类,消除魔法值)================================= + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== public static final String TAG = "GlobalApplicationReceiver"; private static final int BATTERY_LEVEL_MIN = 0; private static final int BATTERY_LEVEL_MAX = 100; - // ================================== 静态成员变量(线程安全,volatile保证多线程可见性)================================= - private static volatile int sLastBatteryLevel = -1; // 历史电量(0-100) + // ====================== 静态状态标记(volatile保证多线程可见性) ====================== + private static volatile int sLastBatteryLevel = -1; // 历史电量(0-100) private static volatile boolean sLastIsCharging = false; // 历史充电状态 - // ================================== 成员变量区(按功能分层)================================= + // ====================== 成员变量区(按功能分层,移除冗余的mCurrentReceiver) ====================== private App mGlobalApplication; private AppConfigUtils mAppConfigUtils; - private GlobalApplicationReceiver mCurrentReceiver; - // ================================== 构造方法(强化参数校验,初始化核心依赖)================================= + // ====================== 构造方法(强化参数校验,初始化核心依赖) ====================== public GlobalApplicationReceiver(App globalApplication) { - LogUtils.d(TAG, "构造接收器 | App=" + globalApplication); + LogUtils.d(TAG, String.format("构造接收器 | App实例:%s", globalApplication)); if (globalApplication == null) { LogUtils.e(TAG, "构造失败:App实例为空"); throw new IllegalArgumentException("App cannot be null"); } - this.mCurrentReceiver = this; this.mGlobalApplication = globalApplication; this.mAppConfigUtils = App.getAppConfigUtils(mGlobalApplication); - LogUtils.d(TAG, "构造完成:AppConfigUtils=" + mAppConfigUtils); + LogUtils.d(TAG, String.format("构造完成 | AppConfigUtils:%s", mAppConfigUtils)); } - // ================================== 广播核心接收逻辑(入口方法,过滤电池状态广播)================================= + // ====================== 广播核心接收逻辑(入口方法,过滤电池状态广播) ====================== @Override 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: 参数无效,终止处理"); return; } // 仅处理电池状态变化广播 - if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { handleBatteryStateChanged(context, intent); } LogUtils.d(TAG, "onReceive: 广播处理完成"); } - // ================================== 业务逻辑方法(处理电池状态变化,同步配置+通知页面)================================= + // ====================== 业务逻辑方法(处理电池状态变化,同步配置+通知页面) ====================== /** * 处理电池状态变化广播 * @param context 上下文 * @param intent 电池状态广播意图 */ private void handleBatteryStateChanged(Context context, Intent intent) { - LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态 | intent=" + intent); - // 1. 解析当前电池状态(复用工具类,二次校验电量范围) - boolean currentIsCharging = BatteryUtils.isCharging(intent); - int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent); - currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX); - LogUtils.d(TAG, "handleBatteryStateChanged: 当前状态 | 充电=" + currentIsCharging + " | 电量=" + currentBatteryLevel + "%"); + LogUtils.d(TAG, "handleBatteryStateChanged: 解析电池状态"); + try { + // 1. 解析当前电池状态(复用工具类,二次校验电量范围) + boolean currentIsCharging = BatteryUtils.isCharging(intent); + int currentBatteryLevel = BatteryUtils.getCurrentBatteryLevel(intent); + currentBatteryLevel = Math.min(Math.max(currentBatteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX); + LogUtils.d(TAG, String.format("handleBatteryStateChanged: 当前状态 | 充电=%b | 电量=%d%%", currentIsCharging, currentBatteryLevel)); - // 2. 状态无变化则跳过,减少无效运算 - if (currentIsCharging == sLastIsCharging && currentBatteryLevel == sLastBatteryLevel) { - LogUtils.d(TAG, "handleBatteryStateChanged: 状态无变化,跳过处理"); - return; - } - - // 3. 同步最新状态到配置工具类 - if (mAppConfigUtils != null) { - if (currentIsCharging != sLastIsCharging) { - mAppConfigUtils.setCharging(currentIsCharging); - LogUtils.d(TAG, "handleBatteryStateChanged: 同步充电状态 | " + currentIsCharging); + // 2. 状态无变化则跳过,减少无效运算 + if (currentIsCharging == sLastIsCharging && currentBatteryLevel == sLastBatteryLevel) { + LogUtils.d(TAG, "handleBatteryStateChanged: 状态无变化,跳过处理"); + return; } - if (currentBatteryLevel != sLastBatteryLevel) { - mAppConfigUtils.setCurrentBatteryValue(currentBatteryLevel); - LogUtils.d(TAG, "handleBatteryStateChanged: 同步电量 | " + currentBatteryLevel + "%"); + + // 3. 同步最新状态到配置工具类 + 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. 执行状态变化后的业务逻辑 - // 记录电量变化时间 - if (App.getAppCacheUtils(context) != null) { - App.getAppCacheUtils(context).addChangingTime(currentBatteryLevel); - LogUtils.d(TAG, "handleBatteryStateChanged: 记录电量变化时间"); - } - // 通知MainActivity更新电量 - MainActivity.sendCurrentBatteryValueMessage(currentBatteryLevel); - LogUtils.d(TAG, "handleBatteryStateChanged: 发送电量更新消息到MainActivity"); + // 4. 执行状态变化后的业务逻辑 + // 记录电量变化时间 + if (App.getAppCacheUtils(context) != null) { + App.getAppCacheUtils(context).addChangingTime(currentBatteryLevel); + LogUtils.d(TAG, "handleBatteryStateChanged: 记录电量变化时间"); + } + // 通知MainActivity更新电量 + MainActivity.sendCurrentBatteryValueMessage(currentBatteryLevel); + LogUtils.d(TAG, String.format("handleBatteryStateChanged: 发送电量更新消息到MainActivity | %d%%", currentBatteryLevel)); - // 5. 更新历史状态缓存 - sLastIsCharging = currentIsCharging; - sLastBatteryLevel = currentBatteryLevel; - LogUtils.d(TAG, "handleBatteryStateChanged: 更新历史状态完成"); + // 5. 更新历史状态缓存 + sLastIsCharging = currentIsCharging; + sLastBatteryLevel = currentBatteryLevel; + LogUtils.d(TAG, "handleBatteryStateChanged: 更新历史状态完成"); + } catch (Exception e) { + LogUtils.e(TAG, "handleBatteryStateChanged: 处理失败", e); + } } - // ================================== 广播注册/注销(强化容错,避免重复操作)================================= + // ====================== 广播注册/注销(强化容错,避免重复操作) ====================== /** * 注册广播接收器 */ public void registerAction() { LogUtils.d(TAG, "registerAction: 注册广播"); - if (mGlobalApplication == null || mCurrentReceiver == null) { - LogUtils.e(TAG, "注册失败:App或Receiver实例为空"); + if (mGlobalApplication == null) { + LogUtils.e(TAG, "注册失败:App实例为空"); return; } @@ -130,7 +133,7 @@ public class GlobalApplicationReceiver extends BroadcastReceiver { unregisterAction(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); - mGlobalApplication.registerReceiver(mCurrentReceiver, filter); + mGlobalApplication.registerReceiver(this, filter); LogUtils.d(TAG, "registerAction: 广播注册成功"); } catch (Exception e) { LogUtils.e(TAG, "registerAction: 注册失败", e); @@ -142,13 +145,13 @@ public class GlobalApplicationReceiver extends BroadcastReceiver { */ public void unregisterAction() { LogUtils.d(TAG, "unregisterAction: 注销广播"); - if (mGlobalApplication == null || mCurrentReceiver == null) { - LogUtils.e(TAG, "注销失败:App或Receiver实例为空"); + if (mGlobalApplication == null) { + LogUtils.e(TAG, "注销失败:App实例为空"); return; } try { - mGlobalApplication.unregisterReceiver(mCurrentReceiver); + mGlobalApplication.unregisterReceiver(this); LogUtils.d(TAG, "unregisterAction: 广播注销成功"); } catch (IllegalArgumentException e) { LogUtils.w(TAG, "unregisterAction: 广播未注册,跳过注销"); @@ -157,7 +160,7 @@ public class GlobalApplicationReceiver extends BroadcastReceiver { } } - // ================================== 资源释放方法(主动释放,彻底避免内存泄漏)================================= + // ====================== 资源释放方法(主动释放,彻底避免内存泄漏) ====================== /** * 释放接收器资源,供App销毁时调用 */ @@ -168,7 +171,6 @@ public class GlobalApplicationReceiver extends BroadcastReceiver { // 置空引用,帮助GC回收 mGlobalApplication = null; mAppConfigUtils = null; - mCurrentReceiver = null; // 重置静态状态缓存 sLastBatteryLevel = -1; sLastIsCharging = false; diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/MainReceiver.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/MainReceiver.java index 3523292..968c979 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/MainReceiver.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/receivers/MainReceiver.java @@ -1,10 +1,5 @@ package cc.winboll.studio.powerbell.receivers; -/** - * @Author ZhanGSKen - * @Date 2024/06/06 15:01:39 - * @Describe 应用广播消息接收类 - */ import android.content.BroadcastReceiver; import android.content.Context; 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.utils.ServiceUtils; +/** + * @Author ZhanGSKen + * @Date 2024/06/06 15:01:39 + * @Describe 应用核心广播接收器 + * 功能:监听开机完成广播,实现服务开机自启 + * 适配:Java7 | API30 | 服务启动兼容性处理 + */ public class MainReceiver extends BroadcastReceiver { - + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== 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"; - // 存储电量指示值, - // 用于校验电量消息时的电量变化 - static volatile int _mnTheQuantityOfElectricityOld = -1; + // ====================== 静态状态标记(volatile保证多线程可见性) ====================== + // 历史电量值,用于校验电量变化(暂未使用,保留扩展能力) + private static volatile int sLastBatteryLevel = -1; + // ====================== 广播核心接收逻辑(入口方法,分Action处理) ====================== @Override public void onReceive(Context context, Intent intent) { - String szAction = intent.getAction(); - if (szAction.equals(ACTION_BOOT_COMPLETED)) { - boolean isEnableService = App.getAppConfigUtils(context).isServiceEnabled(); - if (isEnableService) { - if (ServiceUtils.isServiceAlive(context.getApplicationContext(), ControlCenterService.class.getName()) == false) { - LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService"); - if (Build.VERSION.SDK_INT >= 26) { - context.startForegroundService(new Intent(context, ControlCenterService.class)); - } else { - context.startService(new Intent(context, ControlCenterService.class)); - } - } + // 基础参数校验 + if (context == null || intent == null) { + LogUtils.e(TAG, "onReceive: 上下文或意图为空,终止处理"); + return; + } + + String action = intent.getAction(); + LogUtils.d(TAG, String.format("onReceive: 接收广播 | Action:%s", action)); + + // 仅处理开机完成广播 + 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); } } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/AssistantService.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/AssistantService.java index 194a096..9c90f2f 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/AssistantService.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/AssistantService.java @@ -16,21 +16,25 @@ import cc.winboll.studio.powerbell.utils.ServiceUtils; * 电池提醒核心服务进程守护类 * 功能:监听主服务 {@link ControlCenterService} 存活状态,异常断开时自动重启并绑定 * 适配:Java7 | API30 | 前台服务启动规则 | 服务绑定稳定性保障 + * @Author ZhanGSKen + * @Describe 守护服务:保障ControlCenterService持续运行 */ public class AssistantService extends Service { - // ================================== 静态常量区(置顶归类,消除魔法值)================================= + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== private static final String TAG = "AssistantService"; - // 服务返回策略常量(统一定义,避免魔法值) + // 服务返回策略常量 private static final int SERVICE_RETURN_STICKY = START_STICKY; // 服务绑定标记常量 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 MyServiceConnection mMyServiceConnection; private volatile boolean mIsThreadAlive; - // ================================== 内部类(服务连接状态监听,前置定义便于引用)================================= + // ====================== 内部类(服务连接状态监听,前置定义便于引用) ====================== /** * 服务连接状态监听器 * 主服务连接成功时记录状态,断开时自动重连 @@ -38,12 +42,14 @@ public class AssistantService extends Service { private class MyServiceConnection implements ServiceConnection { @Override 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 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()) { LogUtils.d(TAG, "onServiceDisconnected: 配置启用,尝试重新唤醒并绑定主服务"); @@ -52,11 +58,11 @@ public class AssistantService extends Service { } } - // ================================== 服务生命周期方法(按执行顺序排列:onCreate→onStartCommand→onBind→onDestroy)================================= + // ====================== 服务生命周期方法(按执行顺序排列:onCreate→onStartCommand→onBind→onDestroy) ====================== @Override public void 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); @@ -75,12 +81,12 @@ public class AssistantService extends Service { // 初始化运行状态,执行核心守护逻辑 mIsThreadAlive = false; run(); - LogUtils.d(TAG, "onCreate: 守护服务初始化完成 | 服务启用状态=" + mAppConfigUtils.isServiceEnabled()); + LogUtils.d(TAG, String.format("onCreate: 守护服务初始化完成 | 服务启用状态=%b", mAppConfigUtils.isServiceEnabled())); } @Override 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) { LogUtils.e(TAG, "onStartCommand: AppConfigUtils未初始化,终止服务"); @@ -90,13 +96,13 @@ public class AssistantService extends Service { run(); 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; } @Override public IBinder onBind(Intent intent) { - LogUtils.d(TAG, "onBind: 服务绑定请求 | intent=" + intent); + LogUtils.d(TAG, String.format("onBind: 服务绑定请求 | intent=%s", intent)); return null; } @@ -116,14 +122,15 @@ public class AssistantService extends Service { LogUtils.d(TAG, "onDestroy: 守护服务销毁完成"); } - // ================================== 核心业务逻辑(守护主服务存活)================================= + // ====================== 核心业务逻辑(守护主服务存活) ====================== /** * 执行守护逻辑:检查主服务状态,按需唤醒并绑定 * 前置条件:mAppConfigUtils 必须初始化完成 */ private void run() { - LogUtils.d(TAG, "run: 执行守护逻辑 | 配置启用=" + mAppConfigUtils.isServiceEnabled() + " | 线程存活=" + mIsThreadAlive); - if (mAppConfigUtils.isServiceEnabled()) { + boolean isServiceEnabled = mAppConfigUtils.isServiceEnabled(); + LogUtils.d(TAG, String.format("run: 执行守护逻辑 | 配置启用=%b | 线程存活=%b", isServiceEnabled, mIsThreadAlive)); + if (isServiceEnabled) { if (!mIsThreadAlive) { mIsThreadAlive = true; wakeupAndBindMain(); @@ -141,13 +148,14 @@ public class AssistantService extends Service { */ private void wakeupAndBindMain() { // 检查主服务存活状态 - boolean isMainServiceAlive = ServiceUtils.isServiceAlive(getApplicationContext(), ControlCenterService.class.getName()); - LogUtils.d(TAG, "wakeupAndBindMain: 主服务存活状态=" + isMainServiceAlive); + String mainServiceName = ControlCenterService.class.getName(); + boolean isMainServiceAlive = ServiceUtils.isServiceAlive(getApplicationContext(), mainServiceName); + LogUtils.d(TAG, String.format("wakeupAndBindMain: 主服务存活状态=%b", isMainServiceAlive)); // 主服务未存活时,按需启动(区分API版本) if (!isMainServiceAlive) { 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); LogUtils.d(TAG, "wakeupAndBindMain: API26+ 以前台服务方式启动主服务"); } else { @@ -159,10 +167,10 @@ public class AssistantService extends Service { // 绑定主服务,监听连接状态,添加结果日志 Intent bindIntent = new Intent(AssistantService.this, ControlCenterService.class); 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); LogUtils.d(TAG, "unbindMainService: 已成功解绑ControlCenterService"); } catch (IllegalArgumentException e) { - LogUtils.w(TAG, "unbindMainService: 解绑服务失败,服务未绑定 | " + e.getMessage()); + LogUtils.w(TAG, String.format("unbindMainService: 解绑服务失败,服务未绑定 | %s", e.getMessage())); } mMyServiceConnection = null; } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java index 02ecd48..067164c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java @@ -24,22 +24,30 @@ import java.util.List; * 电池提醒核心服务 * 功能:管理前台服务生命周期、控制提醒线程启停、处理配置更新 * 适配:Java7 | API30 | 前台服务超时防护 | 电池优化忽略引导 + * @Author ZhanGSKen + * @Describe 核心服务:实现电池监测、提醒控制与前台服务保活 */ public class ControlCenterService extends Service { - // ================================== 静态常量区(置顶归类,消除魔法值)================================= + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== public static final String TAG = "ControlCenterService"; + // 线程与服务常量 private static final long THREAD_STOP_TIMEOUT = 1000L; 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_USAGE_REMINDER_VALUE = 20; 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 mIsDestroyed = true; - // ================================== 成员变量区(按功能分层:配置→核心组件→通知相关)================================= + // ====================== 成员变量区(按功能分层:配置→核心组件→通知相关) ====================== // 服务控制配置 private ControlCenterServiceBean mServiceControlBean; private AppConfigBean mCurrentConfigBean; @@ -50,31 +58,33 @@ public class ControlCenterService extends Service { private NotificationManagerUtils mNotificationManager; private NotificationMessage mForegroundNotifyMsg; - // ================================== 服务生命周期方法(按执行顺序:onCreate→onStartCommand→onBind→onDestroy)================================= + // ====================== 服务生命周期方法(按执行顺序:onCreate→onStartCommand→onBind→onDestroy) ====================== @Override public void 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(); - 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 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(); runCoreServiceLogic(); int returnFlag = (mServiceControlBean != null && mServiceControlBean.isEnableService()) ? 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; } @Override public IBinder onBind(Intent intent) { - LogUtils.d(TAG, "onBind执行 | intent=" + intent); + LogUtils.d(TAG, String.format("onBind执行 | intent=%s", intent)); return null; } @@ -101,7 +111,7 @@ public class ControlCenterService extends Service { LogUtils.d(TAG, "onDestroy完成:服务销毁完成"); } - // ================================== 核心业务逻辑(独立抽取,统一调用)================================= + // ====================== 核心业务逻辑(独立抽取,统一调用) ====================== /** * 服务核心运行逻辑,在onCreate/onStartCommand复用 * 避免重复初始化,保证前台服务优先启动 @@ -111,7 +121,7 @@ public class ControlCenterService extends Service { loadLatestServiceControlConfig(); 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) { isServiceRunning = true; @@ -131,7 +141,7 @@ public class ControlCenterService extends Service { } } - // ================================== 前台通知管理(优先执行,防止API26+前台服务5秒超时)================================= + // ====================== 前台通知管理(优先执行,防止API26+前台服务5秒超时) ====================== /** * 立即初始化前台通知,防止API26+前台服务超时异常 * @return true=成功 false=失败 @@ -154,7 +164,7 @@ public class ControlCenterService extends Service { mNotificationManager.startForegroundServiceNotify(this, mForegroundNotifyMsg); 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; } catch (Exception 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); if (latestBean != null) { mServiceControlBean = latestBean; - LogUtils.d(TAG, "loadLatestServiceControlConfig:配置读取成功 | 启用=" + mServiceControlBean.isEnableService()); + LogUtils.d(TAG, String.format("loadLatestServiceControlConfig:配置读取成功 | 启用=%b", mServiceControlBean.isEnableService())); } else { LogUtils.w(TAG, "loadLatestServiceControlConfig:本地无配置,沿用内存配置"); } @@ -202,13 +212,14 @@ public class ControlCenterService extends Service { mCurrentConfigBean.setEnableUsageReminder(true); mCurrentConfigBean.setUsageReminderValue(DEFAULT_USAGE_REMINDER_VALUE); 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 { LogUtils.d(TAG, "loadDefaultConfig:内存已有配置,无需加载"); } } - // ================================== 业务组件初始化与销毁(Handler/广播/线程等)================================= + // ====================== 业务组件初始化与销毁(Handler/广播/线程等) ====================== /** * 初始化Handler等核心业务组件 */ @@ -225,7 +236,7 @@ public class ControlCenterService extends Service { if (mControlCenterServiceReceiver == null) { mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this); mControlCenterServiceReceiver.registerAction(this); - LogUtils.d(TAG, "initServiceBusinessLogic:广播接收器初始化并注册完成 | 接收器=" + mControlCenterServiceReceiver); + LogUtils.d(TAG, "initServiceBusinessLogic:广播接收器初始化并注册完成"); } else { LogUtils.d(TAG, "initServiceBusinessLogic:广播接收器已存在"); } @@ -283,13 +294,13 @@ public class ControlCenterService extends Service { LogUtils.d(TAG, "clearAllReferences:引用清理完成"); } - // ================================== 外部调用接口(静态方法,提供服务启停/配置更新入口)================================= + // ====================== 外部调用接口(静态方法,提供服务启停/配置更新入口) ====================== /** * 外部启动服务的统一入口 * @param 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) { LogUtils.e(TAG, "startControlCenterService:Context为空,启动失败"); return; @@ -298,11 +309,11 @@ public class ControlCenterService extends Service { // 保存启用配置 ControlCenterServiceBean controlBean = new ControlCenterServiceBean(true); ControlCenterServiceBean.saveBean(context, controlBean); - LogUtils.d(TAG, "startControlCenterService:服务启用配置已保存 | 配置=" + controlBean); + LogUtils.d(TAG, "startControlCenterService:服务启用配置已保存"); // 启动服务(区分API版本) 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); LogUtils.d(TAG, "startControlCenterService:以前台服务方式启动(API26+)"); } else { @@ -316,7 +327,7 @@ public class ControlCenterService extends Service { * @param 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) { LogUtils.e(TAG, "stopControlCenterService:Context为空,停止失败"); return; @@ -325,7 +336,7 @@ public class ControlCenterService extends Service { // 保存停用配置 ControlCenterServiceBean controlBean = new ControlCenterServiceBean(false); ControlCenterServiceBean.saveBean(context, controlBean); - LogUtils.d(TAG, "stopControlCenterService:服务停用配置已保存 | 配置=" + controlBean); + LogUtils.d(TAG, "stopControlCenterService:服务停用配置已保存"); // 停止服务 Intent intent = new Intent(context, ControlCenterService.class); @@ -337,28 +348,26 @@ public class ControlCenterService extends Service { * 外部更新配置并触发线程重启 * @param context 上下文 */ - public static void sendAppConfigStatusUpdateMessage(Context context) { - LogUtils.d(TAG, "sendAppConfigStatusUpdateMessage执行 | context=" + context); - if (context == null) { - LogUtils.e(TAG, "sendAppConfigStatusUpdateMessage:参数为空,更新失败"); - return; - } + public static void sendAppConfigStatusUpdateMessage(Context context) { + LogUtils.d(TAG, String.format("sendAppConfigStatusUpdateMessage执行 | context=%s", context)); + if (context == null) { + LogUtils.e(TAG, "sendAppConfigStatusUpdateMessage:参数为空,更新失败"); + return; + } - Intent intent = new Intent(ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED); - intent.setPackage(context.getPackageName()); - // 新增:发送广播并记录结果 - context.sendBroadcast(intent); - LogUtils.d(TAG, "sendAppConfigStatusUpdateMessage:配置更新广播发送 :action=" + ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED); - } - + Intent intent = new Intent(ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED); + intent.setPackage(context.getPackageName()); + context.sendBroadcast(intent); + LogUtils.d(TAG, String.format("sendAppConfigStatusUpdateMessage:配置更新广播发送 | action=%s", ControlCenterServiceReceiver.ACTION_APPCONFIG_CHANGED)); + } /** * 检查并引导用户开启忽略电池优化(API23+) * @param context 上下文 */ public static void checkIgnoreBatteryOptimization(Context context) { - LogUtils.d(TAG, "checkIgnoreBatteryOptimization执行 | context=" + context); - if (context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + LogUtils.d(TAG, String.format("checkIgnoreBatteryOptimization执行 | context=%s", context)); + if (context == null || Build.VERSION.SDK_INT < API_LEVEL_23) { LogUtils.w(TAG, "checkIgnoreBatteryOptimization:无需检查(Context为空或API<23)"); return; } @@ -371,14 +380,14 @@ public class ControlCenterService extends Service { String packageName = context.getPackageName(); boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(packageName); - LogUtils.d(TAG, "checkIgnoreBatteryOptimization:已忽略电池优化=" + isIgnored); + LogUtils.d(TAG, String.format("checkIgnoreBatteryOptimization:已忽略电池优化=%b", isIgnored)); if (!isIgnored) { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + packageName)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 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=未运行 */ 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) { LogUtils.e(TAG, "isServiceRunning:参数为空"); return false; @@ -405,7 +414,7 @@ public class ControlCenterService extends Service { String packageName = context.getPackageName(); String serviceClassName = serviceClass.getName(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= API_LEVEL_30) { // API30+ 禁止获取其他应用服务,通过进程状态判断 List processes = am.getRunningAppProcesses(); if (processes != null) { @@ -418,7 +427,7 @@ public class ControlCenterService extends Service { } } } - LogUtils.d(TAG, "isServiceRunning:API30+ 判断结果=" + isRunning); + LogUtils.d(TAG, String.format("isServiceRunning:API30+ 判断结果=%b", isRunning)); } else { // API30- 通过服务列表判断 List services = am.getRunningServices(RUNNING_SERVICE_LIST_LIMIT); @@ -430,13 +439,13 @@ public class ControlCenterService extends Service { } } } - LogUtils.d(TAG, "isServiceRunning:API30- 判断结果=" + isRunning); + LogUtils.d(TAG, String.format("isServiceRunning:API30- 判断结果=%b", isRunning)); } // 兜底判断:配置启用状态 if (!isRunning) { isRunning = isServiceStarted(context, serviceClass); - LogUtils.d(TAG, "isServiceRunning:兜底判断结果=" + isRunning); + LogUtils.d(TAG, String.format("isServiceRunning:兜底判断结果=%b", isRunning)); } return isRunning; } @@ -455,23 +464,25 @@ public class ControlCenterService extends Service { } } - // ================================== 业务方法(配置更新/电池状态回调)================================= + // ====================== 业务方法(配置更新/电池状态回调) ====================== /** * 接收外部配置更新,同步到提醒线程 * @param 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) { mCurrentConfigBean = latestConfig; RemindThread.startRemindThreadWithAppConfig(this, mServiceHandler, latestConfig); LogUtils.d(TAG, "notifyAppConfigUpdate:配置已同步到提醒线程"); } 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() { return mServiceControlBean; } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java index 899baed..c93e3a4 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java @@ -9,14 +9,16 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; /** - * 提醒线程(多实例列表管理) + * 电量通知提醒线程(多实例列表管理) * 功能:管理充电/耗电提醒逻辑,触发条件时向Handler发送提醒消息 * 适配:Java7 | API30 | 内存泄漏防护 | 多线程状态同步 - * 对外接口:{@link #startRemindThreadWithAppConfig(Context, ControlCenterServiceHandler, AppConfigBean)}、{ - * @link #startRemindThreadWithBatteryInfo(Context, ControlCenterServiceHandler, boolean, int)}、{@link #stopRemindThread()} + * 对外接口:{@link #startRemindThreadWithAppConfig(Context, ControlCenterServiceHandler, AppConfigBean)}、 + * {@link #startRemindThreadWithBatteryInfo(Context, ControlCenterServiceHandler, boolean, int)}、{@link #stopRemindThread()} + * @Author 豆包&ZhanGSKen + * @Describe 电量通知提醒线程 */ public class RemindThread extends Thread { - // ================================== 静态常量区(置顶归类,消除魔法值)================================= + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== public static final String TAG = "RemindThread"; // 时间常量 (ms) @@ -32,10 +34,10 @@ public class RemindThread extends Thread { private static final String REMIND_TYPE_CHARGE = "+"; private static final String REMIND_TYPE_USAGE = "-"; - // ================================== 静态成员(多实例列表管理)================================= + // ====================== 静态成员(多实例列表管理) ====================== private static volatile ArrayList sRemindThreadList; - // ================================== 成员变量区(按功能分层,volatile保证多线程可见性)================================= + // ====================== 成员变量区(按功能分层,volatile保证多线程可见性) ====================== // 并发安全锁(保护线程状态变更) private final Object mRemindLock = new Object(); @@ -56,16 +58,16 @@ public class RemindThread extends Thread { private volatile int quantityOfElectricity; private volatile boolean isCharging; - // ================================== 私有构造器(禁止外部实例化)================================= + // ====================== 私有构造器(禁止外部实例化) ====================== 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.mwrControlCenterServiceHandler = new WeakReference<>(handler); 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: 入参非法 */ 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) { - 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; } - // 初始化线程列表 + // 初始化线程列表(双重校验锁) if (sRemindThreadList == null) { synchronized (RemindThread.class) { if (sRemindThreadList == null) { @@ -102,7 +104,7 @@ public class RemindThread extends Thread { newRemindThread.isExist = false; newRemindThread.start(); 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; } @@ -116,15 +118,15 @@ public class RemindThread extends Thread { * @return true: 启动成功;false: 入参非法 */ 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) { - LogUtils.e(TAG, "启动失败:入参为空 | context=" + context + " | handler=" + handler); + LogUtils.e(TAG, String.format("启动失败:入参为空 | context=%s | handler=%s", context, handler)); return false; } - // 初始化线程列表 + // 初始化线程列表(双重校验锁) if (sRemindThreadList == null) { synchronized (RemindThread.class) { if (sRemindThreadList == null) { @@ -139,13 +141,13 @@ public class RemindThread extends Thread { // 创建并启动新线程 RemindThread newRemindThread = new RemindThread(context, handler); - // 同步电池状态 + // 同步电池状态(范围校验) newRemindThread.isCharging = isCharging; newRemindThread.quantityOfElectricity = Math.min(Math.max(batteryLevel, BATTERY_LEVEL_MIN), BATTERY_LEVEL_MAX); newRemindThread.isExist = false; newRemindThread.start(); 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; } @@ -153,7 +155,8 @@ public class RemindThread extends Thread { * 安全停止所有线程,清空列表 */ 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()) { LogUtils.w(TAG, "停止失败:线程列表为空"); return; @@ -162,14 +165,14 @@ public class RemindThread extends Thread { // 标记所有线程退出 for (RemindThread remindThread : sRemindThreadList) { remindThread.isExist = true; - LogUtils.d(TAG, "标记线程退出 | threadId=" + remindThread.getId()); + LogUtils.d(TAG, String.format("标记线程退出 | threadId=%d", remindThread.getId())); } // 清空列表 sRemindThreadList.clear(); LogUtils.d(TAG, "所有线程已标记退出,列表已清空"); } - // ================================== 私有静态辅助方法(多实例管理)================================= + // ====================== 私有静态辅助方法(多实例管理) ====================== /** * 停止所有旧线程并清空列表 */ @@ -181,29 +184,29 @@ public class RemindThread extends Thread { // 标记所有旧线程退出 for (RemindThread remindThread : sRemindThreadList) { remindThread.isExist = true; - LogUtils.d(TAG, "标记旧线程退出 | threadId=" + remindThread.getId()); + LogUtils.d(TAG, String.format("标记旧线程退出 | threadId=%d", remindThread.getId())); } // 清空旧线程列表 sRemindThreadList.clear(); LogUtils.d(TAG, "旧线程已全部标记退出,列表已清空"); } - // ================================== 线程核心运行逻辑================================= + // ====================== 线程核心运行逻辑 ====================== @Override public void run() { - LogUtils.d(TAG, "run执行 | threadId=" + getId() + " | 状态=" + getState()); + LogUtils.d(TAG, String.format("run执行 | threadId=%d | 状态=%s", getId(), getState())); // 初始化提醒状态(加锁保护,避免多线程竞争) synchronized (mRemindLock) { if (isReminding) { - LogUtils.w(TAG, "线程已在提醒状态,退出运行 | threadId=" + getId()); + LogUtils.w(TAG, String.format("线程已在提醒状态,退出运行 | threadId=%d", getId())); return; } isReminding = true; } // 核心电量检测循环 - LogUtils.d(TAG, "进入电量检测循环 | 休眠时间=" + sleepTime + "ms | threadId=" + getId()); + LogUtils.d(TAG, String.format("进入电量检测循环 | 休眠时间=%dms | threadId=%d", sleepTime, getId())); while (!isExist) { try { // 快速退出判断 @@ -211,37 +214,40 @@ public class RemindThread extends Thread { // 电量有效性校验(非0-100视为无效),退出电量提醒线程 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; } // 充电/耗电提醒触发逻辑 - if (isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue) { - LogUtils.d(TAG, "触发充电提醒 | 当前电量=" + quantityOfElectricity + " ≥ 阈值=" + chargeReminderValue + " | threadId=" + getId()); + boolean chargeRemindTrigger = isCharging && isEnableChargeReminder && quantityOfElectricity >= chargeReminderValue; + 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); - } else if (!isCharging && isEnableUsageReminder && quantityOfElectricity <= usageReminderValue) { - LogUtils.d(TAG, "触发耗电提醒 | 当前电量=" + quantityOfElectricity + " ≤ 阈值=" + usageReminderValue + " | threadId=" + getId()); + } else if (usageRemindTrigger) { + LogUtils.d(TAG, String.format("触发耗电提醒 | 当前电量=%d ≤ 阈值=%d | threadId=%d", quantityOfElectricity, usageReminderValue, getId())); sendNotificationMessageInternal(REMIND_TYPE_USAGE, quantityOfElectricity, isCharging); } else { - // 未有合适类型提醒,退出提醒线程 - LogUtils.d(TAG, "未有合适类型提醒,退出提醒线程"); - break; - } - // 安全休眠,保留中断标记 + LogUtils.d(TAG, String.format("未有合适类型提醒,退出提醒线程 | threadId=%d", getId())); + break; + } + + // 安全休眠,保留中断标记 safeSleepInternal(sleepTime); } catch (Exception e) { - LogUtils.e(TAG, "循环运行异常,退出电量提醒线程 | 当前电量=" + quantityOfElectricity + " | threadId=" + getId(), e); + LogUtils.e(TAG, String.format("循环运行异常,退出电量提醒线程 | 当前电量=%d | threadId=%d", quantityOfElectricity, getId()), e); break; } } // 循环退出,清理状态 cleanThreadStateInternal(); - LogUtils.d(TAG, "run结束 | threadId=" + getId()); + LogUtils.d(TAG, String.format("run结束 | threadId=%d", getId())); } - // ================================== 内部业务辅助方法================================= + // ====================== 内部业务辅助方法 ====================== /** * 发送提醒消息到Handler(弱引用避免内存泄漏) * @param type 提醒类型:+充电/-耗电 @@ -249,20 +255,21 @@ public class RemindThread extends Thread { * @param 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) { - LogUtils.d(TAG, "消息发送跳过:线程已退出或提醒关闭 | threadId=" + getId()); + LogUtils.d(TAG, String.format("消息发送跳过:线程已退出或提醒关闭 | threadId=%d", getId())); return; } - // 获取弱引用的Handler + // 获取弱引用的Handler(校验有效性) ControlCenterServiceHandler handler = mwrControlCenterServiceHandler.get(); if (handler == null) { - LogUtils.w(TAG, "消息发送失败:Handler已被回收 | threadId=" + getId()); + LogUtils.w(TAG, String.format("消息发送失败:Handler已被回收 | threadId=%d", getId())); return; } + // 构建并发送消息 Message message = Message.obtain(handler, ControlCenterServiceHandler.MSG_REMIND_TEXT); message.obj = type; message.arg1 = battery; @@ -270,9 +277,9 @@ public class RemindThread extends Thread { try { 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) { - LogUtils.e(TAG, "消息发送异常 | threadId=" + getId(), e); + LogUtils.e(TAG, String.format("消息发送异常 | threadId=%d", getId()), e); // 异常时回收Message,避免内存泄漏 if (message != null) { message.recycle(); @@ -285,12 +292,12 @@ public class RemindThread extends Thread { * @param millis 休眠时长(ms) */ 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 { Thread.sleep(millis); } catch (InterruptedException e) { 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() { - LogUtils.d(TAG, "resetThreadStateInternal调用 | threadId=" + getId()); + LogUtils.d(TAG, String.format("resetThreadStateInternal调用 | threadId=%d", getId())); // 状态标记初始化 isExist = false; isReminding = false; @@ -310,22 +317,23 @@ public class RemindThread extends Thread { usageReminderValue = -1; quantityOfElectricity = INVALID_BATTERY_VALUE; isCharging = false; - LogUtils.d(TAG, "线程初始状态重置完成 | threadId=" + getId()); + LogUtils.d(TAG, String.format("线程初始状态重置完成 | threadId=%d", getId())); } /** * 清理线程运行状态(循环退出时调用) */ private void cleanThreadStateInternal() { - LogUtils.d(TAG, "cleanThreadStateInternal调用 | threadId=" + getId()); + LogUtils.d(TAG, String.format("cleanThreadStateInternal调用 | threadId=%d", getId())); isReminding = false; isExist = true; quantityOfElectricity = INVALID_BATTERY_VALUE; // 中断当前线程(如果存活) if (isAlive()) { 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 */ 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) { - LogUtils.e(TAG, "配置同步失败:配置Bean为空 | threadId=" + getId()); + LogUtils.e(TAG, String.format("配置同步失败:配置Bean为空 | threadId=%d", getId())); quantityOfElectricity = INVALID_BATTERY_VALUE; return; } @@ -350,7 +358,8 @@ public class RemindThread extends Thread { ? config.getCurrentBatteryValue() : INVALID_BATTERY_VALUE; 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() { 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; } - // ================================== Getter/Setter(按需开放)================================= + // ====================== Getter/Setter(按需开放) ====================== 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; } @@ -373,7 +382,7 @@ public class RemindThread extends Thread { return isExist; } - // ================================== 调试辅助方法================================= + // ====================== 调试辅助方法 ====================== @Override public String toString() { return "RemindThread{" + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTest2Activity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTest2Activity.java index a330e5d..131d5c7 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTest2Activity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTest2Activity.java @@ -16,32 +16,35 @@ import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.models.BackgroundBean; import cc.winboll.studio.powerbell.utils.FileUtils; import cc.winboll.studio.powerbell.utils.ImageCropUtils; -import cc.winboll.studio.powerbell.views.BackgroundView; import cc.winboll.studio.powerbell.views.MemoryCachedBackgroundView; import java.io.File; import java.io.IOException; import java.io.InputStream; /** - * @Author ZhanGSKen&豆包大模型 - * @Date 2025/12/22 08:31 - * @Describe MainUnitTest2Activity + * 单元测试页面2(内存缓存背景视图专用) + * 功能:测试MemoryCachedBackgroundView加载、图片裁剪、双重刷新预览等功能 + * 适配:Java7 | API30 | 私有目录文件操作 | 无Uri冲突 | 内存缓存视图 + * @Author 豆包&ZhanGSKen + * @Describe 单元测试页2:验证带内存缓存的背景视图相关逻辑 */ public class MainUnitTest2Activity extends AppCompatActivity { - // ====================== 常量定义 ====================== + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== public static final String TAG = "MainUnitTest2Activity"; public static final int REQUEST_CROP_IMAGE = 0; 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 LinearLayout mllBackgroundView; private String mAppPrivateDirPath; - private File mPrivateTestImageFile; // 仅用File,不用Uri + private File mPrivateTestImageFile; private File mPrivateCropImageFile; - BackgroundBean mPreviewBackgroundBean; - LinearLayout mllBackgroundView; + private BackgroundBean mPreviewBackgroundBean; - // ====================== 生命周期方法 ====================== + // ====================== 生命周期方法(按执行顺序:onCreate→onActivityResult) ====================== @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -50,65 +53,66 @@ public class MainUnitTest2Activity extends AppCompatActivity { initBaseParams(); initViewAndEvent(); copyAssetsTestImageToPrivateDir(); - //loadBackgroundByFile(); // 直接用File加载 - mPreviewBackgroundBean = new BackgroundBean(); - mPreviewBackgroundBean.setBackgroundFileName(mPrivateTestImageFile.getName()); - mPreviewBackgroundBean.setBackgroundFilePath(mPrivateTestImageFile.getAbsolutePath()); - mPreviewBackgroundBean.setBackgroundScaledCompressFileName(mPrivateCropImageFile.getName()); - mPreviewBackgroundBean.setBackgroundScaledCompressFilePath(mPrivateCropImageFile.getAbsolutePath()); - mPreviewBackgroundBean.setIsUseBackgroundFile(true); - doubleRefreshPreview(); + initBackgroundBean(); + doubleRefreshPreview(); - ToastUtils.show("单元测试页面启动完成"); + ToastUtils.show("单元测试页面2启动完成"); LogUtils.d(TAG, "=== 页面 onCreate 初始化结束 ==="); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent 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) { handleCropResult(resultCode); } } - // ====================== 初始化相关方法 ====================== + // ====================== 初始化相关方法(基础参数→视图→背景Bean) ====================== + /** + * 初始化基础参数:私有目录、测试文件 + */ private void initBaseParams() { - LogUtils.d(TAG, "初始化基础参数:工具类+私有目录+File"); - - // 私有目录(无需权限,无UID冲突) + LogUtils.d(TAG, "initBaseParams:初始化基础参数"); + // 初始化私有目录(无需权限,无UID冲突) mAppPrivateDirPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBellTest/"; File privateDir = new File(mAppPrivateDirPath); if (!privateDir.exists()) { - privateDir.mkdirs(); - LogUtils.d(TAG, "创建私有目录:" + mAppPrivateDirPath); + boolean isDirCreated = privateDir.mkdirs(); + LogUtils.d(TAG, String.format("initBaseParams:创建私有目录 | 路径=%s | 结果=%b", mAppPrivateDirPath, isDirCreated)); } - // 初始化File(无Uri) + // 初始化测试文件与裁剪文件(无Uri) File refFile = new File(ASSETS_TEST_IMAGE_PATH); String uniqueTestName = FileUtils.createUniqueFileName(refFile) + ".png"; String uniqueCropName = uniqueTestName.replace(".png", "_crop.png"); mPrivateTestImageFile = new File(mAppPrivateDirPath, uniqueTestName); 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() { - LogUtils.d(TAG, "初始化布局与控件事件"); + LogUtils.d(TAG, "initViewAndEvent:初始化布局与控件事件"); setContentView(R.layout.activity_mainunittest2); - mllBackgroundView = (LinearLayout) findViewById(R.id.ll_backgroundview); - mMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, "", false); - mllBackgroundView.addView(mMemoryCachedBackgroundView); - - //mMemoryCachedBackgroundView = (BackgroundView) findViewById(R.id.backgroundview); + mllBackgroundView = (LinearLayout) findViewById(R.id.ll_backgroundview); + + // 创建MemoryCachedBackgroundView单例并添加到布局 + mMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, "", false); + mllBackgroundView.addView(mMemoryCachedBackgroundView); + LogUtils.d(TAG, "initViewAndEvent:内存缓存背景视图实例创建并添加完成"); // 跳转主页面按钮 Button btnMain = (Button) findViewById(R.id.btn_main_activity); btnMain.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d(TAG, "点击按钮:跳转主页面"); + LogUtils.d(TAG, "initViewAndEvent:点击按钮→跳转主页面"); startActivity(new Intent(MainUnitTest2Activity.this, MainActivity.class)); } }); @@ -118,11 +122,11 @@ public class MainUnitTest2Activity extends AppCompatActivity { btnCrop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d(TAG, "点击按钮:启动裁剪(File路径版)"); + LogUtils.d(TAG, "initViewAndEvent:点击按钮→启动裁剪(File路径版)"); ToastUtils.show("准备启动图片裁剪"); - if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { - startCropTestByFile(); // 直接传File + if (isFileValid(mPrivateTestImageFile)) { + startCropTestByFile(); } else { ToastUtils.show("测试图片未准备好,重新拷贝"); 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() { - LogUtils.d(TAG, "开始拷贝assets图片到私有目录"); - if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { - LogUtils.d(TAG, "图片已存在,无需拷贝"); + LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir:开始拷贝assets图片到私有目录"); + if (isFileValid(mPrivateTestImageFile)) { + LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir:图片已存在,无需拷贝"); return; } @@ -143,97 +164,87 @@ public class MainUnitTest2Activity extends AppCompatActivity { try { inputStream = getAssets().open(ASSETS_TEST_IMAGE_PATH); FileUtils.copyStreamToFile(inputStream, mPrivateTestImageFile); - LogUtils.d(TAG, "图片拷贝成功,大小:" + mPrivateTestImageFile.length() + "字节"); + LogUtils.d(TAG, String.format("copyAssetsTestImageToPrivateDir:图片拷贝成功 | 大小=%d字节", mPrivateTestImageFile.length())); } catch (IOException e) { - LogUtils.e(TAG, "图片拷贝失败:" + e.getMessage(), e); + LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir:图片拷贝失败 | %s", e.getMessage()), e); ToastUtils.show("图片准备失败"); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { - LogUtils.e(TAG, "关闭流失败:" + e.getMessage()); + LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir:关闭流失败 | %s", e.getMessage())); } } } } - // ====================== 核心业务方法(全改为File路径) ====================== - /** 直接用File路径加载背景图(无Uri,无冲突) */ -// 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重载方法) */ + /** + * 直接用File启动裁剪(关键:调用ImageCropUtils的File重载方法) + */ private void startCropTestByFile() { - LogUtils.d(TAG, "启动裁剪(File路径版),原图:" + mPrivateTestImageFile.getAbsolutePath()); + LogUtils.d(TAG, String.format("startCropTestByFile:启动裁剪 | 原图=%s", mPrivateTestImageFile.getAbsolutePath())); // 确保输出目录存在 File cropParent = mPrivateCropImageFile.getParentFile(); if (!cropParent.exists()) { - cropParent.mkdirs(); + boolean isDirCreated = cropParent.mkdirs(); + LogUtils.d(TAG, String.format("startCropTestByFile:创建裁剪目录 | 路径=%s | 结果=%b", cropParent.getAbsolutePath(), isDirCreated)); } // 调用ImageCropUtils的File参数方法(核心:绕开Uri) ImageCropUtils.startImageCrop( - this, - mPrivateTestImageFile, // 原图File - mPrivateCropImageFile, // 输出File - 0, - 0, - true, - REQUEST_CROP_IMAGE + this, + mPrivateTestImageFile, + mPrivateCropImageFile, + 0, + 0, + true, + REQUEST_CROP_IMAGE ); - LogUtils.d(TAG, "裁剪请求已发送,输出路径:" + mPrivateCropImageFile.getAbsolutePath()); + LogUtils.d(TAG, String.format("startCropTestByFile:裁剪请求已发送 | 输出路径=%s", mPrivateCropImageFile.getAbsolutePath())); ToastUtils.show("已启动图片裁剪"); } - /** 处理裁剪结果(直接校验输出File) */ + /** + * 处理裁剪结果(直接校验输出File) + * @param 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 (mPrivateCropImageFile.exists() && mPrivateCropImageFile.length() > 100) { + if (isFileValid(mPrivateCropImageFile)) { mMemoryCachedBackgroundView.loadImage(mPrivateCropImageFile.getAbsolutePath()); - LogUtils.d(TAG, "裁剪成功,加载裁剪图:" + mPrivateCropImageFile.getAbsolutePath()); + LogUtils.d(TAG, String.format("handleCropResult:裁剪成功 | 加载裁剪图=%s", mPrivateCropImageFile.getAbsolutePath())); ToastUtils.show("裁剪成功"); - mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true); - doubleRefreshPreview(); + mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true); + doubleRefreshPreview(); } else { - LogUtils.e(TAG, "裁剪成功但输出文件无效"); + LogUtils.e(TAG, "handleCropResult:裁剪成功但输出文件无效"); ToastUtils.show("裁剪失败:输出文件无效"); } } else if (resultCode == RESULT_CANCELED) { - LogUtils.d(TAG, "裁剪取消"); + LogUtils.d(TAG, "handleCropResult:裁剪取消"); ToastUtils.show("裁剪已取消"); } else { - LogUtils.e(TAG, "裁剪失败:resultCode异常"); + LogUtils.e(TAG, String.format("handleCropResult:裁剪失败 | resultCode异常=%d", resultCode)); ToastUtils.show("裁剪失败"); } } - /** * 双重刷新预览,确保背景加载最新数据 - * 移除:缓存清空逻辑 */ private void doubleRefreshPreview() { - + LogUtils.d(TAG, "doubleRefreshPreview:执行双重刷新预览"); // 第一重刷新 try { mMemoryCachedBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); - mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); - LogUtils.d(TAG, "【双重刷新】第一重完成"); + mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); + LogUtils.d(TAG, "doubleRefreshPreview:【双重刷新】第一重完成"); } catch (Exception e) { - LogUtils.e(TAG, "【双重刷新】第一重异常:" + e.getMessage()); + LogUtils.e(TAG, String.format("doubleRefreshPreview:【双重刷新】第一重异常 | %s", e.getMessage())); return; } @@ -245,13 +256,25 @@ public class MainUnitTest2Activity extends AppCompatActivity { try { mMemoryCachedBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); mMemoryCachedBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); - LogUtils.d(TAG, "【双重刷新】第二重完成"); + LogUtils.d(TAG, "doubleRefreshPreview:【双重刷新】第二重完成"); } 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; } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTestActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTestActivity.java index ab4b0de..fcefa9a 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTestActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/MainUnitTestActivity.java @@ -12,32 +12,37 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.MainActivity; 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.ImageCropUtils; import cc.winboll.studio.powerbell.views.BackgroundView; import java.io.File; import java.io.IOException; import java.io.InputStream; -import cc.winboll.studio.powerbell.models.BackgroundBean; /** - * 终极修复版:放弃FileProvider,直接用私有目录File路径,彻底解决UID冲突 + * 单元测试页面 + * 功能:测试背景图加载、图片裁剪、双重刷新预览等功能 + * 适配:Java7 | API30 | 私有目录文件操作 | 无Uri冲突 + * @Author 豆包&ZhanGSKen + * @Describe 单元测试页:验证图片处理与背景预览相关逻辑 */ public class MainUnitTestActivity extends AppCompatActivity { - // ====================== 常量定义 ====================== + // ====================== 静态常量区(置顶归类,消除魔法值) ====================== public static final String TAG = "MainUnitTestActivity"; public static final int REQUEST_CROP_IMAGE = 0; 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 String mAppPrivateDirPath; private File mPrivateTestImageFile; // 仅用File,不用Uri private File mPrivateCropImageFile; - BackgroundBean mPreviewBackgroundBean; + private BackgroundBean mPreviewBackgroundBean; - // ====================== 生命周期方法 ====================== + // ====================== 生命周期方法(按执行顺序:onCreate→onActivityResult) ====================== @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -46,15 +51,9 @@ public class MainUnitTestActivity extends AppCompatActivity { initBaseParams(); initViewAndEvent(); copyAssetsTestImageToPrivateDir(); - //loadBackgroundByFile(); // 直接用File加载 - mPreviewBackgroundBean = new BackgroundBean(); - mPreviewBackgroundBean.setBackgroundFileName(mPrivateTestImageFile.getName()); - mPreviewBackgroundBean.setBackgroundFilePath(mPrivateTestImageFile.getAbsolutePath()); - mPreviewBackgroundBean.setBackgroundScaledCompressFileName(mPrivateCropImageFile.getName()); - mPreviewBackgroundBean.setBackgroundScaledCompressFilePath(mPrivateCropImageFile.getAbsolutePath()); - mPreviewBackgroundBean.setIsUseBackgroundFile(true); - doubleRefreshPreview(); - + initBackgroundBean(); + doubleRefreshPreview(); + ToastUtils.show("单元测试页面启动完成"); LogUtils.d(TAG, "=== 页面 onCreate 初始化结束 ==="); } @@ -62,36 +61,42 @@ public class MainUnitTestActivity extends AppCompatActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent 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) { handleCropResult(resultCode); } } - // ====================== 初始化相关方法 ====================== + // ====================== 初始化相关方法(基础参数→视图→背景Bean) ====================== + /** + * 初始化基础参数:私有目录、测试文件 + */ private void initBaseParams() { - LogUtils.d(TAG, "初始化基础参数:工具类+私有目录+File"); - - // 私有目录(无需权限,无UID冲突) + LogUtils.d(TAG, "initBaseParams:初始化基础参数"); + // 初始化私有目录(无需权限,无UID冲突) mAppPrivateDirPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBellTest/"; File privateDir = new File(mAppPrivateDirPath); if (!privateDir.exists()) { - privateDir.mkdirs(); - LogUtils.d(TAG, "创建私有目录:" + mAppPrivateDirPath); + boolean isDirCreated = privateDir.mkdirs(); + LogUtils.d(TAG, String.format("initBaseParams:创建私有目录 | 路径=%s | 结果=%b", mAppPrivateDirPath, isDirCreated)); } - // 初始化File(无Uri) + // 初始化测试文件与裁剪文件(无Uri) File refFile = new File(ASSETS_TEST_IMAGE_PATH); String uniqueTestName = FileUtils.createUniqueFileName(refFile) + ".png"; String uniqueCropName = uniqueTestName.replace(".png", "_crop.png"); mPrivateTestImageFile = new File(mAppPrivateDirPath, uniqueTestName); 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() { - LogUtils.d(TAG, "初始化布局与控件事件"); + LogUtils.d(TAG, "initViewAndEvent:初始化布局与控件事件"); setContentView(R.layout.activity_mainunittest); mBackgroundView = (BackgroundView) findViewById(R.id.backgroundview); @@ -100,7 +105,7 @@ public class MainUnitTestActivity extends AppCompatActivity { btnMain.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d(TAG, "点击按钮:跳转主页面"); + LogUtils.d(TAG, "initViewAndEvent:点击按钮→跳转主页面"); startActivity(new Intent(MainUnitTestActivity.this, MainActivity.class)); } }); @@ -110,11 +115,11 @@ public class MainUnitTestActivity extends AppCompatActivity { btnCrop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d(TAG, "点击按钮:启动裁剪(File路径版)"); + LogUtils.d(TAG, "initViewAndEvent:点击按钮→启动裁剪(File路径版)"); ToastUtils.show("准备启动图片裁剪"); - if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { - startCropTestByFile(); // 直接传File + if (isFileValid(mPrivateTestImageFile)) { + startCropTestByFile(); } else { ToastUtils.show("测试图片未准备好,重新拷贝"); 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() { - LogUtils.d(TAG, "开始拷贝assets图片到私有目录"); - if (mPrivateTestImageFile.exists() && mPrivateTestImageFile.length() > 100) { - LogUtils.d(TAG, "图片已存在,无需拷贝"); + LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir:开始拷贝assets图片到私有目录"); + if (isFileValid(mPrivateTestImageFile)) { + LogUtils.d(TAG, "copyAssetsTestImageToPrivateDir:图片已存在,无需拷贝"); return; } @@ -135,97 +157,87 @@ public class MainUnitTestActivity extends AppCompatActivity { try { inputStream = getAssets().open(ASSETS_TEST_IMAGE_PATH); FileUtils.copyStreamToFile(inputStream, mPrivateTestImageFile); - LogUtils.d(TAG, "图片拷贝成功,大小:" + mPrivateTestImageFile.length() + "字节"); + LogUtils.d(TAG, String.format("copyAssetsTestImageToPrivateDir:图片拷贝成功 | 大小=%d字节", mPrivateTestImageFile.length())); } catch (IOException e) { - LogUtils.e(TAG, "图片拷贝失败:" + e.getMessage(), e); + LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir:图片拷贝失败 | %s", e.getMessage()), e); ToastUtils.show("图片准备失败"); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { - LogUtils.e(TAG, "关闭流失败:" + e.getMessage()); + LogUtils.e(TAG, String.format("copyAssetsTestImageToPrivateDir:关闭流失败 | %s", e.getMessage())); } } } } - // ====================== 核心业务方法(全改为File路径) ====================== - /** 直接用File路径加载背景图(无Uri,无冲突) */ -// 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重载方法) */ + /** + * 直接用File启动裁剪(关键:调用ImageCropUtils的File重载方法) + */ private void startCropTestByFile() { - LogUtils.d(TAG, "启动裁剪(File路径版),原图:" + mPrivateTestImageFile.getAbsolutePath()); + LogUtils.d(TAG, String.format("startCropTestByFile:启动裁剪 | 原图=%s", mPrivateTestImageFile.getAbsolutePath())); // 确保输出目录存在 File cropParent = mPrivateCropImageFile.getParentFile(); if (!cropParent.exists()) { - cropParent.mkdirs(); + boolean isDirCreated = cropParent.mkdirs(); + LogUtils.d(TAG, String.format("startCropTestByFile:创建裁剪目录 | 路径=%s | 结果=%b", cropParent.getAbsolutePath(), isDirCreated)); } // 调用ImageCropUtils的File参数方法(核心:绕开Uri) ImageCropUtils.startImageCrop( - this, - mPrivateTestImageFile, // 原图File - mPrivateCropImageFile, // 输出File - 0, - 0, - true, - REQUEST_CROP_IMAGE + this, + mPrivateTestImageFile, // 原图File + mPrivateCropImageFile, // 输出File + 0, + 0, + true, + REQUEST_CROP_IMAGE ); - LogUtils.d(TAG, "裁剪请求已发送,输出路径:" + mPrivateCropImageFile.getAbsolutePath()); + LogUtils.d(TAG, String.format("startCropTestByFile:裁剪请求已发送 | 输出路径=%s", mPrivateCropImageFile.getAbsolutePath())); ToastUtils.show("已启动图片裁剪"); } - /** 处理裁剪结果(直接校验输出File) */ + /** + * 处理裁剪结果(直接校验输出File) + * @param 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 (mPrivateCropImageFile.exists() && mPrivateCropImageFile.length() > 100) { + if (isFileValid(mPrivateCropImageFile)) { mBackgroundView.loadImage(mPrivateCropImageFile.getAbsolutePath()); - LogUtils.d(TAG, "裁剪成功,加载裁剪图:" + mPrivateCropImageFile.getAbsolutePath()); + LogUtils.d(TAG, String.format("handleCropResult:裁剪成功 | 加载裁剪图=%s", mPrivateCropImageFile.getAbsolutePath())); ToastUtils.show("裁剪成功"); - mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true); - doubleRefreshPreview(); + mPreviewBackgroundBean.setIsUseBackgroundScaledCompressFile(true); + doubleRefreshPreview(); } else { - LogUtils.e(TAG, "裁剪成功但输出文件无效"); + LogUtils.e(TAG, "handleCropResult:裁剪成功但输出文件无效"); ToastUtils.show("裁剪失败:输出文件无效"); } } else if (resultCode == RESULT_CANCELED) { - LogUtils.d(TAG, "裁剪取消"); + LogUtils.d(TAG, "handleCropResult:裁剪取消"); ToastUtils.show("裁剪已取消"); } else { - LogUtils.e(TAG, "裁剪失败:resultCode异常"); + LogUtils.e(TAG, String.format("handleCropResult:裁剪失败 | resultCode异常=%d", resultCode)); ToastUtils.show("裁剪失败"); } } - /** * 双重刷新预览,确保背景加载最新数据 - * 移除:缓存清空逻辑 */ private void doubleRefreshPreview() { - + LogUtils.d(TAG, "doubleRefreshPreview:执行双重刷新预览"); // 第一重刷新 try { mBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); - mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); - LogUtils.d(TAG, "【双重刷新】第一重完成"); + mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); + LogUtils.d(TAG, "doubleRefreshPreview:【双重刷新】第一重完成"); } catch (Exception e) { - LogUtils.e(TAG, "【双重刷新】第一重异常:" + e.getMessage()); + LogUtils.e(TAG, String.format("doubleRefreshPreview:【双重刷新】第一重异常 | %s", e.getMessage())); return; } @@ -237,13 +249,25 @@ public class MainUnitTestActivity extends AppCompatActivity { try { mBackgroundView.loadByBackgroundBean(mPreviewBackgroundBean, true); mBackgroundView.setBackgroundColor(mPreviewBackgroundBean.getPixelColor()); - LogUtils.d(TAG, "【双重刷新】第二重完成"); + LogUtils.d(TAG, "doubleRefreshPreview:【双重刷新】第二重完成"); } 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; } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/APPPlusUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/APPPlusUtils.java index be5cc9b..181e3d8 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/APPPlusUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/APPPlusUtils.java @@ -11,19 +11,18 @@ import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.R; /** - * @Author ZhanGSKen&豆包大模型 - * @Date 2025/11/26 15:54 - * @Describe 应用图标切换工具类(启用组件时创建对应快捷方式) + * 应用图标切换工具类(启用组件时创建对应快捷方式) * 适配:Java7 | API30 | 高低版本快捷方式创建兼容 + * @Author 豆包&ZhanGSKen + * @Describe 应用启动器组件切换与桌面快捷方式创建工具,支持多组件管理与版本兼容 */ public class APPPlusUtils { - // ======================== 静态常量区 ======================== + // ======================== 静态常量区(魔法值与标签管理)======================== public static final String TAG = "APPPlusUtils"; - // 快捷方式配置(名称+图标,需与实际资源匹配,预留扩展) - // private static final String PLUS_SHORTCUT_NAME = "位置服务-Laojun"; - // private static final int PLUS_SHORTCUT_ICON = R.mipmap.ic_launcher; // Laojun 图标资源 + private static final int SHORTCUT_ICON_DEFAULT = R.drawable.ic_launcher; // 默认快捷方式图标 + private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; // 旧版快捷方式广播Action - // ======================== 公共业务方法区 ======================== + // ======================== 公共业务方法区(对外核心接口)======================== /** * 切换应用启动器组件(禁用其他组件,启用目标组件) * @param context 上下文 @@ -31,9 +30,15 @@ public class APPPlusUtils { * @return 切换是否成功 */ public static boolean switchAppLauncherToComponent(Context context, String componentName) { - LogUtils.d(TAG, "switchAppLauncherToComponent() 调用,传入组件名:" + componentName); + LogUtils.d(TAG, String.format("switchAppLauncherToComponent调用 | 传入组件名=%s", componentName)); + + // 参数校验 if (context == null) { - LogUtils.e(TAG, "switchAppLauncherToComponent() 切换失败:上下文为空"); + LogUtils.e(TAG, "switchAppLauncherToComponent失败:上下文为空"); + return false; + } + if (componentName == null || componentName.isEmpty()) { + LogUtils.e(TAG, "switchAppLauncherToComponent失败:组件名为空"); return false; } @@ -51,18 +56,61 @@ public class APPPlusUtils { // 启用目标组件 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(); return true; } 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(); 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+) * @param context 上下文 @@ -72,26 +120,31 @@ public class APPPlusUtils { * @return 是否创建成功 */ 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) { - LogUtils.e(TAG, "createComponentShortcut() 快捷方式创建失败:参数为空"); + // 参数校验 + String componentName = component != null ? component.getClassName() : "null"; + 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; } + // 图标资源默认值补全 + int finalIconRes = iconRes != 0 ? iconRes : SHORTCUT_ICON_DEFAULT; + // Android 8.0+(API 26+):使用 ShortcutManager(系统推荐) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { - PackageManager pm = context.getPackageManager(); android.content.pm.ShortcutManager shortcutManager = context.getSystemService(android.content.pm.ShortcutManager.class); if (shortcutManager == null || !shortcutManager.isRequestPinShortcutSupported()) { - LogUtils.w(TAG, "createComponentShortcut() 系统不支持创建快捷方式"); + LogUtils.w(TAG, "createComponentShortcut:系统不支持创建快捷方式"); return false; } // 检查是否已存在该组件的快捷方式(去重) for (android.content.pm.ShortcutInfo info : shortcutManager.getPinnedShortcuts()) { if (component.getClassName().equals(info.getIntent().getComponent().getClassName())) { - LogUtils.d(TAG, "createComponentShortcut() 快捷方式已存在:" + component.getClassName()); + LogUtils.d(TAG, String.format("createComponentShortcut:快捷方式已存在=%s", componentName)); return true; } } @@ -106,17 +159,17 @@ public class APPPlusUtils { android.content.pm.ShortcutInfo shortcutInfo = new android.content.pm.ShortcutInfo.Builder(context, component.getClassName()) .setShortLabel(name) .setLongLabel(name) - .setIcon(android.graphics.drawable.Icon.createWithResource(context, iconRes)) + .setIcon(android.graphics.drawable.Icon.createWithResource(context, finalIconRes)) .setIntent(launchIntent) .build(); // 请求创建快捷方式(需用户确认) shortcutManager.requestPinShortcut(shortcutInfo, null); - LogUtils.d(TAG, "createComponentShortcut() Android O+ 快捷方式创建请求已发送"); + LogUtils.d(TAG, "createComponentShortcut:Android O+ 快捷方式创建请求已发送"); return true; } 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; } } else { @@ -129,61 +182,22 @@ public class APPPlusUtils { .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_NAME, name); installIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, - Intent.ShortcutIconResource.fromContext(context, iconRes)); + Intent.ShortcutIconResource.fromContext(context, finalIconRes)); installIntent.putExtra("duplicate", false); // 禁止重复创建 context.sendBroadcast(installIntent); - LogUtils.d(TAG, "createComponentShortcut() Android O- 快捷方式创建广播已发送"); + LogUtils.d(TAG, "createComponentShortcut:Android O- 快捷方式创建广播已发送"); return true; } 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; } } } - - // ======================== 私有辅助方法区(组件状态控制) ======================== - /** - * 启用组件(带状态检查,避免重复操作) - * @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()); - } - } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java index c1a5bbb..8cb769d 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java @@ -8,68 +8,76 @@ import java.util.ArrayList; /** * 应用缓存工具类(适配Android API 30,基于Java 7编写) * 负责电池信息的缓存、持久化与管理 + * @Author 豆包&ZhanGSKen + * @Describe 电池信息缓存工具:实现电量变化记录、持久化存储与缓存限制 */ public class AppCacheUtils { - // ===================== 常量定义区 ===================== + // ===================== 静态常量区(置顶归类,消除魔法值) ===================== public static final String TAG = "AppCacheUtils"; + private static final int MAX_BATTERY_RECORD_COUNT = 180; // 电池记录最大条数限制 - // ===================== 静态属性区 ===================== - // 单例实例 + // ===================== 静态成员区(单例相关) ===================== private static AppCacheUtils sInstance; - // ===================== 成员属性区 ===================== - // 上下文环境(使用ApplicationContext避免内存泄漏) - private Context mContext; - // 电池信息缓存列表 - private ArrayList mBatteryInfoList; + // ===================== 成员变量区(按功能分层) ===================== + private Context mContext; // ApplicationContext,避免内存泄漏 + private ArrayList mBatteryInfoList; // 电池信息缓存列表 - // ===================== 单例方法区 ===================== + // ===================== 单例方法区(线程安全) ===================== /** * 获取单例实例 * @param context 上下文(内部会转换为ApplicationContext) * @return 唯一AppCacheUtils实例 */ 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 (context == null) { + LogUtils.e(TAG, "getInstance失败:传入Context为null"); + throw new IllegalArgumentException("Context cannot be null"); + } sInstance = new AppCacheUtils(context.getApplicationContext()); - LogUtils.d(TAG, "getInstance():单例实例已初始化"); + LogUtils.d(TAG, "getInstance:单例实例初始化完成"); } return sInstance; } - // ===================== 构造方法区(私有) ===================== + // ===================== 私有构造方法区(禁止外部实例化) ===================== /** - * 私有构造方法,禁止外部实例化 + * 私有构造方法,初始化缓存列表并加载持久化数据 * @param context ApplicationContext */ private AppCacheUtils(Context context) { - LogUtils.d(TAG, "AppCacheUtils() 构造方法调用"); + LogUtils.d(TAG, "AppCacheUtils构造方法调用"); mContext = context; mBatteryInfoList = new ArrayList(); loadAppCacheData(); - LogUtils.d(TAG, "AppCacheUtils() 构造完成,初始电池信息数量:" + mBatteryInfoList.size()); + LogUtils.d(TAG, String.format("AppCacheUtils构造完成 | 初始电池信息数量=%d", mBatteryInfoList.size())); } - // ===================== 公共业务方法区 ===================== + // ===================== 公共业务方法区(对外暴露接口) ===================== /** * 添加电池电量变化记录(仅当电量变化时添加) * @param batteryValue 电池电量值 */ public void addChangingTime(int batteryValue) { - LogUtils.d(TAG, "addChangingTime() 调用,传入电量值:" + batteryValue); + LogUtils.d(TAG, String.format("addChangingTime调用 | 传入电量值=%d", batteryValue)); + if (mBatteryInfoList.isEmpty()) { addChangingTimeToList(batteryValue); - LogUtils.d(TAG, "addChangingTime():缓存列表为空,直接添加记录"); + LogUtils.d(TAG, "addChangingTime:缓存列表为空,直接添加记录"); return; } + // 对比最后一条记录的电量值,避免重复添加 int lastBatteryValue = mBatteryInfoList.get(mBatteryInfoList.size() - 1).getBatteryValue(); if (lastBatteryValue != batteryValue) { addChangingTimeToList(batteryValue); - LogUtils.d(TAG, "addChangingTime():电量变化,添加新记录(原电量:" + lastBatteryValue + ",新电量:" + batteryValue + ")"); + LogUtils.d(TAG, String.format("addChangingTime:电量变化,添加新记录 | 原电量=%d | 新电量=%d", lastBatteryValue, batteryValue)); } else { - LogUtils.d(TAG, "addChangingTime():电量未变化,跳过添加"); + LogUtils.d(TAG, "addChangingTime:电量未变化,跳过添加"); } } @@ -78,7 +86,7 @@ public class AppCacheUtils { * @return 完整的电池信息列表 */ public ArrayList getArrayListBatteryInfo() { - LogUtils.d(TAG, "getArrayListBatteryInfo() 调用,当前缓存数量:" + mBatteryInfoList.size()); + LogUtils.d(TAG, String.format("getArrayListBatteryInfo调用 | 当前缓存数量=%d", mBatteryInfoList.size())); loadAppCacheData(); return mBatteryInfoList; } @@ -87,27 +95,29 @@ public class AppCacheUtils { * 清除所有电池历史记录 */ public void clearBatteryHistory() { - LogUtils.d(TAG, "clearBatteryHistory() 调用,清除前缓存数量:" + mBatteryInfoList.size()); + LogUtils.d(TAG, String.format("clearBatteryHistory调用 | 清除前缓存数量=%d", mBatteryInfoList.size())); mBatteryInfoList.clear(); saveAppCacheData(); - LogUtils.d(TAG, "clearBatteryHistory() 完成,缓存已清空"); + LogUtils.d(TAG, "clearBatteryHistory完成 | 缓存已清空"); } - // ===================== 私有辅助方法区 ===================== + // ===================== 私有辅助方法区(内部业务逻辑) ===================== /** * 内部方法:添加电量记录到列表并持久化 * @param batteryValue 电池电量值 */ private void addChangingTimeToList(int batteryValue) { - LogUtils.d(TAG, "addChangingTimeToList() 调用,传入电量值:" + batteryValue); - // 限制列表最大长度为180条,避免内存溢出 - if (mBatteryInfoList.size() > 180) { + LogUtils.d(TAG, String.format("addChangingTimeToList调用 | 传入电量值=%d", batteryValue)); + + // 限制列表最大长度,避免内存溢出 + if (mBatteryInfoList.size() >= MAX_BATTERY_RECORD_COUNT) { 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); mBatteryInfoList.add(batteryInfo); - LogUtils.d(TAG, "addChangingTimeToList():添加新记录 - 电量:" + batteryInfo.getBatteryValue() + ",时间戳:" + batteryInfo.getTimeStamp()); + LogUtils.d(TAG, String.format("addChangingTimeToList:添加新记录 | 电量=%d | 时间戳=%d", batteryInfo.getBatteryValue(), batteryInfo.getTimeStamp())); saveAppCacheData(); } @@ -115,19 +125,19 @@ public class AppCacheUtils { * 从文件加载缓存数据 */ private void loadAppCacheData() { - LogUtils.d(TAG, "loadAppCacheData() 调用,开始加载持久化数据"); + LogUtils.d(TAG, "loadAppCacheData调用 | 开始加载持久化数据"); mBatteryInfoList.clear(); BatteryInfoBean.loadBeanList(mContext, mBatteryInfoList, BatteryInfoBean.class); - LogUtils.d(TAG, "loadAppCacheData() 完成,加载数据数量:" + mBatteryInfoList.size()); + LogUtils.d(TAG, String.format("loadAppCacheData完成 | 加载数据数量=%d", mBatteryInfoList.size())); } /** * 保存缓存数据到文件 */ private void saveAppCacheData() { - LogUtils.d(TAG, "saveAppCacheData() 调用,保存数据数量:" + mBatteryInfoList.size()); + LogUtils.d(TAG, String.format("saveAppCacheData调用 | 保存数据数量=%d", mBatteryInfoList.size())); BatteryInfoBean.saveBeanList(mContext, mBatteryInfoList, BatteryInfoBean.class); - LogUtils.d(TAG, "saveAppCacheData() 完成,数据已持久化"); + LogUtils.d(TAG, "saveAppCacheData完成 | 数据已持久化"); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java index f4e0e40..041e9b7 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java @@ -7,10 +7,10 @@ import cc.winboll.studio.powerbell.models.AppConfigBean; import cc.winboll.studio.powerbell.models.ControlCenterServiceBean; /** - * @Author ZhanGSKen&豆包大模型 - * @Date 2025/12/17 13:59 - * @Describe 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等) + * 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等) * 适配:Java7 | API30 | 小米手机,单例模式,线程安全,配置持久化 + * @Author 豆包&ZhanGSKen + * @Describe 应用配置全量管理工具,支持配置持久化、自动校准、线程安全访问 */ public class AppConfigUtils { // ======================== 静态常量区(魔法值统一管理)======================== @@ -21,68 +21,75 @@ public class AppConfigUtils { private static final int MIN_INTERVAL_TIME = 1000; // 最小提醒间隔(ms) private static final int MIN_DETECT_INTERVAL = 500; // 最小电量检测间隔(ms) - // ======================== 静态属性区(单例实例)======================== - private static AppConfigUtils sInstance; // 单例实例(私有,禁止外部直接创建) + // ======================== 静态成员区(单例实例)======================== + private static volatile AppConfigUtils sInstance; // 单例实例(volatile保障双重校验锁有效性) - // ======================== 成员属性区(按依赖优先级排序)======================== - private final Context mContext; // 应用上下文(避免内存泄漏,final保障) - private App mApplication; // 应用Application实例 + // ======================== 成员变量区(按依赖优先级排序,final/volatile保障线程安全)======================== + private final Context mContext; // 应用上下文(ApplicationContext,避免内存泄漏) + private final App mApplication; // 应用Application实例(final保障不可变) public volatile AppConfigBean mAppConfigBean; // 应用配置Bean(持久化核心,volatile保障线程安全) 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) * @return 单例实例 */ 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) { - LogUtils.e(TAG, "getInstance() Context不能为空,获取实例失败"); + LogUtils.e(TAG, "getInstance失败:Context不能为空"); throw new IllegalArgumentException("Context cannot be null"); } + if (sInstance == null) { synchronized (AppConfigUtils.class) { if (sInstance == null) { sInstance = new AppConfigUtils(context); - LogUtils.d(TAG, "getInstance() 单例实例创建成功"); + LogUtils.d(TAG, "getInstance:单例实例创建成功"); } } } - LogUtils.d(TAG, "getInstance() 单例实例获取成功"); + + LogUtils.d(TAG, "getInstance:单例实例获取成功"); 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 */ public AppConfigBean loadAppConfig() { - LogUtils.d(TAG, "loadAppConfig() 开始加载应用配置"); + LogUtils.d(TAG, "loadAppConfig调用:开始加载应用配置"); AppConfigBean savedAppBean = (AppConfigBean) AppConfigBean.loadBean(mContext, AppConfigBean.class); + if (savedAppBean != null) { mAppConfigBean = savedAppBean; - LogUtils.d(TAG, "loadAppConfig() 应用配置加载成功,阈值:充电" + mAppConfigBean.getChargeReminderValue() + "%,耗电" + mAppConfigBean.getUsageReminderValue() + "%"); + LogUtils.d(TAG, String.format("loadAppConfig成功 | 充电阈值=%d%% | 耗电阈值=%d%%", + mAppConfigBean.getChargeReminderValue(), mAppConfigBean.getUsageReminderValue())); } else { mAppConfigBean = new AppConfigBean(); AppConfigBean.saveBean(mContext, mAppConfigBean); - LogUtils.d(TAG, "loadAppConfig() 无已保存配置,使用默认值并持久化"); + LogUtils.d(TAG, "loadAppConfig:无已保存配置,使用默认值并持久化"); } + return mAppConfigBean; } @@ -91,7 +98,7 @@ public class AppConfigUtils { */ private void saveAppConfig() { AppConfigBean.saveBean(mContext, mAppConfigBean); - LogUtils.d(TAG, "saveAppConfig() 应用配置保存成功"); + LogUtils.d(TAG, "saveAppConfig:应用配置保存成功"); } // ======================== 充电提醒配置方法区(开关+阈值)======================== @@ -100,14 +107,16 @@ public class AppConfigUtils { * @param isEnabled 目标状态(true=开启,false=关闭) */ public void setChargeReminderEnabled(final boolean isEnabled) { - LogUtils.d(TAG, "setChargeReminderEnabled() 调用,传入状态:" + isEnabled); + LogUtils.d(TAG, String.format("setChargeReminderEnabled调用 | 传入状态=%b", isEnabled)); + if (isEnabled == mAppConfigBean.isEnableChargeReminder()) { - LogUtils.d(TAG, "setChargeReminderEnabled() 充电提醒状态无变化,无需操作"); + LogUtils.d(TAG, "setChargeReminderEnabled:充电提醒状态无变化,无需操作"); return; } + mAppConfigBean.setEnableChargeReminder(isEnabled); saveAppConfig(); - LogUtils.d(TAG, "setChargeReminderEnabled() 充电提醒状态更新为:" + (isEnabled ? "开启" : "关闭")); + LogUtils.d(TAG, String.format("setChargeReminderEnabled成功 | 充电提醒状态=%s", isEnabled ? "开启" : "关闭")); } /** @@ -116,7 +125,7 @@ public class AppConfigUtils { */ public boolean isChargeReminderEnabled() { boolean isEnabled = mAppConfigBean.isEnableChargeReminder(); - LogUtils.d(TAG, "isChargeReminderEnabled() 获取充电提醒状态:" + (isEnabled ? "开启" : "关闭")); + LogUtils.d(TAG, String.format("isChargeReminderEnabled:获取充电提醒状态=%s", isEnabled ? "开启" : "关闭")); return isEnabled; } @@ -125,15 +134,17 @@ public class AppConfigUtils { * @param 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); + if (calibratedValue == mAppConfigBean.getChargeReminderValue()) { - LogUtils.d(TAG, "setChargeReminderValue() 充电提醒阈值无变化,无需操作"); + LogUtils.d(TAG, "setChargeReminderValue:充电提醒阈值无变化,无需操作"); return; } + mAppConfigBean.setChargeReminderValue(calibratedValue); saveAppConfig(); - LogUtils.d(TAG, "setChargeReminderValue() 充电提醒阈值更新为:" + calibratedValue + "%"); + LogUtils.d(TAG, String.format("setChargeReminderValue成功 | 充电提醒阈值=%d%%", calibratedValue)); } /** @@ -142,7 +153,7 @@ public class AppConfigUtils { */ public int getChargeReminderValue() { int value = mAppConfigBean.getChargeReminderValue(); - LogUtils.d(TAG, "getChargeReminderValue() 获取充电提醒阈值:" + value + "%"); + LogUtils.d(TAG, String.format("getChargeReminderValue:获取充电提醒阈值=%d%%", value)); return value; } @@ -152,14 +163,16 @@ public class AppConfigUtils { * @param isEnabled 目标状态(true=开启,false=关闭) */ public void setUsageReminderEnabled(final boolean isEnabled) { - LogUtils.d(TAG, "setUsageReminderEnabled() 调用,传入状态:" + isEnabled); + LogUtils.d(TAG, String.format("setUsageReminderEnabled调用 | 传入状态=%b", isEnabled)); + if (isEnabled == mAppConfigBean.isEnableUsageReminder()) { - LogUtils.d(TAG, "setUsageReminderEnabled() 耗电提醒状态无变化,无需操作"); + LogUtils.d(TAG, "setUsageReminderEnabled:耗电提醒状态无变化,无需操作"); return; } + mAppConfigBean.setEnableUsageReminder(isEnabled); saveAppConfig(); - LogUtils.d(TAG, "setUsageReminderEnabled() 耗电提醒状态更新为:" + (isEnabled ? "开启" : "关闭")); + LogUtils.d(TAG, String.format("setUsageReminderEnabled成功 | 耗电提醒状态=%s", isEnabled ? "开启" : "关闭")); } /** @@ -168,7 +181,7 @@ public class AppConfigUtils { */ public boolean isUsageReminderEnabled() { boolean isEnabled = mAppConfigBean.isEnableUsageReminder(); - LogUtils.d(TAG, "isUsageReminderEnabled() 获取耗电提醒状态:" + (isEnabled ? "开启" : "关闭")); + LogUtils.d(TAG, String.format("isUsageReminderEnabled:获取耗电提醒状态=%s", isEnabled ? "开启" : "关闭")); return isEnabled; } @@ -177,15 +190,17 @@ public class AppConfigUtils { * @param 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); + if (calibratedValue == mAppConfigBean.getUsageReminderValue()) { - LogUtils.d(TAG, "setUsageReminderValue() 耗电提醒阈值无变化,无需操作"); + LogUtils.d(TAG, "setUsageReminderValue:耗电提醒阈值无变化,无需操作"); return; } + mAppConfigBean.setUsageReminderValue(calibratedValue); saveAppConfig(); - LogUtils.d(TAG, "setUsageReminderValue() 耗电提醒阈值更新为:" + calibratedValue + "%"); + LogUtils.d(TAG, String.format("setUsageReminderValue成功 | 耗电提醒阈值=%d%%", calibratedValue)); } /** @@ -194,7 +209,7 @@ public class AppConfigUtils { */ public int getUsageReminderValue() { int value = mAppConfigBean.getUsageReminderValue(); - LogUtils.d(TAG, "getUsageReminderValue() 获取耗电提醒阈值:" + value + "%"); + LogUtils.d(TAG, String.format("getUsageReminderValue:获取耗电提醒阈值=%d%%", value)); return value; } @@ -204,13 +219,15 @@ public class AppConfigUtils { * @param isCharging 充电状态(true=充电中,false=未充电) */ public void setCharging(boolean isCharging) { - LogUtils.d(TAG, "setCharging() 调用,传入状态:" + isCharging); + LogUtils.d(TAG, String.format("setCharging调用 | 传入状态=%b", isCharging)); + if (isCharging == mAppConfigBean.isCharging()) { - LogUtils.d(TAG, "setCharging() 充电状态无变化,无需操作"); + LogUtils.d(TAG, "setCharging:充电状态无变化,无需操作"); return; } + 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() { boolean isCharging = mAppConfigBean.isCharging(); - LogUtils.d(TAG, "isCharging() 获取充电状态:" + (isCharging ? "充电中" : "未充电")); + LogUtils.d(TAG, String.format("isCharging:获取充电状态=%s", isCharging ? "充电中" : "未充电")); return isCharging; } @@ -228,14 +245,16 @@ public class AppConfigUtils { * @param 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); + if (calibratedValue == mAppConfigBean.getCurrentBatteryValue()) { - LogUtils.d(TAG, "setCurrentBatteryValue() 电池电量无变化,无需操作"); + LogUtils.d(TAG, "setCurrentBatteryValue:电池电量无变化,无需操作"); return; } + 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() { int value = mAppConfigBean.getCurrentBatteryValue(); - LogUtils.d(TAG, "getCurrentBatteryValue() 获取电池电量:" + value + "%"); + LogUtils.d(TAG, String.format("getCurrentBatteryValue:获取电池电量=%d%%", value)); return value; } @@ -254,15 +273,17 @@ public class AppConfigUtils { * @param interval 目标间隔(单位:ms) */ 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); + if (calibratedInterval == mAppConfigBean.getReminderIntervalTime()) { - LogUtils.d(TAG, "setReminderIntervalTime() 提醒间隔无变化,无需操作"); + LogUtils.d(TAG, "setReminderIntervalTime:提醒间隔无变化,无需操作"); return; } + mAppConfigBean.setReminderIntervalTime(calibratedInterval); 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() { int interval = mAppConfigBean.getReminderIntervalTime(); - LogUtils.d(TAG, "getReminderIntervalTime() 获取提醒间隔:" + interval + "ms"); + LogUtils.d(TAG, String.format("getReminderIntervalTime:获取提醒间隔=%dms", interval)); return interval; } @@ -280,15 +301,17 @@ public class AppConfigUtils { * @param interval 目标间隔(单位:ms) */ 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); + if (calibratedInterval == mAppConfigBean.getBatteryDetectInterval()) { - LogUtils.d(TAG, "setBatteryDetectInterval() 检测间隔无变化,无需操作"); + LogUtils.d(TAG, "setBatteryDetectInterval:检测间隔无变化,无需操作"); return; } + mAppConfigBean.setBatteryDetectInterval(calibratedInterval); 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() { int interval = mAppConfigBean.getBatteryDetectInterval(); - LogUtils.d(TAG, "getBatteryDetectInterval() 获取电量检测间隔:" + interval + "ms"); + LogUtils.d(TAG, String.format("getBatteryDetectInterval:获取电量检测间隔=%dms", interval)); return interval; } @@ -307,15 +330,16 @@ public class AppConfigUtils { * @return 服务开关状态(true=开启,false=关闭) */ public boolean isServiceEnabled() { - LogUtils.d(TAG, "isServiceEnabled() 开始获取服务开关状态"); + LogUtils.d(TAG, "isServiceEnabled调用:开始获取服务开关状态"); ControlCenterServiceBean savedServiceBean = (ControlCenterServiceBean) ControlCenterServiceBean.loadBean(mContext, ControlCenterServiceBean.class); + if (savedServiceBean != null) { boolean isEnabled = savedServiceBean.isEnableService(); - LogUtils.d(TAG, "isServiceEnabled() 服务开关状态:" + isEnabled); + LogUtils.d(TAG, String.format("isServiceEnabled:服务开关状态=%b", isEnabled)); return isEnabled; } else { ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(false)); - LogUtils.d(TAG, "isServiceEnabled() 无已保存服务配置,默认关闭并持久化"); + LogUtils.d(TAG, "isServiceEnabled:无已保存服务配置,默认关闭并持久化"); return false; } } @@ -325,9 +349,9 @@ public class AppConfigUtils { * @param isServiceEnabled 目标状态(true=开启,false=关闭) */ public void setIsServiceEnabled(boolean isServiceEnabled) { - LogUtils.d(TAG, "setIsServiceEnabled() 调用,传入状态:" + isServiceEnabled); + LogUtils.d(TAG, String.format("setIsServiceEnabled调用 | 传入状态=%b", isServiceEnabled)); ControlCenterServiceBean.saveBean(mContext, new ControlCenterServiceBean(isServiceEnabled)); - LogUtils.d(TAG, "setIsServiceEnabled() 服务开关状态更新为:" + isServiceEnabled); + LogUtils.d(TAG, String.format("setIsServiceEnabled成功 | 服务开关状态=%b", isServiceEnabled)); } }