源码整理

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

View File

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

View File

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

View File

@@ -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&豆包大模型<zhangsken@qq.com>
* @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<ControlCenterServiceBean> CREATOR = new Parcelable.Creator<ControlCenterServiceBean>() {
@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 标准实现)
}
/**
* 序列化对象到 ParcelIntent 传递必备Java7 适配)
* 序列化对象到 ParcelIntent 传递必备Java7 适配:用 byte 存储 boolean
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
LogUtils.d(TAG, "writeToParcel: 开始将对象序列化到Parcelflags=" + flags);
// Java7 + API30 适配Parcel 无 writeBoolean 方法,用 byte 存储1=true0=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));
}
}

View File

@@ -1,36 +1,75 @@
package cc.winboll.studio.powerbell.models;
import cc.winboll.studio.libappbase.LogUtils;
/**
* 通知数据模型:统一存储通知标题、内容等信息,适配各组件数据传递
* 通知数据模型
* 适配 API30统一存储通知标题、内容、标识信息支持各组件数据传递
* @Author ZhanGSKen<zhangsken@qq.com>
* @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;
}
}

View File

@@ -14,14 +14,14 @@ import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
import java.lang.ref.WeakReference;
/**
* 控制中心广播接收器
* 功能:监听电池状态变化、前台通知更新、配置变更指令
* 适配Java7 | API30 | 内存泄漏防护 | 多线程状态同步
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @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<ControlCenterService> 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;
}
}

View File

@@ -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;

View File

@@ -1,10 +1,5 @@
package cc.winboll.studio.powerbell.receivers;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2024/06/06 15:01:39
* @Describe 应用广播消息接收类
*/
import android.content.BroadcastReceiver;
import android.content.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<zhangsken@qq.com>
* @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);
}
}
}

View File

@@ -16,21 +16,25 @@ import cc.winboll.studio.powerbell.utils.ServiceUtils;
* 电池提醒核心服务进程守护类
* 功能:监听主服务 {@link ControlCenterService} 存活状态,异常断开时自动重启并绑定
* 适配Java7 | API30 | 前台服务启动规则 | 服务绑定稳定性保障
* @Author ZhanGSKen<zhangsken@qq.com>
* @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;
}

View File

@@ -24,22 +24,30 @@ import java.util.List;
* 电池提醒核心服务
* 功能:管理前台服务生命周期、控制提醒线程启停、处理配置更新
* 适配Java7 | API30 | 前台服务超时防护 | 电池优化忽略引导
* @Author ZhanGSKen<zhangsken@qq.com>
* @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, "startControlCenterServiceContext为空启动失败");
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, "stopControlCenterServiceContext为空停止失败");
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<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
if (processes != null) {
@@ -418,7 +427,7 @@ public class ControlCenterService extends Service {
}
}
}
LogUtils.d(TAG, "isServiceRunningAPI30+ 判断结果=" + isRunning);
LogUtils.d(TAG, String.format("isServiceRunningAPI30+ 判断结果=%b", isRunning));
} else {
// API30- 通过服务列表判断
List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(RUNNING_SERVICE_LIST_LIMIT);
@@ -430,13 +439,13 @@ public class ControlCenterService extends Service {
}
}
}
LogUtils.d(TAG, "isServiceRunningAPI30- 判断结果=" + isRunning);
LogUtils.d(TAG, String.format("isServiceRunningAPI30- 判断结果=%b", isRunning));
}
// 兜底判断:配置启用状态
if (!isRunning) {
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;
}

View File

@@ -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<zhangsken@qq.com>
* @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<RemindThread> 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{" +

View File

@@ -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&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/22 08:31
* @Describe MainUnitTest2Activity
* 单元测试页面2内存缓存背景视图专用
* 功能测试MemoryCachedBackgroundView加载、图片裁剪、双重刷新预览等功能
* 适配Java7 | API30 | 私有目录文件操作 | 无Uri冲突 | 内存缓存视图
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @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;
}
}

View File

@@ -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<zhangsken@qq.com>
* @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;
}
}

View File

@@ -11,19 +11,18 @@ import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/26 15:54
* @Describe 应用图标切换工具类(启用组件时创建对应快捷方式)
* 应用图标切换工具类(启用组件时创建对应快捷方式)
* 适配Java7 | API30 | 高低版本快捷方式创建兼容
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @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, "createComponentShortcutAndroid 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, "createComponentShortcutAndroid 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());
}
}
}

View File

@@ -8,68 +8,76 @@ import java.util.ArrayList;
/**
* 应用缓存工具类适配Android API 30基于Java 7编写
* 负责电池信息的缓存、持久化与管理
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @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<BatteryInfoBean> mBatteryInfoList;
// ===================== 成员变量区(按功能分层) =====================
private Context mContext; // ApplicationContext避免内存泄漏
private ArrayList<BatteryInfoBean> 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<BatteryInfoBean>();
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<BatteryInfoBean> 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完成 | 数据已持久化");
}
}

View File

@@ -7,10 +7,10 @@ import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 13:59
* @Describe 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等)
* 应用配置工具类:管理应用核心配置(服务开关、电池提醒阈值、背景设置等)
* 适配Java7 | API30 | 小米手机,单例模式,线程安全,配置持久化
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @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));
}
}