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