static Object b(int i2, int i3, long j, String str, Object obj) { ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect; if (PatchProxy.isEnable(changeQuickRedirect2)) { PatchProxyResult proxy = PatchProxy.proxy(new Object[]{Integer.valueOf(i2), Integer.valueOf(i3), new Long(j), str, obj}, null, changeQuickRedirect2, true, 1); if (proxy.isSupported) { return proxy.result; } } return b.b(i2, i3, j, str, obj);

时间: 2024-04-16 11:25:57 AIGC 浏览: 134
这段代码是一个静态方法 b,它接受五个参数:i2、i3、j、str 和 obj。首先它判断是否启用了 PatchProxy,并创建了一个 ChangeQuickRedirect 对象 changeQuickRedirect2。如果启用了 PatchProxy,则调用 PatchProxy.proxy 方法,并传入参数数组和 changeQuickRedirect2 对象。如果返回的 proxy 对象的 isSupported 属性为 true,则返回 proxy 对象的 result 属性;否则,调用 b.b 方法,并传入相同的参数,返回其结果。
相关问题

public static native Object a(int i2, int i3, long j, String str, Object obj); 这个是什么意思

这段代码定义了一个名为 `a` 的本地方法(Native Method)。本地方法是在Java代码中声明但实现是由本地代码(通常是C或C++)提供的方法。 在这个例子中,`a` 方法接受五个参数:i2、i3、j、str 和 obj。由于这是一个本地方法,它的具体实现不是在Java代码中定义的,而是由本地代码实现。这段代码告诉编译器,在运行时可以在本地代码中找到一个名为 `a` 的方法,并且可以通过Java代码调用它。 在使用本地方法时,通常需要编写相应的本地代码来实现该方法,然后将其与Java代码进行绑定。本地代码的实现通常使用C或C++编写,并通过JNI(Java Native Interface)与Java代码进行交互。 总结一下,这段代码声明了一个本地方法 `a`,其具体实现位于本地代码中,可以通过JNI与Java代码进行交互和调用。

package com.example.kucun2.entity.data; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class ReflectionJsonUtils { /** * 将任意Java对象转换为JSON字符串 * * @param obj 需要转换的对象 * @return JSON格式字符串 */ public static String toJson(Object obj) { if (obj == null) { return "null"; } Class<?> clazz = obj.getClass(); // 处理基础类型 if (isPrimitiveOrWrapper(clazz) || clazz == String.class) { return handlePrimitive(obj); } // 处理数组 if (clazz.isArray()) { return handleArray(obj); } // 处理集合 if (Collection.class.isAssignableFrom(clazz)) { return handleCollection((Collection<?>) obj); } // 处理Map if (Map.class.isAssignableFrom(clazz)) { return handleMap((Map<?, ?>) obj); } // 处理自定义对象 return handleObject(obj); } /** * 处理基本数据类型和字符串 */ private static String handlePrimitive(Object obj) { if (obj == null) return "null"; Class<?> clazz = obj.getClass(); if (clazz == String.class) { return "\"" + escapeString((String) obj) + "\""; } if (clazz == Character.class || clazz == char.class) { return "\"" + escapeChar((Character) obj) + "\""; } return obj.toString(); } /** * 处理数组 */ private static String handleArray(Object array) { Class<?> componentType = array.getClass().getComponentType(); int length = java.lang.reflect.Array.getLength(array); StringBuilder sb = new StringBuilder("["); for (int i = 0; i < length; i++) { Object element = java.lang.reflect.Array.get(array, i); sb.append(toJson(element)); if (i < length - 1) { sb.append(","); } } sb.append("]"); return sb.toString(); } /** * 处理集合 */ private static String handleCollection(Collection<?> collection) { StringBuilder sb = new StringBuilder("["); int index = 0; int size = collection.size(); for (Object element : collection) { sb.append(toJson(element)); if (index < size - 1) { sb.append(","); } index++; } sb.append("]"); return sb.toString(); } /** * 处理Map */ private static String handleMap(Map<?, ?> map) { StringBuilder sb = new StringBuilder("{"); int index = 0; int size = map.size(); for (Map.Entry<?, ?> entry : map.entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); if (!(key instanceof String) && !isPrimitiveOrWrapper(key.getClass())) { throw new IllegalArgumentException("Map keys must be strings or primitives for JSON conversion"); } String keyStr = key instanceof String ? (String) key : key.toString(); sb.append("\"").append(escapeString(keyStr)).append("\":"); sb.append(toJson(value)); if (index < size - 1) { sb.append(","); } index++; } sb.append("}"); return sb.toString(); } /** * 处理自定义对象 */ private static String handleObject(Object obj) { Class<?> clazz = obj.getClass(); Field[] fields = getAllFields(clazz); StringBuilder sb = new StringBuilder("{"); int index = 0; for (Field field : fields) { // 跳过静态字段 if (Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); String fieldName = field.getName(); Object fieldValue = null; try { fieldValue = field.get(obj); } catch (IllegalAccessException e) { // 忽略访问异常,字段值保持null // Log.e("ReflectionJsonUtils", "handleObject: " + e.getLocalizedMessage()); } sb.append("\"").append(escapeString(fieldName)).append("\":"); if (SynchronizableEntity.class.isAssignableFrom(field.getType()) && fieldValue != null && ((SynchronizableEntity) fieldValue).getId() != null) { try { // Log.d("ReflectionJsonUtils", "isAssignableFrom: " + sb); sb.append("{\"id\":") .append(((SynchronizableEntity) fieldValue).getId()) .append("}"); } catch (Exception e) { sb.append(toJson(fieldValue)); } } else { sb.append(toJson(fieldValue)); } if (index < fields.length - 1) { sb.append(","); } index++; } sb.append("}"); // Log.d("ReflectionJsonUtils", "handleObject: " + sb); return sb.toString(); } // 获取类及其父类的所有字段 private static Field[] getAllFields(Class<?> type) { List<Field> fields = new ArrayList<>(); while (type != null && type != Object.class) { Field[] declaredFields = type.getDeclaredFields(); for (Field field : declaredFields) { // 跳过静态字段 if (!Modifier.isStatic(field.getModifiers())) { fields.add(field); } } type = type.getSuperclass(); } return fields.toArray(new Field[0]); } // 检查是否基本类型或其包装类 private static boolean isPrimitiveOrWrapper(Class<?> clazz) { return clazz.isPrimitive() || clazz == Boolean.class || clazz == Character.class || clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class || clazz == Float.class || clazz == Double.class || clazz == Void.class; } // 转义JSON字符串中的特殊字符 private static String escapeString(String str) { if (str == null) return ""; return str.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("/", "\\/") .replace("\b", "\\b") .replace("\f", "\\f") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } // 转义特殊字符 private static String escapeChar(Character ch) { if (ch == null) return ""; return escapeString(String.valueOf(ch)); } }有枚举类型字段

<think>我们注意到在提供的代码中,`ReflectionJsonUtils`类用于将Java对象转换为JSON字符串。但是,当前代码在处理枚举类型时可能会出现问题,因为代码中没有显式处理枚举类型的逻辑。在`toJson`方法中,我们检查了基本类型、字符串、数组、集合、Map和自定义对象,但没有检查枚举类型。枚举类型在Java中是一种特殊的类,它继承自`java.lang.Enum`。因此,我们需要在转换时处理枚举类型。通常,枚举类型在JSON中应该被转换为其名称的字符串形式(有时也可以是序号,但更常见的是名称)。因此,我们可以将枚举当作字符串来处理,但需要确保在转换时调用`name()`方法(或`toString()`,但通常使用`name()`更可靠)来获取枚举常量的名称。修改思路:1.在`toJson`方法中,添加对枚举类型的判断。我们可以通过`clazz.isEnum()`来判断一个类是否是枚举类型。2.如果是枚举类型,则调用`handleEnum`方法(或者直接在判断中处理),将其转换为字符串。我们可以这样修改:在`toJson`方法中,在检查基本类型之后,添加对枚举类型的处理:```java//处理枚举if(clazz.isEnum()){returnhandleEnum(obj);}```然后编写`handleEnum`方法:```java/***处理枚举类型*/privatestaticStringhandleEnum(ObjectenumObj){//枚举对象,调用name()方法获取枚举常量名称,并用双引号包围return"\""+((Enum)enumObj).name()+"\"";}```另外,我们还需要注意,在自定义对象处理(`handleObject`)中,如果某个字段是枚举类型,那么它会被当作自定义对象处理,从而进入`handleObject`方法,但实际上我们已经在`toJson`中处理了枚举,所以字段值为枚举类型时,会递归调用`toJson`,然后被识别为枚举并正确处理。但是,我们也要考虑枚举数组、枚举集合等情况。由于我们在`toJson`方法中已经对枚举类型做了处理,所以当数组或集合中的元素是枚举时,在`handleArray`和`handleCollection`中会调用`toJson`,从而正确转换。因此,我们只需要添加对枚举类型的判断和处理方法即可。修改后的代码:在`toJson`方法中添加枚举处理:```javapublicstaticStringtoJson(Objectobj){if(obj==null){return"null";}Class<?>clazz=obj.getClass();//处理基础类型if(isPrimitiveOrWrapper(clazz)||clazz==String.class){returnhandlePrimitive(obj);}//处理枚举if(clazz.isEnum()){returnhandleEnum(obj);}//处理数组if(clazz.isArray()){returnhandleArray(obj);}//处理集合if(Collection.class.isAssignableFrom(clazz)){returnhandleCollection((Collection<?>)obj);}//处理Mapif(Map.class.isAssignableFrom(clazz)){returnhandleMap((Map<?,?>)obj);}//处理自定义对象returnhandleObject(obj);}```添加`handleEnum`方法:```java/***处理枚举类型*/privatestaticStringhandleEnum(ObjectenumObj){if(enumObj==null){return"null";}//枚举对象,调用name()方法获取枚举常量名称return"\""+((Enum<?>)enumObj).name()+"\"";}```注意:在枚举转换中,我们返回的是带双引号的字符串,符合JSON字符串的要求。另外,在`handlePrimitive`方法中,我们处理了字符类型,但枚举类型不会进入该方法,因为枚举类型不是基本类型或包装类,也不是String,所以不会冲突。但是,我们还需要注意转义问题。枚举的名称通常只包含字母、数字和下划线,但为了安全起见,我们也可以对枚举名称进行转义。不过,枚举的name()方法返回的字符串是合法的Java标识符,通常不包含需要转义的字符。但为了通用性,我们可以使用`escapeString`方法进行转义。修改`handleEnum`方法:```javaprivatestaticStringhandleEnum(ObjectenumObj){if(enumObj==null){return"null";}StringenumName=((Enum<?>)enumObj).name();return"\""+escapeString(enumName)+"\"";}```这样,如果枚举名称中包含双引号等特殊字符(虽然这种情况很少见,因为枚举名称必须是合法的Java标识符,不能包含双引号),我们也能正确处理。此外,我们还需要考虑枚举类型在自定义对象中作为字段的情况。在`handleObject`方法中,我们有一个特殊的处理:如果字段类型是`SynchronizableEntity`的子类,并且字段值不为空且id不为空,则只序列化id。这个处理可能会影响枚举字段吗?不会,因为枚举类型不会继承`SynchronizableEntity`(除非有枚举显式继承,但枚举不能继承类,因为枚举隐式继承`Enum`,而Java不支持多继承)。所以枚举字段会按照普通字段处理,即调用`toJson`方法,然后进入枚举处理分支。因此,我们只需要添加上述代码即可支持枚举类型。最后,由于代码中使用了`SynchronizableEntity`,我们需要确保这个类在枚举处理中不会引起问题。因为枚举类型不会继承`SynchronizableEntity`,所以不会进入那个特殊分支。修改后的完整代码示例(仅展示修改部分):</think>您的 `ReflectionJsonUtils` 类实现了 Java 对象到 JSON 的转换,但缺少对枚举类型的支持。以下是针对枚举类型的修改方案: ### 修改后的代码 ```java package com.example.kucun2.entity.data; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class ReflectionJsonUtils { // ... 其他方法保持不变 ... /** * 将任意Java对象转换为JSON字符串(添加枚举支持) */ public static String toJson(Object obj) { if (obj == null) { return "null"; } Class<?> clazz = obj.getClass(); // 添加枚举类型处理 if (clazz.isEnum()) { return handleEnum((Enum<?>) obj); } // 其他类型处理保持不变... // [原有代码] } /** * 处理枚举类型 */ private static String handleEnum(Enum<?> enumObj) { return "\"" + escapeString(enumObj.name()) + "\""; } /** * 处理自定义对象(添加枚举字段支持) */ private static String handleObject(Object obj) { Class<?> clazz = obj.getClass(); Field[] fields = getAllFields(clazz); StringBuilder sb = new StringBuilder("{"); int index = 0; for (Field field : fields) { // 跳过静态字段 if (Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); String fieldName = field.getName(); Object fieldValue = null; try { fieldValue = field.get(obj); } catch (IllegalAccessException e) { // 忽略访问异常 } sb.append("\"").append(escapeString(fieldName)).append("\":"); // 添加枚举类型判断 if (fieldValue != null && field.getType().isEnum()) { sb.append(handleEnum((Enum<?>) fieldValue)); } // 原有SynchronizableEntity处理 else if (SynchronizableEntity.class.isAssignableFrom(field.getType()) && fieldValue != null && ((SynchronizableEntity) fieldValue).getId() != null) { try { sb.append("{\"id\":") .append(((SynchronizableEntity) fieldValue).getId()) .append("}"); } catch (Exception e) { sb.append(toJson(fieldValue)); } } else { sb.append(toJson(fieldValue)); } if (index < fields.length - 1) { sb.append(","); } index++; } sb.append("}"); return sb.toString(); } // ... 其他辅助方法保持不变 ... } ``` ### 关键修改说明: 1. **枚举类型检测**: ```java if (clazz.isEnum()) { return handleEnum((Enum<?>) obj); } ``` 在 `toJson()` 入口处检测枚举类型 2. **枚举处理方法**: ```java private static String handleEnum(Enum<?> enumObj) { return "\"" + escapeString(enumObj.name()) + "\""; } ``` 将枚举转换为带引号的字符串(如 `"RED"`) 3. **对象字段处理**: ```java if (fieldValue != null && field.getType().isEnum()) { sb.append(handleEnum((Enum<?>) fieldValue)); } ``` 在 `handleObject()` 中专门处理枚举字段 ### 使用示例: ```java enum Color { RED, GREEN, BLUE } class Product { String name = "Apple"; Color color = Color.RED; } // 转换结果: // {"name":"Apple","color":"RED"} ``` ### 处理逻辑: 1. **顶层枚举**:直接调用 `handleEnum()` 2. **对象内的枚举字段**:在反射字段时特殊处理 3. **集合中的枚举**:通过递归调用 `toJson()` 自动处理 4. **特殊字符转义**:使用现有的 `escapeString()` 方法处理引号等字符 ### 注意事项: 1. 枚举值始终转换为**字符串形式**(使用 `name()` 方法) 2. 枚举常量中的特殊字符(如引号)会被自动转义 3. 支持嵌套在各种数据结构中(数组/集合/Map/对象字段) ### 测试用例建议: ```java enum Status { ACTIVE, INACTIVE } class Test { public static void main(String[] args) { // 测试顶层枚举 System.out.println(toJson(Status.ACTIVE)); // "ACTIVE" // 测试对象中的枚举字段 User user = new User("Alice", Status.ACTIVE); System.out.println(toJson(user)); // {"name":"Alice","status":"ACTIVE"} // 测试枚举集合 List<Status> statuses = List.of(Status.ACTIVE, Status.INACTIVE); System.out.println(toJson(statuses)); // ["ACTIVE","INACTIVE"] } } ```
阅读全文

相关推荐

/* ========== Dingdan_Chanpin.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; /** 订单产品关联 */ public class Dingdan_Chanpin extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private Dingdan dingdan; private Chanpin chanpin; private Integer shuliang; public Dingdan_Chanpin(Integer id, Dingdan dingdan, Chanpin chanpin, Integer shuliang) { this.id = id; this.dingdan = dingdan; this.chanpin = chanpin; this.shuliang = shuliang; } @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { Dingdan oldValue = this.dingdan; this.dingdan = dingdan; firePropertyChange(“dingdan”, oldValue, dingdan); } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { Chanpin oldValue = this.chanpin; this.chanpin = chanpin; firePropertyChange(“chanpin”, oldValue, chanpin); } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { Integer oldValue = this.shuliang; this.shuliang = shuliang; firePropertyChange(“shuliang”, oldValue, shuliang); } public Dingdan_Chanpin() { } } /* ========== Dingdan_chanpin_zujian.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; /** 、 订单板材关联 */ public class Dingdan_chanpin_zujian extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private Dingdan dingdian; private Chanpin_Zujian zujian; private Bancai bancai; private Integer shuliang; @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public Dingdan getDingdian() { return dingdian; } public void setDingdian(Dingdan dingdian) { Dingdan oldValue = this.dingdian; this.dingdian = dingdian; firePropertyChange(“dingdian”, oldValue, dingdian); } public Chanpin_Zujian getZujian() { return zujian; } public void setZujian(Chanpin_Zujian zujian) { Chanpin_Zujian oldValue = this.zujian; this.zujian = zujian; firePropertyChange(“zujian”, oldValue, zujian); } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { Bancai oldValue = this.bancai; this.bancai = bancai; firePropertyChange(“bancai”, oldValue, bancai); } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { Integer oldValue = this.shuliang; this.shuliang = shuliang; firePropertyChange(“shuliang”, oldValue, shuliang); } public Dingdan_chanpin_zujian() { } } /* ========== Information.java ========== */ package com.example.kucun2.entity; import android.annotation.SuppressLint; import androidx.annotation.Keep; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; @Keep // 防止Proguard混淆 public class Information<T> { private Integer status; private String text; private T data; // 使用泛型保证类型安全 private static final Gson gson = new Gson(); // Gson实例复用 // 构造方法 public Information(Integer status, String text, T data) { this.status = status; this.text = text; this.data = data; } // JSON反序列化构造方法 @SuppressLint(“NewApi”) public Information(Integer status, String text, String jsonData, Type dataType) { this.status = status; this.text = text; try { this.data = gson.fromJson(jsonData, dataType); } catch (JsonSyntaxException e) { this.data = null; // 处理解析失败 } } // 空构造方法 public Information() { } // 序列化为JSON public String toJson() { return gson.toJson(this); } // 静态创建方法 public static <T> Information<T> newSuccess(T data) { return new Information<>(200, “success”, data); } public static Information<String> newSuccess(String text) { return new Information<>(200, “success”, text); } public static <T> Information<T> newFail(int status, String text, T data) { return new Information<>(status, text, data); } // Getter/Setter public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public String getText() { return text; } public void setText(String text) { this.text = text; } public T getData() { return data; } public void setData(T data) { this.data = data; } } /* ========== Jinhuo.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; import java.util.Date; /** 进货消耗类 */ public class Jinhuo extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; //属于哪个订单 private Dingdan dingdan; //属于哪个产品 private Chanpin chanpin; //属于哪个组件 private Zujian zujian; //进的什么板材 private Bancai bancai; //进货数量 private Integer shuliang; private Date date; private User user; public Jinhuo(Integer id, Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, Integer shuliang, Date date, User user) { super(); this.id = id; this.dingdan = dingdan; this.chanpin = chanpin; this.zujian = zujian; this.bancai = bancai; this.shuliang = shuliang; this.date = date; this.user = user; } public Jinhuo() { super(); // TODO Auto-generated constructor stub } @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { Dingdan oldValue = this.dingdan; this.dingdan = dingdan; firePropertyChange(“dingdan”, oldValue, dingdan); } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { Chanpin oldValue = this.chanpin; this.chanpin = chanpin; firePropertyChange(“chanpin”, oldValue, chanpin); } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { Zujian oldValue = this.zujian; this.zujian = zujian; firePropertyChange(“zujian”, oldValue, zujian); } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { Bancai oldValue = this.bancai; this.bancai = bancai; firePropertyChange(“bancai”, oldValue, bancai); } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { Integer oldValue = this.shuliang; this.shuliang = shuliang; firePropertyChange(“shuliang”, oldValue, shuliang); } public Date getDate() { return date; } public void setDate(Date date) { Date oldValue = this.date; this.date = date; firePropertyChange(“date”, oldValue, date); } public User getUser() { return user; } public void setUser(User user) { User oldValue = this.user; this.user = user; firePropertyChange(“user”, oldValue, user); } } /* ========== Kucun.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; /** 库存 */ public class Kucun extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private Integer shuliang; private Bancai bancai; @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { Integer oldValue = this.shuliang; this.shuliang = shuliang; firePropertyChange(“shuliang”, oldValue, shuliang); } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { Bancai oldValue = this.bancai; this.bancai = bancai; firePropertyChange(“bancai”, oldValue, bancai); } public Kucun() { } public Kucun(Integer id, Integer shuliang, Bancai bancai) { this.id = id; this.shuliang = shuliang; this.bancai = bancai; } } /* ========== Mupi.java ========== */ package com.example.kucun2.entity; import android.annotation.SuppressLint; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; import java.util.ArrayList; import java.util.List; public class Mupi extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private String name; /** 是否有油漆 */ private Boolean you = false; private List<Bancai> bancais = new ArrayList<>(); @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public String getName() { return name; } public void setName(String name) { String oldValue = this.name; this.name = name; firePropertyChange(“name”, oldValue, name); } public Boolean getYou() { if (you == null) { you = false; return false; } return you; } public void setYou(Boolean you) { if (you == null) return; Boolean oldValue = this.you; this.you = you; firePropertyChange("you", oldValue, you); } public List<Bancai> getBancais() { return bancais; } public void setBancais(List<Bancai> bancais) { List<Bancai> oldValue = this.bancais; this.bancais = bancais; firePropertyChange(“bancais”, oldValue, bancais); } public Mupi() { } public Mupi(Integer id, String name, List<Bancai> bancais) { this.id = id; this.name = name; this.bancais = bancais; } public Mupi(List<Bancai> bancais, Boolean you, String name, Integer id) { this.bancais = bancais; this.you = you; this.name = name; this.id = id; } // 3. 木皮显示格式化方法 public String formatMupiDisplay() { return getName() + (getYou() ? "油" : ""); } } /* ========== User.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; /** 用户 */ public class User extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private String name; private String andy; private String pass; private Integer role; @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public String getName() { return name; } public void setName(String name) { String oldValue = this.name; this.name = name; firePropertyChange(“name”, oldValue, name); } public String getAndy() { return andy; } public void setAndy(String andy) { String oldValue = this.andy; this.andy = andy; firePropertyChange(“andy”, oldValue, andy); } public String getPass() { return pass; } public void setPass(String pass) { String oldValue = this.pass; this.pass = pass; firePropertyChange(“pass”, oldValue, pass); } public Integer getRole() { return role; } public void setRole(Integer role) { Integer oldValue = this.role; this.role = role; firePropertyChange(“role”, oldValue, role); } public User(int id, String name, String andy, String pass, int role) { this.id = id; this.name = name; this.andy = andy; this.pass = pass; this.role = role; } public User() { } @Override public String toString() { StringBuilder sb = new StringBuilder(“{”); // 处理属性名 sb.append("“id”: "); // 处理不同数据类型 // 其他对象类型 sb.append((id != null) ? id : “null”); sb.append(“,”); // 处理属性名 sb.append(""name": "); // 处理不同数据类型 // 字符串类型处理(含转义) sb.append(“"”) .append(name .replace(“\”, “\\”) .replace(“"”, “\"”) .replace(“\b”, “\b”) .replace(“\f”, “\f”) .replace(“\n”, “\n”) .replace(“\r”, “\r”) .replace(“\t”, “\t”)) .append(“"”); sb.append(“,”); // 处理属性名 sb.append(""andy": "); // 处理不同数据类型 // 字符串类型处理(含转义) sb.append(“"”) .append(andy .replace(“\”, “\\”) .replace(“"”, “\"”) .replace(“\b”, “\b”) .replace(“\f”, “\f”) .replace(“\n”, “\n”) .replace(“\r”, “\r”) .replace(“\t”, “\t”)) .append(“"”); sb.append(“,”); // 处理属性名 sb.append(""pass": "); // 处理不同数据类型 // 字符串类型处理(含转义) sb.append(“"”) .append(pass .replace(“\”, “\\”) .replace(“"”, “\"”) .replace(“\b”, “\b”) .replace(“\f”, “\f”) .replace(“\n”, “\n”) .replace(“\r”, “\r”) .replace(“\t”, “\t”)) .append(“"”); sb.append(“,”); // 处理属性名 sb.append(""role": "); // 处理不同数据类型 // 其他对象类型 sb.append((role != null) ? role : “null”); sb.append(“}”); return sb.toString(); } } /* ========== Zujian.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; /** 组件 */ public class Zujian extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private String name; public Zujian() { } public Zujian(Integer id, String name) { this.id = id; this.name = name; } @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public String getName() { return name; } public void setName(String name) { String oldValue = this.name; this.name = name; firePropertyChange(“name”, oldValue, name); } } /* ========== Bancai.java ========== */ package com.example.kucun2.entity; import android.annotation.SuppressLint; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; import java.util.Collections; import java.util.Map; import java.util.Objects; //板材 public class Bancai extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private Caizhi caizhi; private Mupi mupi1; private Mupi mupi2; private Double houdu; private Dingdan_chanpin_zujian Dingdan_chanpin_zujian; public Bancai() { } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bancai bancai = (Bancai) o; return Objects.equals(id, bancai.id) && Objects.equals(caizhi, bancai.caizhi) && Objects.equals(mupi1, bancai.mupi1) && Objects.equals(mupi2, bancai.mupi2) && Objects.equals(houdu, bancai.houdu); } @Override public int hashCode() { return Objects.hash(id, caizhi, mupi1, mupi2, houdu); } public Bancai(Integer id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { this.id = id; this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public Caizhi getCaizhi() { return caizhi; } public void setCaizhi(Caizhi caizhi) { Caizhi oldValue = this.caizhi; this.caizhi = caizhi; firePropertyChange(“caizhi”, oldValue, caizhi); } public Mupi getMupi1() { return mupi1; } public void setMupi1(Mupi mupi1) { Mupi oldValue = this.mupi1; this.mupi1 = mupi1; firePropertyChange(“mupi1”, oldValue, mupi1); } public Mupi getMupi2() { return mupi2; } public void setMupi2(Mupi mupi2) { Mupi oldValue = this.mupi2; this.mupi2 = mupi2; firePropertyChange(“mupi2”, oldValue, mupi2); } public Double getHoudu() { return houdu; } public void setHoudu(Double houdu) { Double oldValue = this.houdu; this.houdu = houdu; firePropertyChange(“houdu”, oldValue, houdu); } /** 表格中显示的文字 @return */ @SuppressLint(“DefaultLocale”) public String TableText() { String boardInfo = “”; boardInfo += String.format(“%.1f”, this.getHoudu()) + this.getCaizhi().getName() + “(”; if (mupi1 != null) { boardInfo += (this.getMupi1().getYou() ? this.getMupi1().getName() + “油” : this.getMupi1().getName()); } if (mupi2 != null && mupi1 != null) { boardInfo += “,”; } if (mupi2 != null) { boardInfo += (this.getMupi2().getYou() ? this.getMupi2().getName() + “油” : this.getMupi2().getName()); } boardInfo += “)”; return boardInfo; } public com.example.kucun2.entity.Dingdan_chanpin_zujian getDingdan_chanpin_zujian() { return Dingdan_chanpin_zujian; } public void setDingdan_chanpin_zujian(com.example.kucun2.entity.Dingdan_chanpin_zujian dingdan_chanpin_zujian) { com.example.kucun2.entity.Dingdan_chanpin_zujian oldValue = this.Dingdan_chanpin_zujian; Dingdan_chanpin_zujian = dingdan_chanpin_zujian; firePropertyChange(“dingdan_chanpin_zujian”, oldValue, dingdan_chanpin_zujian); } } /* ========== Caizhi.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; import java.util.ArrayList; import java.util.List; import java.util.Objects; //材质 public class Caizhi extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private String name; private List<Bancai> bancai = new ArrayList<>(); @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public String getName() { return name; } public void setName(String name) { String oldValue = this.name; this.name = name; firePropertyChange(“name”, oldValue, name); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Caizhi caizhi = (Caizhi) o; return Objects.equals(name, caizhi.name); } @Override public int hashCode() { return Objects.hashCode(name); } public List<Bancai> getBancai() { return bancai; } public void setBancai(List<Bancai> bancai) { List<Bancai> oldValue = this.bancai; this.bancai = bancai; firePropertyChange(“bancai”, oldValue, bancai); } } /* ========== Chanpin.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; import java.util.ArrayList; import java.util.List; //产品 public class Chanpin extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private String bianhao; private List<Chanpin_Zujian> chanpinZujian = new ArrayList<>(); private List<Dingdan_Chanpin> dingdanChanpin = new ArrayList<>(); ; @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public List<Chanpin_Zujian> getChanpinZujian() { return chanpinZujian; } public void setChanpinZujian(List<Chanpin_Zujian> chanpinZujian) { List<Chanpin_Zujian> oldValue = this.chanpinZujian; this.chanpinZujian = chanpinZujian; firePropertyChange(“chanpinZujian”, oldValue, chanpinZujian); } public List<Dingdan_Chanpin> getDingdanChanpin() { return dingdanChanpin; } public void setDingdanChanpin(List<Dingdan_Chanpin> dingdanChanpin) { List<Dingdan_Chanpin> oldValue = this.dingdanChanpin; this.dingdanChanpin = dingdanChanpin; firePropertyChange(“dingdanChanpin”, oldValue, dingdanChanpin); } public Chanpin() { } public String getBianhao() { return bianhao; } public void setBianhao(String bianhao) { String oldValue = this.bianhao; this.bianhao = bianhao; firePropertyChange(“bianhao”, oldValue, bianhao); } } /* ========== Chanpin_Zujian.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; //产品组件关联 public class Chanpin_Zujian extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private Chanpin chanpin; private Zujian zujian; private Double one_howmany; private Bancai bancai; public Chanpin_Zujian() { super(); } @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { Chanpin oldValue = this.chanpin; this.chanpin = chanpin; firePropertyChange(“chanpin”, oldValue, chanpin); } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { Zujian oldValue = this.zujian; this.zujian = zujian; firePropertyChange(“zujian”, oldValue, zujian); } public Double getOne_howmany() { return one_howmany; } public void setOne_howmany(Double one_howmany) { Double oldValue = this.one_howmany; this.one_howmany = one_howmany; firePropertyChange(“one_howmany”, oldValue, one_howmany); } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { Bancai oldValue = this.bancai; this.bancai = bancai; firePropertyChange(“bancai”, oldValue, bancai); } } /* ========== Dingdan.java ========== */ package com.example.kucun2.entity; import com.example.kucun2.entity.data.EntityClassGrassrootsid; import com.example.kucun2.entity.data.SynchronizableEntity; import java.util.ArrayList; import java.util.Date; import java.util.List; /** 订单 */ public class Dingdan extends SynchronizableEntity implements EntityClassGrassrootsid { private Integer id; private String number; private List<Dingdan_Chanpin> dingdanChanpin = new ArrayList<>(); ; private List<Dingdan_chanpin_zujian> dingdanChanpinZujian = new ArrayList<>(); ; private Date xiadan; private Date jiaohuo; public List<Dingdan_Chanpin> getDingdanChanpin() { return dingdanChanpin; } public void setDingdanChanpin(List<Dingdan_Chanpin> dingdanChanpin) { List<Dingdan_Chanpin> oldValue = this.dingdanChanpin; this.dingdanChanpin = dingdanChanpin; firePropertyChange(“dingdanChanpin”, oldValue, dingdanChanpin); } @Override public Integer getId() { return id; } public void setId(Integer id) { Integer oldValue = this.id; this.id = id; firePropertyChange(“id”, oldValue, id); } public String getNumber() { return number; } public void setNumber(String number) { String oldValue = this.number; this.number = number; firePropertyChange(“number”, oldValue, number); } public Date getXiadan() { return xiadan; } public void setXiadan(Date xiadan) { Date oldValue = this.xiadan; this.xiadan = xiadan; firePropertyChange(“xiadan”, oldValue, xiadan); } public Date getJiaohuo() { return jiaohuo; } public void setJiaohuo(Date jiaohuo) { Date oldValue = this.jiaohuo; this.jiaohuo = jiaohuo; firePropertyChange(“jiaohuo”, oldValue, jiaohuo); } public Dingdan() { } public List<Dingdan_chanpin_zujian> getDingdanChanpinZujian() { return dingdanChanpinZujian; } public void setDingdanChanpinZujian(List<Dingdan_chanpin_zujian> dingdanChanpinZujian) { List<Dingdan_chanpin_zujian> oldValue = this.dingdanChanpinZujian; this.dingdanChanpinZujian = dingdanChanpinZujian; firePropertyChange(“dingdanChanpinZujian”, oldValue, dingdanChanpinZujian); } } /* ========== EntityClassGrassrootsid.java ========== */ package com.example.kucun2.entity.data; public interface EntityClassGrassrootsid { Integer getId(); void setId(Integer id); String getEndpoint(String type); void sync(); } /* ========== ReflectionJsonUtils.java ========== */ package com.example.kucun2.entity.data; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class ReflectionJsonUtils { /** 将任意Java对象转换为JSON字符串 @param obj 需要转换的对象 @return JSON格式字符串 */ public static String toJson(Object obj) { if (obj == null) { return “null”; } Class<?> clazz = obj.getClass(); // 处理基础类型 if (isPrimitiveOrWrapper(clazz) || clazz == String.class) { return handlePrimitive(obj); } // 处理数组 if (clazz.isArray()) { return handleArray(obj); } // 处理集合 if (Collection.class.isAssignableFrom(clazz)) { return handleCollection((Collection<?>) obj); } // 处理Map if (Map.class.isAssignableFrom(clazz)) { return handleMap((Map<?, ?>) obj); } // 处理自定义对象 return handleObject(obj); } /** 处理基本数据类型和字符串 */ private static String handlePrimitive(Object obj) { if (obj == null) return “null”; Class<?> clazz = obj.getClass(); if (clazz == String.class) { return “"” + escapeString((String) obj) + “"”; } if (clazz == Character.class || clazz == char.class) { return “"” + escapeChar((Character) obj) + “"”; } return obj.toString(); } /** 处理数组 */ private static String handleArray(Object array) { Class<?> componentType = array.getClass().getComponentType(); int length = java.lang.reflect.Array.getLength(array); StringBuilder sb = new StringBuilder(“[”); for (int i = 0; i < length; i++) { Object element = java.lang.reflect.Array.get(array, i); sb.append(toJson(element)); if (i < length - 1) { sb.append(","); } } sb.append(“]”); return sb.toString(); } /** 处理集合 */ private static String handleCollection(Collection<?> collection) { StringBuilder sb = new StringBuilder(“[”); int index = 0; int size = collection.size(); for (Object element : collection) { sb.append(toJson(element)); if (index < size - 1) { sb.append(“,”); } index++; } sb.append(“]”); return sb.toString(); } /** 处理Map */ private static String handleMap(Map<?, ?> map) { StringBuilder sb = new StringBuilder(“{”); int index = 0; int size = map.size(); for (Map.Entry<?, ?> entry : map.entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); if (!(key instanceof String) && !isPrimitiveOrWrapper(key.getClass())) { throw new IllegalArgumentException("Map keys must be strings or primitives for JSON conversion"); } String keyStr = key instanceof String ? (String) key : key.toString(); sb.append("\"").append(escapeString(keyStr)).append("\":"); sb.append(toJson(value)); if (index < size - 1) { sb.append(","); } index++; } sb.append(“}”); return sb.toString(); } /** 处理自定义对象 */ private static String handleObject(Object obj) { Class<?> clazz = obj.getClass(); Field[] fields = getAllFields(clazz); StringBuilder sb = new StringBuilder(“{”); int index = 0; for (Field field : fields) { // 跳过静态字段 if (Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); String fieldName = field.getName(); Object fieldValue = null; try { fieldValue = field.get(obj); } catch (IllegalAccessException e) { // 忽略访问异常,字段值保持null Log.e("ReflectionJsonUtils", "handleObject: " + e.getLocalizedMessage()); } sb.append("\"").append(escapeString(fieldName)).append("\":"); if (SynchronizableEntity.class.isAssignableFrom(field.getType()) && fieldValue != null && ((SynchronizableEntity) fieldValue).getId() != null) { try { Log.d("ReflectionJsonUtils", "isAssignableFrom: " + sb); sb.append("{\"id\":") .append(((SynchronizableEntity) fieldValue).getId()) .append("}"); } catch (Exception e) { sb.append(toJson(fieldValue)); } } else { sb.append(toJson(fieldValue)); } if (index < fields.length - 1) { sb.append(","); } index++; } sb.append(“}”); Log.d(“ReflectionJsonUtils”, "handleObject: " + sb); return sb.toString(); } // 获取类及其父类的所有字段 private static Field[] getAllFields(Class<?> type) { List<Field> fields = new ArrayList<>(); while (type != null && type != Object.class) { Field[] declaredFields = type.getDeclaredFields(); for (Field field : declaredFields) { // 跳过静态字段 if (!Modifier.isStatic(field.getModifiers())) { fields.add(field); } } type = type.getSuperclass(); } return fields.toArray(new Field[0]); } // 检查是否基本类型或其包装类 private static boolean isPrimitiveOrWrapper(Class<?> clazz) { return clazz.isPrimitive() || clazz == Boolean.class || clazz == Character.class || clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class || clazz == Float.class || clazz == Double.class || clazz == Void.class; } // 转义JSON字符串中的特殊字符 private static String escapeString(String str) { if (str == null) return “”; return str.replace(“\”, “\\”) .replace(“"”, “\"”) .replace(“/”, “\/”) .replace(“\b”, “\b”) .replace(“\f”, “\f”) .replace(“\n”, “\n”) .replace(“\r”, “\r”) .replace(“\t”, “\t”); } // 转义特殊字符 private static String escapeChar(Character ch) { if (ch == null) return “”; return escapeString(String.valueOf(ch)); } } /* ========== SynchronizableEntity.java ========== */ package com.example.kucun2.entity.data; import android.util.Log; import com.example.kucun2.entity.Information; import com.example.kucun2.function.MyAppFnction; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.HashMap; import java.util.Map; public abstract class SynchronizableEntity implements EntityClassGrassrootsid { public boolean isPreservedObject() { return this.getId() != null && this.getId() == -1; } public enum SyncState {NEW, MODIFIED, DELETED, PRESERVED} SyncState state = SyncState.NEW; private static boolean syncEnabled = true; public static void setSyncEnabled(boolean enabled) { syncEnabled = enabled; } // 设置状态的方法 public void setState(SyncState state) { this.state = state; } // 使用标准属性变更支持类 private transient PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); // 属性缓存(可选,用于优化) private final Map<String, Object> propertyCache = new HashMap<>(); // 添加属性变更监听器 public void addPropertyChangeListener(PropertyChangeListener listener) { if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } // 移除属性变更监听器 public void removePropertyChangeListener(PropertyChangeListener listener) { if (changeSupport != null) { changeSupport.removePropertyChangeListener(listener); } } // 触发属性变更通知(子类在setter中调用) protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (changeSupport != null && changeSupport.hasListeners(propertyName)) { // 更新属性缓存(可选) propertyCache.put(propertyName, newValue); // 触发变更事件 changeSupport.firePropertyChange(propertyName, oldValue, newValue); } } // 获取属性当前值(可选,用于setter中获取旧值) protected <T> T getCachedProperty(String propertyName) { return (T) propertyCache.get(propertyName); } // 初始化属性缓存(可选,在构造函数中调用) protected void initPropertyCache() { // 初始化所有字段到缓存 for (java.lang.reflect.Field field : getClass().getDeclaredFields()) { try { field.setAccessible(true); propertyCache.put(field.getName(), field.get(this)); } catch (Exception e) { // 忽略初始化异常 } } } /** 添加url @param type 操作增删改查 @return 返回相对url */ public String getEndpoint(String type) { //从String.xml获取url Log.d(“getEndpoint”, "getEndpoint: " + “url_” + type + “" + this.getClass().getSimpleName().toLowerCase()); return MyAppFnction.getStringResource(“string”, "url” + type + “_” + this.getClass().getSimpleName().toLowerCase()); } public void sync() { if (!syncEnabled) return; // 全局控制开关 if (this.state == null) return; try { switch (this.state) { case NEW: createToServer(); break; case MODIFIED: updateToServer(); break; case DELETED: deleteFromServer(); break; } } catch (Exception e) { Log.e("SyncError", "Sync failed for " + getClass().getSimpleName(), e); } } /** 创建实体到后端 */ private void createToServer() { String endpoint = MyAppFnction.getStringResource(“string”, “url”) + getEndpoint(“add”); Information<SynchronizableEntity> requestData = new Information<SynchronizableEntity>(); // 使用Volley、Retrofit或OkHttp执行POST请求 // 伪代码示例: ApiClient.post(endpoint, requestData, new ApiClient.ApiCallback<SynchronizableEntity>() { @Override public void onSuccess(Information<SynchronizableEntity> e) { // 解析服务器返回的ID Log.d(“onSuccess”, e.toJson()); if (e.getData() != null) { setId(e.getData().getId()); // 设置服务器返回的ID } state = SyncState.MODIFIED; state = SyncState.MODIFIED; } }); } /** 更新实体到后端 */ private void updateToServer() { String endpoint = MyAppFnction.getStringResource(“string”, “url”) + getEndpoint(“update”) + “/” + getId(); // 获取实体ID Information<SynchronizableEntity> requestData = new Information<SynchronizableEntity>(); ApiClient.post(endpoint, requestData, new ApiClient.ApiCallback<SynchronizableEntity>() { @Override public void onSuccess(Information<SynchronizableEntity> e) { // 解析服务器返回的ID Log.d(“onSuccess”, e.toJson()); // String serverId = parseServerId(response); // if (serverId != null) { // // 更新本地ID // setId(serverId); // 需要实体实现setId方法 // } // state = SyncState.MODIFIED; } }); } /** 从后端删除实体 */ private void deleteFromServer() { String endpoint = MyAppFnction.getStringResource(“string”, “url”) + getEndpoint(“delete”) + “/” + getId(); ApiClient.delete(endpoint, new ApiClient.ApiCallback<SynchronizableEntity>() { @Override public void onSuccess(Information<SynchronizableEntity> e) { Log.d(“SyncDelete”, "Successfully deleted " + getId()); } }); } /** 将实体转换为请求映射 */ protected Information<SynchronizableEntity> toRequestMap() { return Information.newSuccess(this); } } /* ========== SynchronizedList.java ========== */ package com.example.kucun2.entity.data; import android.annotation.SuppressLint; import android.util.Log; import com.google.gson.Gson; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /** 支持数据变更自动同步的泛型集合类 基于SynchronizableEntity的事件机制实现数据变更自动同步 @param <T> 必须是SynchronizableEntity或其子类 */ public class SynchronizedList<T extends SynchronizableEntity> implements List<T> { // 使用ArrayList作为底层存储 private final List<T> list = new ArrayList<>(); public Class<T> getEntityType() { return entityType; } private Class<T> entityType; public SynchronizedList(Class<T> T) { this.entityType = T; } // 实体变更监听器 private final EntityChangeListener<T> entityChangeListener = new EntityChangeListener<>(); @Override public int size() { return list.size(); } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public boolean contains(Object o) { return list.contains(o); } @Override public Iterator<T> iterator() { return new SyncedIterator<>(list.iterator(), entityChangeListener); } @Override public Object[] toArray() { return list.toArray(); } @Override public <E> E[] toArray(E[] a) { return list.toArray(a); } // 添加元素时注册监听器 @SuppressLint(“SuspiciousIndentation”) @Override public boolean add(T element) { if (element != null) { element.addPropertyChangeListener(entityChangeListener); } return list.add(element); } // 移除元素时注销监听器 @Override public boolean remove(Object o) { if (o instanceof SynchronizableEntity) { ((SynchronizableEntity) o).removePropertyChangeListener(entityChangeListener); } return list.remove(o); } @Override public boolean containsAll(Collection<?> c) { return list.containsAll©; } // 批量添加时逐个注册监听器 @Override public boolean addAll(Collection<? extends T> c) { for (T element : c) { if (element != null) { element.addPropertyChangeListener(entityChangeListener); } } return list.addAll©; } @Override public boolean addAll(int index, Collection<? extends T> c) { for (T element : c) { if (element != null) { element.addPropertyChangeListener(entityChangeListener); } } return list.addAll(index, c); } // 批量移除时逐个注销监听器 @Override public boolean removeAll(Collection<?> c) { for (Object o : c) { if (o instanceof SynchronizableEntity) { ((SynchronizableEntity) o).removePropertyChangeListener(entityChangeListener); } } return list.removeAll©; } @Override public boolean retainAll(Collection<?> c) { // 先移除未保留元素的监听器 for (T element : list) { if (!c.contains(element)) { element.removePropertyChangeListener(entityChangeListener); } } return list.retainAll©; } // 清空集合时移除所有监听器 @Override public void clear() { for (T element : list) { element.removePropertyChangeListener(entityChangeListener); } list.clear(); } @Override public T get(int index) { return list.get(index); } // 替换元素时更新监听器 @Override public T set(int index, T element) { T oldElement = list.get(index); if (oldElement != null) { oldElement.removePropertyChangeListener(entityChangeListener); } if (element != null) { element.addPropertyChangeListener(entityChangeListener); } return list.set(index, element); } // 在指定位置添加元素时注册监听器 @Override public void add(int index, T element) { if (element != null) { element.addPropertyChangeListener(entityChangeListener); } list.add(index, element); } // 移除元素时注销监听器 @Override public T remove(int index) { T removed = list.remove(index); if (removed != null) { removed.removePropertyChangeListener(entityChangeListener); } return removed; } @Override public int indexOf(Object o) { return list.indexOf(o); } @Override public int lastIndexOf(Object o) { return list.lastIndexOf(o); } @Override public ListIterator<T> listIterator() { return new SyncedListIterator<>(list.listIterator(), entityChangeListener); } @Override public ListIterator<T> listIterator(int index) { return new SyncedListIterator<>(list.listIterator(index), entityChangeListener); } @Override public List<T> subList(int fromIndex, int toIndex) { return list.subList(fromIndex, toIndex); } /** 内部实体变更监听器 监听属性变化并触发同步操作 / private static class EntityChangeListener<T extends SynchronizableEntity> implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { @SuppressWarnings(“unchecked”) T entity = (T) evt.getSource(); entity.sync(); // 触发同步操作 } } /* 增强的Iterator实现 移除元素时自动注销监听器 */ private static class SyncedIterator<T extends SynchronizableEntity> implements Iterator<T> { private final Iterator<T> iterator; private final EntityChangeListener<T> listener; private T current; public SyncedIterator(Iterator<T> iterator, EntityChangeListener<T> listener) { this.iterator = iterator; this.listener = listener; } @Override public boolean hasNext() { return iterator.hasNext(); } @Override public T next() { current = iterator.next(); return current; } @Override public void remove() { if (current != null) { current.removePropertyChangeListener(listener); } iterator.remove(); } } /** 增强的ListIterator实现 支持添加/设置元素时的监听器管理 */ private static class SyncedListIterator<T extends SynchronizableEntity> implements ListIterator<T> { private final ListIterator<T> iterator; private final EntityChangeListener<T> listener; private T lastReturned; public SyncedListIterator(ListIterator<T> iterator, EntityChangeListener<T> listener) { this.iterator = iterator; this.listener = listener; } @Override public boolean hasNext() { return iterator.hasNext(); } @Override public T next() { lastReturned = iterator.next(); return lastReturned; } @Override public boolean hasPrevious() { return iterator.hasPrevious(); } @Override public T previous() { lastReturned = iterator.previous(); return lastReturned; } @Override public int nextIndex() { return iterator.nextIndex(); } @Override public int previousIndex() { return iterator.previousIndex(); } @Override public void remove() { if (lastReturned != null) { lastReturned.removePropertyChangeListener(listener); } iterator.remove(); } @Override public void set(T e) { if (lastReturned != null) { lastReturned.removePropertyChangeListener(listener); } if (e != null) { e.addPropertyChangeListener(listener); } iterator.set(e); lastReturned = e; } @Override public void add(T e) { if (e != null) { e.addPropertyChangeListener(listener); } iterator.add(e); } } } /* ========== Data.java ========== */ package com.example.kucun2.entity.data; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.example.kucun2.entity.; import com.example.kucun2.function.MyAppFnction; import com.example.kucun2.function.SafeLogger; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.; import java.util.; import java.util.stream.Collectors; import okhttp3.; /** 核心数据管理类 负责: 声明所有实体类的静态存储列表 从服务器加载全量数据 管理实体间的关联关系 处理数据同步状态 */ public class Data { // ====================== 静态数据列表声明 ====================== public static SynchronizedList<Bancai> bancais = new SynchronizedList<>(Bancai.class); public static SynchronizedList<Caizhi> caizhis = new SynchronizedList<>(Caizhi.class); public static SynchronizedList<Mupi> mupis = new SynchronizedList<>(Mupi.class); public static SynchronizedList<Chanpin> chanpins = new SynchronizedList<>(Chanpin.class); public static SynchronizedList<Chanpin_Zujian> chanpin_zujians = new SynchronizedList<>(Chanpin_Zujian.class); public static SynchronizedList<Dingdan> dingdans = new SynchronizedList<>(Dingdan.class); public static SynchronizedList<Dingdan_Chanpin> dingdan_chanpins = new SynchronizedList<>(Dingdan_Chanpin.class); public static SynchronizedList<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians = new SynchronizedList<>(Dingdan_chanpin_zujian.class); public static SynchronizedList<Kucun> kucuns = new SynchronizedList<>(Kucun.class); public static SynchronizedList<Zujian> zujians = new SynchronizedList<>(Zujian.class); public static SynchronizedList<User> users = new SynchronizedList<>(User.class); public static SynchronizedList<Jinhuo> jinhuos = new SynchronizedList<>(Jinhuo.class); private static User user; // 实体类型与列表的映射表 <实体类, 对应的同步列表> public static final Map<Class, SynchronizedList<SynchronizableEntity>> dataCollectionMap = new HashMap<>(); private static final Gson gson = GsonFactory.createWithIdSerialization(); private static OkHttpClient client; private static final String TAG = “DataLoader”; // 静态初始化:建立实体类型与列表的映射关系 static { try { // 通过反射获取所有SynchronizedList字段 for (Field field : Data.class.getDeclaredFields()) { if (SynchronizedList.class.equals(field.getType())) { SynchronizedList<?> list = (SynchronizedList<?>) field.get(null); if (list != null) { // 将实体类型与列表关联 dataCollectionMap.put(list.getEntityType(), (SynchronizedList<SynchronizableEntity>) list); } } } } catch (IllegalAccessException e) { throw new RuntimeException(“初始化dataCollectionMap失败”, e); } } // ====================== 核心数据加载方法 ====================== /** 从服务器加载全量数据 @param context Android上下文 @param callback 加载结果回调 */ public static void loadAllData(Context context, LoadDataCallback callback) { // 主线程检查 if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException(“必须在主线程调用Data.loadAllData”); } ensurePreservedObjects(); // 确保存在预置对象 // 使用传入的 Context 获取主线程 Handler Handler mainHandler = new Handler(context.getMainLooper()); // 确保使用安全的客户端 if (client == null) { client = MyAppFnction.getClient(); } SynchronizableEntity.setSyncEnabled(false); String url = MyAppFnction.getStringResource(“string”, “url”) + MyAppFnction.getStringResource(“string”, “url_all”); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, “Failed to load data”, e); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { Log.e(TAG, "Unexpected response code: " + response.code()); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); return; } String responseData = response.body().string(); SynchronizableEntity.setSyncEnabled(true); ensurePreservedObjects(); // 在主线程处理解析 mainHandler.post(() -> { parseAndAssignData(responseData, context, callback); }); } }); } // ====================== 数据处理私有方法 ====================== /** 解析JSON数据并更新到实体列表 */ private static void parseAndAssignData(String jsonData, Context context, LoadDataCallback callback) { try { // 解析顶层响应结构 Type responseType = new TypeToken<Information<AllDataResponse>>() {}.getType(); Information<AllDataResponse> info = gson.fromJson(jsonData, responseType); // 验证响应有效性 if (info == null || info.getData() == null || info.getStatus() != 200) { throw new IllegalStateException(“无效服务器响应”); } AllDataResponse allData = info.getData(); SafeLogger.d(“data”, "原始数据: " + gson.toJson(allData)); // 更新所有数据列表 updateAllLists(allData); automaticAssociation(); // 建立实体关联 setAllEntitiesState(SynchronizableEntity.SyncState.MODIFIED); // 设置状态 safeCallback(callback, true); // 成功回调 } catch (Exception e) { Log.e(TAG, "数据处理异常: " + e.getMessage()); safeCallback(callback, false); } finally { SynchronizableEntity.setSyncEnabled(true); // 重新启用同步 } } /** 批量更新所有实体列表 / private static void updateAllLists(AllDataResponse allData) { updateList(bancais, allData.bancais); updateList(caizhis, allData.caizhis); updateList(mupis, allData.mupis); updateList(chanpins, allData.chanpins); updateList(chanpin_zujians, allData.chanpin_zujians); updateList(dingdans, allData.dingdans); updateList(dingdan_chanpins, allData.dingdan_chanpins); updateList(Dingdan_chanpin_zujians, allData.Dingdan_chanpin_zujians); updateList(kucuns, allData.kucuns); updateList(zujians, allData.zujians); updateList(users, allData.users); updateList(jinhuos, allData.jinhuos); } /* 安全更新单个列表(保留预置对象) */ private static <T extends SynchronizableEntity> void updateList( List<T> existingList, List<T> newList ) { if (newList == null) return; // 保留现有列表中的预置对象 List<T> preservedItems = existingList.stream() .filter(item -> item != null && item.isPreservedObject()) .collect(Collectors.toList()); // 清空后重新添加(预置对象 + 有效新数据) existingList.clear(); existingList.addAll(preservedItems); existingList.addAll(newList.stream() .filter(item -> item != null && item.getId() != null && item.getId() != -1) .collect(Collectors.toList()) ); } /** 确保每个列表都有预置对象(用于表示空值/默认值) / private static void ensurePreservedObjects() { // 为每个实体类型检查并添加预置对象 addIfMissing(bancais, Bancai.class); addIfMissing(caizhis, Caizhi.class); addIfMissing(mupis, Mupi.class); addIfMissing(chanpins, Chanpin.class); addIfMissing(chanpin_zujians, Chanpin_Zujian.class); addIfMissing(dingdans, Dingdan.class); addIfMissing(kucuns, Kucun.class); addIfMissing(zujians, Zujian.class); addIfMissing(Dingdan_chanpin_zujians, Dingdan_chanpin_zujian.class); addIfMissing(dingdan_chanpins, Dingdan_Chanpin.class); addIfMissing(jinhuos, Jinhuo.class); addIfMissing(users, User.class); } private static <T extends SynchronizableEntity> void addIfMissing( List<T> list, Class<T> clazz ) { if (!containsPreservedObject(list)) { list.add(createInstance(clazz)); } } /* 检查列表是否包含预置对象 @param list 目标实体列表 @return 是否包含预置对象 / private static <T extends SynchronizableEntity> boolean containsPreservedObject(List<T> list) { return list.stream().anyMatch(item -> item != null && item.isPreservedObject() ); } /* 自动建立实体间关联关系(通过反射实现) */ private static void automaticAssociation() { for (Class<?> entityClass : dataCollectionMap.keySet()) { try { associateEntities(dataCollectionMap.get(entityClass)); } catch (Exception e) { Log.e(TAG, entityClass.getSimpleName() + " 关联失败", e); } } } private static <T extends SynchronizableEntity> void associateEntities( SynchronizedList<T> list ) throws IllegalAccessException, ClassNotFoundException { for (T entity : list) { if (entity == null) continue; for (Field field : entity.getClass().getDeclaredFields()) { field.setAccessible(true); Class<?> fieldType = field.getType(); // 处理实体引用字段 if (SynchronizableEntity.class.isAssignableFrom(fieldType)) { associateSingleReference(entity, field); } // 处理实体列表字段 else if (List.class.isAssignableFrom(fieldType)) { associateReferenceList(entity, field); } // 处理基础类型字段 else { setDefaultPrimitiveValue(entity, field); } } } } // ====================== 关联辅助方法 ====================== private static void associateSingleReference( SynchronizableEntity entity, Field field ) throws IllegalAccessException { SynchronizableEntity ref = (SynchronizableEntity) field.get(entity); Class<?> targetType = field.getType(); // 查找目标实体 SynchronizableEntity target = findTargetEntity(ref, targetType); field.set(entity, target); } private static void associateReferenceList( SynchronizableEntity entity, Field field ) throws IllegalAccessException, ClassNotFoundException { // 获取列表泛型类型 Type genericType = field.getGenericType(); if (!(genericType instanceof ParameterizedType)) return; Class<?> itemType = Class.forName( ((ParameterizedType) genericType).getActualTypeArguments()[0].getTypeName() ); // 只处理实体列表 if (!SynchronizableEntity.class.isAssignableFrom(itemType)) return; List<SynchronizableEntity> refList = (List<SynchronizableEntity>) field.get(entity); if (refList == null) { refList = new ArrayList<>(); field.set(entity, refList); } // 清理空值并重建引用 refList.removeAll(Collections.singleton(null)); for (int i = 0; i < refList.size(); i++) { refList.set(i, findTargetEntity(refList.get(i), itemType)); } } /** 查找关联实体(优先匹配ID,找不到则使用预置对象) */ private static SynchronizableEntity findTargetEntity( SynchronizableEntity ref, Class<?> targetType ) { SynchronizedList<SynchronizableEntity> targetList = dataCollectionMap.get(targetType); if (targetList == null) return null; // 无效引用时返回预置对象 if (ref == null || ref.getId() == null || ref.getId() < 0) { return targetList.stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } // 按ID查找目标实体 return targetList.stream() .filter(e -> ref.getId().equals(e.getId())) .findFirst() .orElseGet(() -> targetList.stream() // 找不到时回退到预置对象 .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null) ); } // ====================== 工具方法 ====================== /** 创建带默认值的实体实例(用作预置对象) */ public static <T> T createInstance(Class<T> clazz) { try { T instance = clazz.getDeclaredConstructor().newInstance(); // 设置基础字段默认值 for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); setDefaultFieldValue(instance, field); } // 设置特殊字段 clazz.getMethod(“setId”, Integer.class).invoke(instance, -1); clazz.getMethod(“setState”, SynchronizableEntity.SyncState.class) .invoke(instance, SynchronizableEntity.SyncState.PRESERVED); return instance; } catch (Exception e) { Log.e(“Data”, "创建实例失败: " + clazz.getName(), e); try { return clazz.newInstance(); // 回退创建 } catch (Exception ex) { throw new RuntimeException(“无法创建实例”, ex); } } } private static <T> void setDefaultFieldValue(T instance, Field field) throws IllegalAccessException { Class<?> type = field.getType(); if (type == String.class) field.set(instance, “无”); else if (type == Integer.class || type == int.class) field.set(instance, -1); else if (type == Double.class || type == double.class) field.set(instance, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(instance, false); else if (type == Date.class) field.set(instance, new Date()); else if (SynchronizableEntity.class.isAssignableFrom(type)) { field.set(instance, getPreservedEntity((Class<?>) type)); } else if (List.class.isAssignableFrom(type)) { field.set(instance, new ArrayList<>()); } } private static SynchronizableEntity getPreservedEntity(Class<?> type) { return dataCollectionMap.get(type).stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } private static void setDefaultPrimitiveValue( SynchronizableEntity entity, Field field ) throws IllegalAccessException { if (field.get(entity) != null) return; Class<?> type = field.getType(); if (type == String.class) field.set(entity, “无”); else if (type == Integer.class || type == int.class) field.set(entity, -1); else if (type == Double.class || type == double.class) field.set(entity, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(entity, false); else if (type == Date.class) field.set(entity, new Date()); } /** 主线程安全回调 / private static void safeCallback(LoadDataCallback callback, boolean success) { new Handler(Looper.getMainLooper()).post(() -> { if (callback == null) return; if (success) callback.onSuccess(); else callback.onFailure(); }); } /* 设置所有实体同步状态 */ private static void setAllEntitiesState(SynchronizableEntity.SyncState state) { dataCollectionMap.values().forEach(list -> list.forEach(entity -> { if (entity != null) entity.setState(state); }) ); } // ====================== 内部类/接口 ====================== public interface LoadDataCallback { void onSuccess(); void onFailure(); } /** JSON响应数据结构 */ public static class AllDataResponse { public List<Bancai> bancais; public List<Caizhi> caizhis; public List<Mupi> mupis; public List<Chanpin> chanpins; public List<Chanpin_Zujian> chanpin_zujians; public List<Dingdan> dingdans; public List<Dingdan_Chanpin> dingdan_chanpins; public List<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians; public List<Kucun> kucuns; public List<Zujian> zujians; public List<User> users; public List<Jinhuo> jinhuos; } } package com.example.kucun2.entity.data; import static android.content.ContentValues.TAG; import android.content.Context; import android.util.Log; import com.example.kucun2.R; import com.example.kucun2.entity.Information; import com.example.kucun2.function.MyAppFnction; import com.example.kucun2.function.TLSUtils; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.Type; import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; public class ApiClient { // 使用Volley、Retrofit或OkHttp实现以下方法 private static final Gson gson = new Gson(); private static OkHttpClient client; private static final String TAG = "DataLoader"; static { } public static <T, R> void post(String url, Information<T> requestData, ApiCallback<R> callback) { // 1. 构建请求体(JSON格式) String jsonRequest = gson.toJson(requestData); RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonRequest); // 2. 创建POST请求 Request request = new Request.Builder() .url(url) .post(body) .build(); Log.d(TAG, "POST请求URL: " + url); Log.d(TAG, "请求数据: " + jsonRequest); // 3. 发送异步请求 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "请求失败: " + e.getMessage()); if (callback != null) { //callback.onError(-1, e.getMessage()); } } @Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody responseBody = response.body()) { if (!response.isSuccessful()) { Log.e(TAG, "服务器响应错误: " + response.code() + " - " + response.message()); if (callback != null) { // callback.onError(response.code(), response.message()); } return; } // 4. 处理成功响应 String jsonResponse = responseBody.string(); Log.d(TAG, "服务器响应: " + jsonResponse); // 5. 解析为Information对象 // 注意:这里需要提前确定响应中data的类型(TypeToken) Type responseType = new TypeToken<Information<R>>() { }.getType(); Information<R> responseInfo = gson.fromJson(jsonResponse, responseType); if (callback != null) { callback.onSuccess(responseInfo); } } } }); } public static <T, R> void put(String url, Information<T> data, ApiCallback<T> callback) { // 实现PUT请求逻辑 } public static <R> void delete(String url, ApiCallback<R> callback) { // 实现DELETE请求逻辑 } public static interface ApiCallback<T> { void onSuccess(Information<T> data); // void onError(int statusCode, String error); } }<string name=“url”>https://192.168.31.177:3000</string> <string name=“url_all”>/app/all</string> <string name=“url_bancis”>/app/bancai/all</string> <string name=“url_caizhis”>/app/caizhi/all</string> <string name=“url_mupis”>/app/mupi/all</string> <string name=“url_dingdans”>/app/dingdan/all</string> <string name=“url_chanpins”>/app/chanpin/all</string> <string name=“url_zujians”>/app/zujian/all</string> <string name="url_chanpin_zujians">/app/chanpin_zujian/all</string> <string name="url_dingdan_zujians">/app/dingdan_zujian/all</string> <string name="url_dingdan_chanpins">/app/dingdan_chanpin/all</string> <string name="url_jinhuos">/app/jinhuo/all</string> <string name="url_add_bancai">/app/bancai/add</string> <string name="url_add_dingdan">/app/dingdan/add</string> <string name="url_add_chanpin">/app/chanpin/add</string> <string name="url_add_zujian">/app/zujian/add</string> <string name="url_add_caizhi">/app/caizhi/add</string> <string name="url_add_mupi">/app/mupi/add</string> <string name="url_add_dingdan_chanpin">/app/dingdan_chanpi/add</string> <string name="url_add_dingdan_zujian">/app/dingdan_zujian/add</string> <string name="url_add_chanpin_zujian">/app/chanpin_zujian/add</string> <string name="url_add_jinhuo">/app/jinhuo/add</string> <string name="url_login">/user/login</string>补全自动同步

m_data_list.clear(); 会不会是清理的这个时候,dataVersionCheck()触发导致问题出现?package com.isa.navi.jni.hmi; import android.content.Context; import android.os.Message; import android.os.StatFs; import android.util.Log; import androidx.annotation.NonNull; import com.isa.navi.jni.hmi.bean.UpdateList; import com.isa.navi.jni.hmi.update.USBListener; import com.isa.navi.library.map.UpdateEnvironment; import com.isa.navi.library.map.UpdateInfoBean; import com.isa.navi.manager.CallBackManager; import com.isa.navi.manager.CarManager; import com.isa.navi.manager.base.BaseHandlerManager; import com.isa.navi.receiver.USBReceiver; import com.isa.navi.utils.GsonUtils; import com.isa.navi.utils.LogUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class HmiJNIImpl extends BaseHandlerManager implements IHmiJNI { private static final String TAG = "UPDATE"; private static final int MSG_DOWNLOAD_STATUS_CHANGE = 1; private final HmiProxyJNI mHmiProxyJNI; private final Format mFormat; private static final String PASSWORD_PREFIX = "DfCs"; private static final String PASSWORD_SUFFIX = "#@"; private static final int LAST_CHARS_LENGTH = 4; private static String updateUSBpath = ""; /** * 车辆管理实例,用于管理车辆相关的功能。 */ private CarManager mCarManager; private volatile static HmiJNIImpl mInstance; private boolean is_run = false; private final Set<String> USBPathSet = Collections.synchronizedSet(new HashSet<>()); public static HmiJNIImpl getInstance() { if (mInstance == null) { synchronized (HmiJNIImpl.class) { if (mInstance == null) { mInstance = new HmiJNIImpl(); } } } return mInstance; } private HmiJNIImpl() { super(true); mHmiProxyJNI = new HmiProxyJNI(); mFormat = Format.getInstance(); mCarManager = CarManager.getInstance(); } @Override public void initialize() { super.initialize(); LogUtils.d(TAG, "initialize : "); DfCert.getInstance().getService(); CallBackManager.getInstance().registerUSBEventChangeListener(new USBListener() { @Override public void path(String mountPath) { USBPathSet.add(mountPath); LogUtils.i(TAG,"USBPathSet:" + mountPath); } }); // 初始化C++模块 mHmiProxyJNI.InitializeCpp(); InstanceEngine(); } private int[] getCurrentYearAndQuarter() { int[] result = new int[2]; LocalDate currentDate = LocalDate.now(); // 获取当前年份 int currentYear = currentDate.getYear(); // 获取当前月份 Month currentMonth = currentDate.getMonth(); // 根据月份确定当前季度 int currentQuarter; switch (currentMonth) { case JANUARY: case FEBRUARY: case MARCH: currentQuarter = 1; break; case APRIL: case MAY: case JUNE: currentQuarter = 2; break; case JULY: case AUGUST: case SEPTEMBER: currentQuarter = 3; break; case OCTOBER: case NOVEMBER: case DECEMBER: currentQuarter = 4; break; default: currentQuarter = 0; } result[0] = currentYear; result[1] = currentQuarter; return result; } private int[] parseMapInfo(String mapInfo) { int[] result = new int[2]; // 检查输入字符串的长度是否为6(4位年份 + 2位季度) if (mapInfo == null || mapInfo.length() != 6) { LogUtils.e(TAG, "Input string must be in the format YYYYMM where YYYY is the year and MM is the quarter as a two-digit number."); return result; } // 提取年份(前4位) String yearStr = mapInfo.substring(0, 4); result[0] = Integer.parseInt(yearStr); // 提取季度(后2位),并验证它在1到4的范围内 String quarterStr = mapInfo.substring(4, 6); int quarter = Integer.parseInt(quarterStr); if (quarter < 1 || quarter > 4) { LogUtils.e(TAG, "Quarter must be between 1 and 4."); return result; } result[1] = quarter; return result; } public void dataVersionCheck() { // 创建独立线程执行版本检查,避免阻塞主线程 Thread dataVersionCheckThread = new Thread(new Runnable() { @Override public void run() { try { UpdateInfoBean bean = new UpdateInfoBean(); // 通过JNI获取本地地图版本号(格式如"202302") String localVersion = mHmiProxyJNI.getMapInfo(); // 特殊情况处理:无版本号(首次安装/数据损坏) if(localVersion.isEmpty()) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "zwx:不存在地图"); LogUtils.i(TAG, "Detected no updates for a long time"); return; } // 获取当前年份和季度(如[2024, 2]表示2024年第二季度) int[] currentYearAndQuarter = getCurrentYearAndQuarter(); // 解析本地版本号中的年份和季度(如将"202302"解析为[2023, 2]) int[] localYearAndQuarter = parseMapInfo(localVersion); // 计算年份差和季度差 int yearDifference = currentYearAndQuarter[0] - localYearAndQuarter[0]; int QuarterDifference = currentYearAndQuarter[1] - localYearAndQuarter[1]; // 核心判断逻辑:将年份差转换为季度数(1年=4季度)后叠加季度差 // 示例:当前2024Q2 vs 本地2023Q1 → (2024-2023)*4 + (2-1) = 5季度 // 当总季度差 > 3(即9个月)时触发更新提醒 if(yearDifference * 4 + QuarterDifference > 3) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "zwx:地图版本滞后"); LogUtils.i(TAG, "Detected no updates for a long time"); } } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 dataVersionCheckThread.setName("ISA_NAVI_dataVersionCheck"); // 启动线程 dataVersionCheckThread.start(); } public void setContext(Context context) { if(mContext == null) { mContext = context; } language.getInstance().setContext(mContext); } @Override protected void handleMessage(Message msg) { switch (msg.what) { case MSG_DOWNLOAD_STATUS_CHANGE: handleDownloadStatusChange((String) msg.obj); break; default: break; } } private List<String> JsonArrayToListExample(JSONArray jsonArray) { List<String> stringList = new ArrayList<>(); try { // 遍历JSONArray for (int i = 0; i < jsonArray.length(); i++) { // 由于我们假设JSONArray只包含字符串,所以我们可以直接getString String item = jsonArray.getString(i); // 将字符串添加到列表中 stringList.add(item); } } catch (JSONException e) { e.printStackTrace(); } return stringList; } private UpdateInfoBean jsonToUpdateInfoBean(String json) { UpdateInfoBean result = null; try { // 解析整个JSON字符串为一个JSONObject JSONObject rootObject = new JSONObject(json); // 解析mUpdateMessage对象 JSONObject mUpdateMessage = rootObject.getJSONObject("mUpdateMessage"); String country = mUpdateMessage.getString("country"); double downloadSize = mUpdateMessage.getDouble("downloadSize"); boolean isCurrentCountry = mUpdateMessage.getBoolean("isCurrentCountry"); double progress = mUpdateMessage.getDouble("progress"); double totalSize = mUpdateMessage.getDouble("totalSize"); int updateStatus = mUpdateMessage.getInt("updateStatus"); String version = mUpdateMessage.getString("version"); // 解析mUpdateNotification对象 JSONObject mUpdateNotification = rootObject.getJSONObject("mUpdateNotification"); JSONArray updateException = mUpdateNotification.getJSONArray("UpdateException"); JSONArray updateFailedList = mUpdateNotification.getJSONArray("UpdateFailedList"); boolean updateResult = mUpdateNotification.getBoolean("UpdateResult"); JSONArray updateSuccessfulList = mUpdateNotification.getJSONArray("UpdateSuccessfulList"); // 解析mapStatus int mapStatus = rootObject.getInt("mapStatus"); UpdateInfoBean.UpdateNotification temp1 = new UpdateInfoBean.UpdateNotification(updateResult,JsonArrayToListExample(updateException),JsonArrayToListExample(updateSuccessfulList),JsonArrayToListExample(updateFailedList)); UpdateInfoBean.UpdateMessage temp2 = new UpdateInfoBean.UpdateMessage(country, version, totalSize, (int) progress, isCurrentCountry); temp2.setUpdateStatus(updateStatus); temp2.setDownloadSize(downloadSize); result = new UpdateInfoBean(mapStatus, temp1, temp2); } catch (Exception e) { LogUtils.e(TAG, "JSON parsing failed:" + e.toString()); } return result; } private void handleDownloadStatusChange(String json) { UpdateInfoBean bean = jsonToUpdateInfoBean(json); LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } } private void handleDownloadStatusChange(UpdateInfoBean bean) { LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } else { LogUtils.e(TAG, "UpdateInfoBean is null!"); } } public void sendUpdateMessage(String json) { LogUtils.d(TAG, "sendUpdateMessage : " + json); Message message = Message.obtain(); message.what = MSG_DOWNLOAD_STATUS_CHANGE; message.obj = json; mHandler.sendMessage(message); } @Override public void updateMap() { LogUtils.d(TAG, "activeUpdateStart : "); // 检查更新过程是否已经在运行,防止重复创建线程 if(is_run) { LogUtils.i(TAG, "更新程序已经在进行"); return; }else { is_run = true; } // 创建一个新线程来执行主动更新 Thread updateMapThread = new Thread(new Runnable() { @Override public void run() { try { // 调用JNI方法开始主动更新 mHmiProxyJNI.updateMap(); LogUtils.d(TAG, "activeUpdateStart end: "); } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 updateMapThread.setName("ISA_NAVI_UpdateMapThread"); // 启动线程 updateMapThread.start(); } public void updateMapend() { is_run = false; } @Override public void suspendUpdate(boolean suspend) { mHmiProxyJNI.suspendUpdate(suspend); } @Override public void cancelUpdate() { mHmiProxyJNI.cancelUpdate(); } /** * 启动USB检测线程,用于检测插入的U盘并判断是否有可用的升级文件。 */ public void startUSBDetection() { LogUtils.i(TAG, "startUSBDetection START"); // setUSBPath("/data/KVM_EU_202404_T1.2.zip"); Thread USBDetectionThread = new Thread(new Runnable(){ @Override public void run() { UpdateInfoBean bean = new UpdateInfoBean(); boolean isHaveUSBData = false; //遍历检测到的U盘路径 if(USBPathSet.isEmpty()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } int set_num = 0; for(String usbPath : USBPathSet ) { //判断U盘路径是否真实存在 set_num ++; if(USBReceiver.isMounted(usbPath)) { File usbFile = new File(usbPath); File[] listFiles = usbFile.listFiles(); if(listFiles == null) { continue; } File newfile = null; for(File file : listFiles) { //判断U盘内是否含有升级文件 if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) { isHaveUSBData = true; if(newfile == null) { newfile = file; } else { if(Format.getInstance().isNewerVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) { newfile = file; } } } } //未检测到U盘指定路径下是否有数据,触发提示弹窗 if(!isHaveUSBData || newfile == null) { LogUtils.e(TAG, "The USB drive does not contain an upgrade package!"); bean.setUpdateStatus(3); handleDownloadStatusChange(bean); return; }else{ // 从文件名中提取版本号 String localDataPath = Format.getInstance().extractVersion(getCountryDbPath()); LogUtils.i(TAG, "file name:" + newfile.getName()); LogUtils.i(TAG, "localDataPath:" + localDataPath); if(localDataPath.isEmpty()) { String mapinfo = getMapInfo(); if(!mapinfo.isEmpty()) { String[] newVersionParts = new String[2]; Format.getInstance().extractVersion(newfile.getName().replaceAll("\\.\\w+$", ""), newVersionParts); String newUpdateVersion = newVersionParts[0]; String newReleasesVersion = newVersionParts[1]; LogUtils.i(TAG, "localDataPath.isEmpty() newUpdateVersion: " + newUpdateVersion); LogUtils.i(TAG, "localDataPath.isEmpty() newReleasesVersion: " + newReleasesVersion); if(Integer.parseInt(newUpdateVersion) > Integer.parseInt(mapinfo) || (Integer.parseInt(newUpdateVersion) == Integer.parseInt(mapinfo) && Double.parseDouble(newReleasesVersion) > 1)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } } else { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } return; } // 判断U盘内数据版本是否大于车机数据版本 if(Format.getInstance().isNewerVersion(newfile.getName().replaceAll("\\.\\w+$", ""), localDataPath)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } return; } } else { if(set_num == USBPathSet.size()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } } } } }); USBDetectionThread.setName("ISA_NAVI_USBDetectionThread"); USBDetectionThread.start(); } public String getMapInfo() { String result = mHmiProxyJNI.getMapInfo(); LogUtils.i(TAG,"MapInfo:" + result); return result; } public String getUpdateVersion() { String result = mHmiProxyJNI.getUpdateVersion(); LogUtils.i(TAG,"UpdateVersion:" + result); return result; } public UpdateEnvironment checkUpdateEnvironment() { String json = mHmiProxyJNI.checkUpdateEnvironment(); LogUtils.d(TAG,"checkUpdateEnvironment JSON:" + json); UpdateEnvironment result = GsonUtils.fromJson(json, UpdateEnvironment.class); if(result == null) { LogUtils.e(TAG, "GsonUtils.fromJson(json, UpdateEnvironment.class) Fail!"); return null; } if (!result.getResult()) { List<String> temp_list = new ArrayList<>(); for (int i : result.getInt_reason()) { String str = language.getInstance().getEnvironment(i); if(str != null && !str.isEmpty()) { temp_list.add(str); } } // 更新temp对象中的reason字段 result.setReason(temp_list); } LogUtils.i(TAG,"checkUpdateEnvironment :" + result); return result; } public String getUpdatePurpose() { return language.getInstance().getUpdatePurpose(); } public String getUpdateContent() { return language.getInstance().getUpdateContent(); } public String getUserManual() { return language.getInstance().getUserManual(); } public String getUpdatedContentDescription() { return language.getInstance().getUpdatedContentDescription(); } public int getEstimatedTime() { return 30; } private void InstanceEngine() { Thread InstanceEngine = new Thread(new Runnable(){ @Override public void run() { LogUtils.d("ISA_NAVI", "InstanceEngine start!"); mHmiProxyJNI.instanceEngine(); } }); InstanceEngine.setName("ISA_NAVI_InstanceEngine"); InstanceEngine.start(); } // 获取后四位字符 @NonNull private static String getLastFourChars(String mapInfo) { if (mapInfo == null || mapInfo.isEmpty()) { LogUtils.e(TAG, "mapInfo cannot be null or empty"); return "0000"; } return mapInfo.length() >= LAST_CHARS_LENGTH ? mapInfo.substring(mapInfo.length() - LAST_CHARS_LENGTH) : "0000"; } // 生成密码 @NonNull private String generatePassword() { String mapInfo = getMapInfo(); String lastFourChars = getLastFourChars(mapInfo); return PASSWORD_PREFIX + lastFourChars + PASSWORD_SUFFIX; } // 校验密码 public boolean validatePassword(String inputPassword) { if (inputPassword == null) { return false; } String generatedPassword = generatePassword(); LogUtils.i(TAG, "generatedPassword:" + generatedPassword); LogUtils.i(TAG, "inputPassword:" + inputPassword); return inputPassword.equals(generatedPassword); } public void setUSBPath(String USBPath) { mHmiProxyJNI.setUSBPath(USBPath); } public boolean unzipFile(String zipFilePath, String destinationDirPath) { return ZipUtils.getInstance().unzip(zipFilePath, destinationDirPath); } public long getFileSize(String filePath) { File file = new File(filePath); if(!file.isFile() || !file.exists()) { return 0; } LogUtils.d(TAG, "getFileSize:" + file.length()); return file.length(); } public long getAvailableSpace(String filePath) { StatFs stat = new StatFs(filePath); long availableBlocks = stat.getAvailableBlocksLong(); long blockSize = stat.getBlockSizeLong(); LogUtils.i(TAG, "[AvailableSpace]:" + availableBlocks * blockSize + "字节"); return availableBlocks * blockSize; } public String getFail(int i) { return language.getInstance().getFail(i); } public boolean decryptFile(String encryptedFilePath, String decryptedFilePath) { return new DecryptUtil().decryptFile(encryptedFilePath, decryptedFilePath, DfCert.getInstance().getKEY_AES_256()); } public boolean verifySignature(String filePath, String signFilePath) { try { return DecryptUtil.verifySignature(filePath, signFilePath, DfCert.getInstance().getPUBLIC_KEY()); } catch (Exception e) { return false; } } /** * 获取高压电池电量百分比。 * * 此方法通过调用mCarManager.getHVPercent来获取高压电池的电量百分比。如果调用成功,则返回电量百分比值; * 如果调用失败或发生异常,则捕获异常并打印堆栈跟踪,最后返回0。 * * @return 一个浮点数,表示高压电池的电量百分比(0-100);如果获取失败或发生异常,则返回0。 */ public float getHVPercent() { float HVPercent = 0; try { HVPercent = mCarManager.getHVPercent(); // LogUtils.i(TAG, "zwx:HVPercent_test:HVPercent_get:"+HVPercent); if(HVPercent > 100 || HVPercent < 0) { HVPercent = 0; } } catch (Exception e) { e.printStackTrace(); } return HVPercent; } boolean isMounted() { return USBReceiver.isMounted(updateUSBpath); } public String getCountryDbPath() { return mHmiProxyJNI.getCountryDbPath(); } public boolean dataVerification(String verifyDataPath) { LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath); String signPath = "/data/data/com.isa.navi/sign/"; if(!mapControl()) { return false; } //解压加密数据 LogUtils.i(TAG, "verifyDataPath[" + verifyDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(verifyDataPath, signPath)) { //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //提取签名文件以及加密文件的绝对路径 String[] temp = ZipUtils.getInstance().findEncryptedZipFiles(signPath); String aesDaraPath = temp[1]; String signDataPtah = temp[0]; LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath); LogUtils.i(TAG, "signDataPtah" + signDataPtah); if(!mapControl()) { return false; } //验证签名 if(!verifySignature(aesDaraPath, signDataPtah)) { LogUtils.e(TAG, "verifySignature fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //对称解密Zip if(!decryptFile(aesDaraPath, verifyDataPath)) { LogUtils.e(TAG, "decryptFile fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } LogUtils.i(TAG, "dataVerification success"); return true; } /** * 删除指定目录及其所有内容 * * @param directory 指定的目录路径 * @return 如果删除成功返回 true,否则返回 false */ public boolean deleteDirectory(String directory) { File dir = new File(directory); if (!dir.exists()) { LogUtils.e("目录不存在: " + directory); return false; // 目录不存在,视为删除失败 } if (!dir.isDirectory()) { LogUtils.e("指定路径不是一个目录: " + directory); return false; // 路径不是目录,无法删除 } return deleteRecursive(dir); // 递归删除目录 } /** * 递归删除文件或目录 * * @param file 文件或目录 * @return 如果删除成功返回 true,否则返回 false */ private boolean deleteRecursive(File file) { if (file.isDirectory()) { // 如果是目录,递归删除其所有子文件和子目录 File[] files = file.listFiles(); if (files != null) { for (File subFile : files) { if (!deleteRecursive(subFile)) { return false; // 如果某个子文件或子目录删除失败,则整体失败 } } } } // 删除当前文件或空目录 if (!file.delete()) { LogUtils.e("无法删除文件或目录: " + file.getAbsolutePath()); return false; // 删除失败 } LogUtils.i("已成功删除: " + file.getAbsolutePath()); return true; // 删除成功 } public boolean mapControl() { LogUtils.d(TAG, "mapControl JAVA start"); return mHmiProxyJNI.mapControl(); } public boolean zipDbFiles(String sourceDir, String outputZipPath) { return ZipUtils.getInstance().zipDbFiles(sourceDir, outputZipPath); } public boolean zip(String sourcePath, String outputZipPath) { return ZipUtils.getInstance().zip(sourcePath, outputZipPath, true); } // Used to load the 'native-lib' library on application startup. static { LogUtils.d(TAG, "loadLibrary IsaEngineJni " + System.currentTimeMillis()); System.loadLibrary("IsaEngineJni"); } } 检查线程是独立的

package com.isa.navi.jni.hmi; import static java.lang.Thread.sleep; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.StatFs; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import androidx.annotation.NonNull; import com.isa.navi.jni.hmi.update.USBListener; import com.isa.navi.library.map.UpdateEnvironment; import com.isa.navi.library.map.UpdateInfoBean; import com.isa.navi.manager.CallBackManager; import com.isa.navi.manager.CarManager; import com.isa.navi.manager.base.BaseHandlerManager; import com.isa.navi.receiver.USBReceiver; import com.isa.navi.utils.GsonUtils; import com.isa.navi.utils.LogUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.InputStreamReader; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; public class HmiJNIImpl extends BaseHandlerManager implements IHmiJNI { private static final String TAG = "UPDATE"; private static final int MSG_DOWNLOAD_STATUS_CHANGE = 1; private final HmiProxyJNI mHmiProxyJNI; private final Format mFormat; private static final String PASSWORD_PREFIX = "DfCs"; private static final String PASSWORD_SUFFIX = "#@"; private static final int LAST_CHARS_LENGTH = 4; private static String updateUSBpath = ""; /** * 车辆管理实例,用于管理车辆相关的功能。 */ private CarManager mCarManager; private volatile static HmiJNIImpl mInstance; private boolean is_run = false; private final Set<String> USBPathSet = Collections.synchronizedSet(new HashSet<>()); public static HmiJNIImpl getInstance() { if (mInstance == null) { synchronized (HmiJNIImpl.class) { if (mInstance == null) { mInstance = new HmiJNIImpl(); } } } return mInstance; } private HmiJNIImpl() { super(true); mHmiProxyJNI = new HmiProxyJNI(); mFormat = Format.getInstance(); mCarManager = CarManager.getInstance(); } @Override public void initialize() { super.initialize(); LogUtils.d(TAG, "initialize : "); DfCert.getInstance().getService(); CallBackManager.getInstance().registerUSBEventChangeListener(new USBListener() { @Override public void path(String mountPath) { USBPathSet.add(mountPath); LogUtils.i(TAG, "USBPathSet:" + mountPath); } }); LogUtils.i(TAG, "Path:" + mContext.getFilesDir()); // 初始化C++模块 mHmiProxyJNI.InitializeCpp(); InstanceEngine(); //decryptUtilTest(); // UpdateTest(); } // 辅助方法:记录访问被拒绝的详细原因 private void logAccessDeniedReason(File dir) { // 记录 SELinux 拒绝日志 try { Process process = Runtime.getRuntime().exec("dmesg | grep avc"); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); String line; boolean found = false; while ((line = reader.readLine()) != null) { if (line.contains("avc: denied") && line.contains(dir.getName())) { LogUtils.e(TAG, "SELinux拒绝记录: " + line); found = true; } } if (!found) { LogUtils.i(TAG, "未找到相关的SELinux拒绝记录"); } } catch (Exception e) { LogUtils.e(TAG, "获取SELinux日志失败"+e); } // 记录文件系统权限 try { Process process = Runtime.getRuntime().exec("ls -ld " + dir.getAbsolutePath()); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { LogUtils.i(TAG, "详细权限信息: " + line); } } catch (Exception e) { LogUtils.e(TAG, "获取详细权限失败"+e); } // 记录当前应用权限 LogUtils.i(TAG, "应用权限上下文: " + getSELinuxContext()); } // 获取当前应用的 SELinux 安全上下文 private String getSELinuxContext() { try { Process process = Runtime.getRuntime().exec("id -Z"); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); return reader.readLine(); } catch (Exception e) { return "未知: " + e.getMessage(); } } // public void upDateTest(){ // Thread UpdateTest = new Thread(new Runnable(){ // @Override // public void run() { // LogUtils.i(TAG, "upDateTest start"); // startUSBDetection(); // } // }); // // UpdateTest.setName("ISA_NAVI_upDateTest"); // UpdateTest.start(); // } public void UpdateTest() { Thread UpdateTest = new Thread(new Runnable(){ @Override public void run() { LogUtils.i(TAG, "UpdateTest start!"); setUSBPath("/data/user/0/com.isa.navi/usbpath/KVM_EU_202502_T1.0_266dd24ba609476c99293f0ef7403a9f.zip"); updateMap(); } }); UpdateTest.setName("ISA_NAVI_UpdateTest"); UpdateTest.start(); } // // public void decryptUtilTest(){ // Thread UpdateTest = new Thread(new Runnable(){ // @Override // public void run() { // try { // sleep(20); // LogUtils.i(TAG, "decryptUtilTest start"); // String encryptedZipPath = "/map/KVM_EU_202502_T1.0_266dd24ba609476c99293f0ef7403a9f.zip"; // boolean isSuccess = dataVerification(encryptedZipPath); // LogUtils.i(TAG, "验签和解密结果: " + (isSuccess ? "成功" : "失败")); // } catch (Exception e) { // LogUtils.e(TAG, "测试异常: " + e.getMessage()); // } // } // }); // // UpdateTest.setName("ISA_NAVI_decryptUtilTest"); // UpdateTest.start(); // } //读取DES秘钥文本 public String readKeyFromFile(String keyDataPath) { File keyFile = new File(keyDataPath); StringBuilder keyBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new FileReader(keyFile))) { String line; while ((line = reader.readLine()) != null) { // 移除行首尾空白字符(包括换行符) String trimmedLine = line.trim(); // 跳过空行和注释行(以#开头的行) if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) { continue; } keyBuilder.append(trimmedLine); } // 返回读取到的内容 return keyBuilder.toString(); } catch (IOException e) { // 处理文件读取异常 System.err.println("读取文件失败: " + e.getMessage()); e.printStackTrace(); return null; } } public boolean desDecrypt(String DesDataPath,String key){ LogUtils.i(TAG, "encryptedZipPath[encryptedZipPath]:" + DesDataPath); LogUtils.i(TAG, "key:" + key); String signPath = get_Global_m_data_address() +"sign/"; String downLoadPath = get_Global_m_data_address(); //解压加密数据 LogUtils.i(TAG, "DesDataPath[" + DesDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(DesDataPath, signPath)) { //删除des解密中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除des解密中间文件!"); } return false; } String str = ZipUtils.getInstance().findFiles(signPath); LogUtils.i(TAG, " des文件[" + str + "]"); //解密des文件 if(!desDecryptFile(new File(str), downLoadPath, key)) { LogUtils.e(TAG, "DesdecryptFile fail!"); //删除des解密中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除Des解密中间文件!"); } return false; } //删除des解密中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除des解密中间文件!"); } LogUtils.i(TAG, "desDecryptUtil success"); return true; } private int[] getCurrentYearAndQuarter() { int[] result = new int[2]; LocalDate currentDate = LocalDate.now(); // 获取当前年份 int currentYear = currentDate.getYear(); // 获取当前月份 Month currentMonth = currentDate.getMonth(); // 根据月份确定当前季度 int currentQuarter; switch (currentMonth) { case JANUARY: case FEBRUARY: case MARCH: currentQuarter = 1; break; case APRIL: case MAY: case JUNE: currentQuarter = 2; break; case JULY: case AUGUST: case SEPTEMBER: currentQuarter = 3; break; case OCTOBER: case NOVEMBER: case DECEMBER: currentQuarter = 4; break; default: currentQuarter = 0; } result[0] = currentYear; result[1] = currentQuarter; return result; } private int[] parseMapInfo(String mapInfo) { int[] result = new int[2]; // 检查输入字符串的长度是否为6(4位年份 + 2位季度) if (mapInfo == null || mapInfo.length() != 6) { LogUtils.e(TAG, "Input string must be in the format YYYYMM where YYYY is the year and MM is the quarter as a two-digit number."); return result; } // 提取年份(前4位) String yearStr = mapInfo.substring(0, 4); result[0] = Integer.parseInt(yearStr); // 提取季度(后2位),并验证它在1到4的范围内 String quarterStr = mapInfo.substring(4, 6); int quarter = Integer.parseInt(quarterStr); if (quarter < 1 || quarter > 4) { LogUtils.e(TAG, "Quarter must be between 1 and 4."); return result; } result[1] = quarter; return result; } public void dataVersionCheck() { Thread dataVersionCheckThread = new Thread(new Runnable() { @Override public void run() { try { UpdateInfoBean bean = new UpdateInfoBean(); String localVersion = mHmiProxyJNI.getMapInfo(); if(localVersion.isEmpty()) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); return; } int[] currentYearAndQuarter = getCurrentYearAndQuarter(); int[] localYearAndQuarter = parseMapInfo(localVersion); int yearDifference = currentYearAndQuarter[0] - localYearAndQuarter[0]; int QuarterDifference = currentYearAndQuarter[1] - localYearAndQuarter[1]; if(yearDifference * 4 + QuarterDifference > 3) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); } } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 dataVersionCheckThread.setName("ISA_NAVI_dataVersionCheck"); // 启动线程 dataVersionCheckThread.start(); } public void setContext(Context context) { if(mContext == null) { mContext = context; } language.getInstance().setContext(mContext); } @Override protected void handleMessage(Message msg) { switch (msg.what) { case MSG_DOWNLOAD_STATUS_CHANGE: handleDownloadStatusChange((String) msg.obj); break; default: break; } } private List<String> JsonArrayToListExample(JSONArray jsonArray) { List<String> stringList = new ArrayList<>(); try { // 遍历JSONArray for (int i = 0; i < jsonArray.length(); i++) { // 由于我们假设JSONArray只包含字符串,所以我们可以直接getString String item = jsonArray.getString(i); // 将字符串添加到列表中 stringList.add(item); } } catch (JSONException e) { e.printStackTrace(); } return stringList; } private UpdateInfoBean jsonToUpdateInfoBean(String json) { UpdateInfoBean result = null; try { // 解析整个JSON字符串为一个JSONObject JSONObject rootObject = new JSONObject(json); // 解析mUpdateMessage对象 JSONObject mUpdateMessage = rootObject.getJSONObject("mUpdateMessage"); String country = mUpdateMessage.getString("country"); double downloadSize = mUpdateMessage.getDouble("downloadSize"); boolean isCurrentCountry = mUpdateMessage.getBoolean("isCurrentCountry"); double progress = mUpdateMessage.getDouble("progress"); double totalSize = mUpdateMessage.getDouble("totalSize"); int updateStatus = mUpdateMessage.getInt("updateStatus"); String version = mUpdateMessage.getString("version"); // 解析mUpdateNotification对象 JSONObject mUpdateNotification = rootObject.getJSONObject("mUpdateNotification"); JSONArray updateException = mUpdateNotification.getJSONArray("UpdateException"); JSONArray updateFailedList = mUpdateNotification.getJSONArray("UpdateFailedList"); boolean updateResult = mUpdateNotification.getBoolean("UpdateResult"); JSONArray updateSuccessfulList = mUpdateNotification.getJSONArray("UpdateSuccessfulList"); // 解析mapStatus int mapStatus = rootObject.getInt("mapStatus"); UpdateInfoBean.UpdateNotification temp1 = new UpdateInfoBean.UpdateNotification(updateResult,JsonArrayToListExample(updateException),JsonArrayToListExample(updateSuccessfulList),JsonArrayToListExample(updateFailedList)); UpdateInfoBean.UpdateMessage temp2 = new UpdateInfoBean.UpdateMessage(country, version, totalSize, (int) progress, isCurrentCountry); temp2.setUpdateStatus(updateStatus); temp2.setDownloadSize(downloadSize); result = new UpdateInfoBean(mapStatus, temp1, temp2); } catch (Exception e) { LogUtils.e(TAG, "JSON parsing failed:" + e.toString()); } return result; } private void handleDownloadStatusChange(String json) { UpdateInfoBean bean = jsonToUpdateInfoBean(json); LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } } private void handleDownloadStatusChange(UpdateInfoBean bean) { LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } else { LogUtils.e(TAG, "UpdateInfoBean is null!"); } } public void sendUpdateMessage(String json) { LogUtils.d(TAG, "sendUpdateMessage : " + json); Message message = Message.obtain(); message.what = MSG_DOWNLOAD_STATUS_CHANGE; message.obj = json; mHandler.sendMessage(message); } @Override public void updateMap() { LogUtils.d(TAG, "activeUpdateStart : "); // 检查更新过程是否已经在运行,防止重复创建线程 if(is_run) { LogUtils.i(TAG, "更新程序已经在进行"); return; }else { is_run = true; } // 创建一个新线程来执行主动更新 Thread updateMapThread = new Thread(new Runnable() { @Override public void run() { try { // 调用JNI方法开始主动更新 mHmiProxyJNI.updateMap(); LogUtils.d(TAG, "activeUpdateStart end: "); } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 updateMapThread.setName("ISA_NAVI_UpdateMapThread"); // 启动线程 updateMapThread.start(); } public void updateMapend() { is_run = false; } @Override public void suspendUpdate(boolean suspend) { mHmiProxyJNI.suspendUpdate(suspend); } @Override public void cancelUpdate() { mHmiProxyJNI.cancelUpdate(); } /** * 启动USB检测线程,用于检测插入的U盘并判断是否有可用的升级文件。 */ public void startUSBDetection() { LogUtils.i(TAG, "startUSBDetection START"); // setUSBPath("/data/KVM_EU_202404_T1.2.zip"); Thread USBDetectionThread = new Thread(new Runnable(){ @Override public void run() { UpdateInfoBean bean = new UpdateInfoBean(); boolean isHaveUSBData = false; //遍历检测到的U盘路径 if(USBPathSet.isEmpty()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } int set_num = 0; for(String usbPath : USBPathSet ) { //判断U盘路径是否真实存在 set_num ++; if(USBReceiver.isMounted(usbPath)) { File usbFile = new File(usbPath); File[] listFiles = usbFile.listFiles(); if(listFiles == null) { continue; } File newfile = null; String file_str = null; for(File file : listFiles) { LogUtils.i(TAG, "file:" + file.getAbsolutePath()); //判断U盘内是否含有升级文件 if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) { isHaveUSBData = true; if(newfile == null) { newfile = file; file_str = newfile.getAbsolutePath(); } else { if(Format.getInstance().oldIsNewVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) { newfile = file; file_str = newfile.getAbsolutePath(); } } } } //未检测到U盘指定路径下是否有数据,触发提示弹窗 if(!isHaveUSBData || newfile == null) { LogUtils.e(TAG, "The USB drive does not contain an upgrade package!"); bean.setUpdateStatus(3); handleDownloadStatusChange(bean); return; }else{ // 从文件名中提取版本号-zwx // String localDataPath = Format.getInstance().extractVersion(getCountryDbPath()); String localDataPath = Format.getInstance().extractVersionFromDbPath(getCountryDbPath());//202501_T1.0 String newDataPath = Format.getInstance().extractVersionFromDbPath(file_str);//202502_T1.0 LogUtils.i(TAG, "file name:" + newDataPath); LogUtils.i(TAG, "localDataPath:" + localDataPath); if(localDataPath.isEmpty()) { String mapinfo = getMapInfo(); if(!mapinfo.isEmpty()) { String[] newVersionParts = new String[2]; newVersionParts = Format.getInstance().parseVersion(newDataPath); String newUpdateVersion = newVersionParts[0]; // 取第一部分:202502 LogUtils.i(TAG, "newUpdateVersion:" + newUpdateVersion); String newReleasesVersion = newVersionParts[1];//取第二部分:01/02 LogUtils.i(TAG, "newReleasesVersion:" + newReleasesVersion); LogUtils.i(TAG, "localDataPath.isEmpty() newUpdateVersion: " + newUpdateVersion); LogUtils.i(TAG, "localDataPath.isEmpty() newReleasesVersion: " + newReleasesVersion); if(Integer.parseInt(newUpdateVersion) > Integer.parseInt(mapinfo) || (Integer.parseInt(newUpdateVersion) == Integer.parseInt(mapinfo) && Double.parseDouble(newReleasesVersion) > 1)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } } else { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } return; } // 判断U盘内数据版本是否大于车机数据版本 if(Format.getInstance().isNewerVersion(newDataPath, localDataPath)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } return; } } else { if(set_num == USBPathSet.size()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } } } } }); USBDetectionThread.setName("ISA_NAVI_USBDetectionThread"); USBDetectionThread.start(); } public String getMapInfo() { String result = mHmiProxyJNI.getMapInfo(); LogUtils.i(TAG,"MapInfo:" + result); return result; } public String getUpdateVersion() { String result = mHmiProxyJNI.getUpdateVersion(); LogUtils.i(TAG,"UpdateVersion:" + result); return result; } public UpdateEnvironment checkUpdateEnvironment() { String json = mHmiProxyJNI.checkUpdateEnvironment(); LogUtils.d(TAG,"checkUpdateEnvironment JSON:" + json); UpdateEnvironment result = GsonUtils.fromJson(json, UpdateEnvironment.class); if(result == null) { LogUtils.e(TAG, "GsonUtils.fromJson(json, UpdateEnvironment.class) Fail!"); return null; } if (!result.getResult()) { List<String> temp_list = new ArrayList<>(); for (int i : result.getInt_reason()) { String str = language.getInstance().getEnvironment(i); if(str != null && !str.isEmpty()) { temp_list.add(str); } } // 更新temp对象中的reason字段 result.setReason(temp_list); } LogUtils.i(TAG,"checkUpdateEnvironment :" + result); return result; } public String getUpdatePurpose() { return language.getInstance().getUpdatePurpose(); } public String getUpdateContent() { return language.getInstance().getUpdateContent(); } public String getUserManual() { return language.getInstance().getUserManual(); } public String getUpdatedContentDescription() { return language.getInstance().getUpdatedContentDescription(); } public int getEstimatedTime() { return 30; } private void InstanceEngine() { Thread InstanceEngine = new Thread(new Runnable(){ @Override public void run() { LogUtils.d("ISA_NAVI", "InstanceEngine start!"); mHmiProxyJNI.instanceEngine(); } }); InstanceEngine.setName("ISA_NAVI_InstanceEngine"); InstanceEngine.start(); } // 获取后四位字符 @NonNull private static String getLastFourChars(String mapInfo) { if (mapInfo == null || mapInfo.isEmpty()) { LogUtils.e(TAG, "mapInfo cannot be null or empty"); return "0000"; } return mapInfo.length() >= LAST_CHARS_LENGTH ? mapInfo.substring(mapInfo.length() - LAST_CHARS_LENGTH) : "0000"; } // 生成密码 @NonNull private String generatePassword() { String mapInfo = getMapInfo(); String lastFourChars = getLastFourChars(mapInfo); return PASSWORD_PREFIX + lastFourChars + PASSWORD_SUFFIX; } // 校验密码 public boolean validatePassword(String inputPassword) { if (inputPassword == null) { return false; } String generatedPassword = generatePassword(); LogUtils.i(TAG, "generatedPassword:" + generatedPassword); LogUtils.i(TAG, "inputPassword:" + inputPassword); return inputPassword.equals(generatedPassword); } public void setUSBPath(String USBPath) { mHmiProxyJNI.setUSBPath(USBPath); } public boolean unzipFile(String zipFilePath, String destinationDirPath) { return ZipUtils.getInstance().unzip(zipFilePath, destinationDirPath); } public long getFileSize(String filePath) { File file = new File(filePath); if(!file.isFile() || !file.exists()) { return 0; } LogUtils.d(TAG, "getFileSize:" + file.length()); return file.length(); } public long getAvailableSpace(String filePath) { StatFs stat = new StatFs(filePath); long availableBlocks = stat.getAvailableBlocksLong(); long blockSize = stat.getBlockSizeLong(); LogUtils.i(TAG, "[AvailableSpace]:" + availableBlocks * blockSize + "字节"); return availableBlocks * blockSize; } public String getFail(int i) { return language.getInstance().getFail(i); } public boolean decryptFile(String encryptedFilePath, String decryptedFilePath) { return new DecryptUtil().decryptFile(encryptedFilePath, decryptedFilePath, DfCert.getInstance().getKEY_AES_256()); } public boolean desDecryptFile(File desDaraPath,String downLoadPath,String key){ return new DecryptUtil().desDecryptFile(desDaraPath, downLoadPath, key); } public boolean verifySignature(String filePath, String signFilePath) { try { return DecryptUtil.verifySignature(filePath, signFilePath, DfCert.getInstance().getPUBLIC_KEY()); } catch (Exception e) { return false; } } /** * 获取高压电池电量百分比。 * * 此方法通过调用mCarManager.getHVPercent来获取高压电池的电量百分比。如果调用成功,则返回电量百分比值; * 如果调用失败或发生异常,则捕获异常并打印堆栈跟踪,最后返回0。 * * @return 一个浮点数,表示高压电池的电量百分比(0-100);如果获取失败或发生异常,则返回0。 */ public float getHVPercent() { float HVPercent = 0; try { HVPercent = mCarManager.getHVPercent(); if(HVPercent > 100 || HVPercent < 0) { HVPercent = 0; } } catch (Exception e) { e.printStackTrace(); } return HVPercent; } boolean isMounted() { return USBReceiver.isMounted(updateUSBpath); } public String getCountryDbPath() { return mHmiProxyJNI.getCountryDbPath(); } public String get_Global_m_data_address() { return mHmiProxyJNI.get_Global_m_data_address(); } public boolean dataVerification(String verifyDataPath) { LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath); String signPath = get_Global_m_data_address() + "sign/"; if(!mapControl()) { return false; } //解压加密数据 LogUtils.i(TAG, "verifyDataPath[" + verifyDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(verifyDataPath, signPath)) { //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //提取签名文件以及加密文件的绝对路径 String[] temp = ZipUtils.getInstance().findEncryptedZipFiles(signPath); String aesDaraPath = temp[1]; String signDataPtah = temp[0]; String KeyDataPath = temp [2]; LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath); LogUtils.i(TAG, "signDataPtah" + signDataPtah); LogUtils.i(TAG, "KeyDataPath" + KeyDataPath); if(aesDaraPath == null || signDataPtah == null || KeyDataPath == null){ return false; } String key = readKeyFromFile(KeyDataPath); if(!mapControl()) { return false; } //验证签名 if(!verifySignature(aesDaraPath, signDataPtah)) { LogUtils.e(TAG, "verifySignature fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //对称解密Zip if(!decryptFile(aesDaraPath, verifyDataPath)) { LogUtils.e(TAG, "decryptFile fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } //des解密 LogUtils.i(TAG, "verifyDataPath:" + verifyDataPath); boolean isSuccess = desDecrypt(verifyDataPath,key); if(!isSuccess){ LogUtils.i(TAG, "DES解密结果: 失败" ); return false; } LogUtils.i(TAG, "DES解密结果:成功 " ); LogUtils.i(TAG, "dataVerification success"); return true; } /** * 删除指定目录及其所有内容 * * @param directory 指定的目录路径 * @return 如果删除成功返回 true,否则返回 false */ public boolean deleteDirectory(String directory) { File dir = new File(directory); if (!dir.exists()) { LogUtils.e("目录不存在: " + directory); return false; // 目录不存在,视为删除失败 } if (!dir.isDirectory()) { LogUtils.e("指定路径不是一个目录: " + directory); return false; // 路径不是目录,无法删除 } return deleteRecursive(dir); // 递归删除目录 } /** * 递归删除文件或目录 * * @param file 文件或目录 * @return 如果删除成功返回 true,否则返回 false */ private boolean deleteRecursive(File file) { if (file.isDirectory()) { // 如果是目录,递归删除其所有子文件和子目录 File[] files = file.listFiles(); if (files != null) { for (File subFile : files) { if (!deleteRecursive(subFile)) { return false; // 如果某个子文件或子目录删除失败,则整体失败 } } } } // 删除当前文件或空目录 if (!file.delete()) { LogUtils.e("无法删除文件或目录: " + file.getAbsolutePath()); return false; // 删除失败 } LogUtils.i("已成功删除: " + file.getAbsolutePath()); return true; // 删除成功 } public boolean mapControl() { LogUtils.d(TAG, "mapControl JAVA start"); return mHmiProxyJNI.mapControl(); } public boolean zipDbFiles(String sourceDir, String outputZipPath) { return ZipUtils.getInstance().zipDbFiles(sourceDir, outputZipPath); } public boolean zip(String sourcePath, String outputZipPath) { return ZipUtils.getInstance().zip(sourcePath, outputZipPath, true); } // Used to load the 'native-lib' library on application startup. static { LogUtils.d(TAG, "loadLibrary IsaEngineJni " + System.currentTimeMillis()); System.loadLibrary("IsaEngineJni"); } } package com.isa.navi.jni.hmi; import android.os.Build; import android.util.Base64; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.nio.charset.StandardCharsets; import java.io.*; import java.nio.charset.StandardCharsets; import android.util.Base64; import com.isa.navi.utils.LogUtils; import java.io.IOException; import java.io.ByteArrayInputStream; import java.security.PublicKey; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.List; import org.json.JSONObject; import org.json.JSONException; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.NoSuchAlgorithmException; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.MessageDigest; import java.security.Security; import java.security.Signature; import java.util.Objects; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * 对称解密管理类,负责提供对称解密相关方法。 */ public class DecryptUtil { /** * 用于日志记录的标签。 * 这是一个静态常量,用于在日志输出中标识下载管理器的相关日志。 */ private static final String TAG = "DecryptUtil"; private long lastCheckTime; static { Security.addProvider(new BouncyCastleProvider()); } /** * 对称解密文件 * @param encryptedFilePath 加密文件路径 * @param decryptedFilePath 解密后文件输出路径 * @param ENCRYPTION_KEY_BASE64 密钥 * @return 解密是否成功,成功返回true,失败返回false */ public boolean decryptFile(String encryptedFilePath, String decryptedFilePath, String ENCRYPTION_KEY_BASE64) { boolean success = false; try { // 创建密钥和IV规格 SecretKeySpec keySpec = new SecretKeySpec(getKey(ENCRYPTION_KEY_BASE64), "AES/CBC/PKCS5Padding"); IvParameterSpec ivSpec = new IvParameterSpec(getIV(ENCRYPTION_KEY_BASE64)); LogUtils.i(TAG, "创建密钥和IV规格 success!"); LogUtils.i(TAG, "创建密钥和IV规格 success!"); // 创建Cipher实例并初始化 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); LogUtils.i(TAG, "创建Cipher实例并初始化 success!"); if(!HmiJNIImpl.getInstance().mapControl()) { return false; } //读取文件进行对称解密,并将解密后的文件按照指定路径输出 try (FileInputStream fis = new FileInputStream(encryptedFilePath); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(decryptedFilePath); CipherOutputStream cos = new CipherOutputStream(fos, cipher)) { byte[] buffer = new byte[10240]; // 使用10KB的缓冲区 int bytesRead; while ((bytesRead = bis.read(buffer)) != -1) { cos.write(buffer, 0, bytesRead); // 优化后的检查逻辑(每秒最多1次) long currentTime = System.currentTimeMillis(); if (currentTime - lastCheckTime > 1000) { synchronized (this) { if (currentTime - lastCheckTime > 1000) { if (!HmiJNIImpl.getInstance().mapControl()) { return false; } lastCheckTime = currentTime; } } } } LogUtils.i(TAG, "解密并写入数据 success!"); success = true; } catch (IOException e) { LogUtils.e(TAG, "处理文件时发生错误:" + e.getMessage()); throw new IOException("文件处理失败", e); } } catch (Exception e) { // 捕获所有其他异常并记录错误 LogUtils.e(TAG, "解密文件时发生错误:" + e.getMessage()); } return success; } /** * 验签 * @param filePath 加密文件路径 * @param signFilePath 签名文件路径(.sig文件) * @param CERT_KEY 公钥 * @return 签名成功返回true,签名失败返回false */ public static boolean verifySignature(String filePath, String signFilePath, String CERT_KEY) throws Exception { if(!HmiJNIImpl.getInstance().mapControl()) { return false; } try{ // 获取加密文件的SHA256哈希值 byte[] fileHash = getFileHash(filePath); // 读取base64格式签名数据 String base64Signature = readSignatureFromFile(signFilePath); LogUtils.i("base64格式签名数据: " + base64Signature); // 解码签名 byte[] signatureBytes = Base64.decode(base64Signature,Base64.DEFAULT); // 加载公钥 PublicKey publicKey = loadPublicKey(CERT_KEY); LogUtils.i("PUBLIC_KEY: " + publicKey); if(!HmiJNIImpl.getInstance().mapControl()) { return false; } // 验签 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(fileHash); return signature.verify(signatureBytes); } catch (Exception e){ e.printStackTrace(); return false; } } public boolean desDecryptFile(File desDataPath, String downLoadPath, String key) { final int BUFFER_SIZE = 8 * 1024 * 1024; // 8MB缓冲区 try { // // 生成密钥(与加密时一致) // byte[] utf8Bytes = key.getBytes(StandardCharsets.UTF_8); // // // 2. Base64 编码(使用 Base64.DEFAULT,无换行) // String base64Str = Base64.encodeToString(utf8Bytes,Base64.DEFAULT); // // // 3. 取前16个字符 // String result = base64Str.substring(0, 16); // // byte[] byteStream = result.getBytes(StandardCharsets.UTF_8); // SecretKeySpec secretKeySpec = new SecretKeySpec(byteStream, "DES"); LogUtils.i(TAG,"开始解析des秘钥"); DESKeySpec desKeySpec = new DESKeySpec(Base64.encode(key.getBytes("utf-8"), Base64.DEFAULT)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(desKeySpec); LogUtils.i(TAG,"des秘钥解析成功"); LogUtils.i(TAG,"初始化DES秘钥"); // 初始化DES解密Cipher Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); LogUtils.i(TAG,"秘钥初始化完成"); LogUtils.i(TAG,"开始解析文件"); // 解析文件名 String name = desDataPath.getName(); int lastDotIndex = name.lastIndexOf("."); String fileExtension = (lastDotIndex == -1) ? "" : name.substring(lastDotIndex + 1); int firstIndex = name.indexOf("_"); String fileNameContent = (firstIndex == -1) ? name : name.substring(0, firstIndex); // 创建输出文件 File outputFile = new File(downLoadPath, fileNameContent + "." + fileExtension); try (InputStream inputStream = new FileInputStream(desDataPath); BufferedInputStream bis = new BufferedInputStream(inputStream); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0+ 使用流式Base64解码 LogUtils.i("Android 8.0+ 使用流式Base64解码"); java.util.Base64.Decoder base64Decoder = java.util.Base64.getMimeDecoder(); try (InputStream base64DecodeStream = base64Decoder.wrap(bis); CipherInputStream cis = new CipherInputStream(base64DecodeStream, cipher)) { byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = cis.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); // 优化后的检查逻辑(每秒最多1次) long currentTime = System.currentTimeMillis(); if (currentTime - lastCheckTime > 1000) { synchronized (this) { if (currentTime - lastCheckTime > 1000) { if (!HmiJNIImpl.getInstance().mapControl()) { return false; } lastCheckTime = currentTime; } } } } } } } LogUtils.i("文件解密成功,保存为: " + outputFile.getAbsolutePath()); return true; } catch (Exception e) { // 修复5:添加详细错误日志 LogUtils.e(TAG, "解密失败: " + e.getMessage() + "\n文件: " + desDataPath.getAbsolutePath() + "\n密钥: " + key.substring(0, Math.min(4, key.length())) + "***"); return false; } } // 处理Base64数据块:解码->解密->写入 private void processBase64Chunk(ByteArrayOutputStream base64Buffer, Cipher cipher, BufferedOutputStream bos) throws Exception { byte[] base64Data = base64Buffer.toByteArray(); base64Buffer.reset(); byte[] decodedData = Base64.decode(base64Data, Base64.DEFAULT); byte[] decryptedData = cipher.update(decodedData); if (decryptedData != null) { bos.write(decryptedData); } } // 从 .sig 文件读取签名 public static String readSignatureFromFile(String sigFilePath) throws IOException , JSONException{ byte[] fileBytes = new byte[(int) new File(sigFilePath).length()]; try (FileInputStream fis = new FileInputStream(sigFilePath)) { fis.read(fileBytes); } String fileContent = new String(fileBytes).trim(); // 解析 JSON,提取 signature 字段 JSONObject json = new JSONObject(fileContent); return json.getString("signature"); } // 获取byte数组格式向量 private static byte[] getIV(String iv) { // 1. 清理密钥 String cleanKey = iv.replaceAll("\\?+$", ""); // 2. 提取第一个!到第二个!之间的部分 String[] parts = cleanKey.split("!"); if (parts.length < 2) { throw new IllegalArgumentException("Invalid key format"); } String prefix = parts[1]; String reversed = new StringBuilder(prefix.substring(0, 7)) .reverse() .append("!") .toString(); reversed = reversed + "abcdefg!"; return reversed.getBytes(StandardCharsets.UTF_8); } // 获取byte数组格式秘钥 private static byte[] getKey(String password) { String cleaned = password.replaceAll("[^!$a-zA-Z0-9]", ""); return cleaned.getBytes(StandardCharsets.UTF_8); } // 获取加密文件的SHA256哈希值 public static byte[] getFileHash(String filePath) throws IOException { try{ MessageDigest digest = MessageDigest.getInstance("SHA-256"); File file = new File(filePath); try (FileInputStream fis = new FileInputStream(file)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { digest.update(buffer, 0, bytesRead); } } return digest.digest(); }catch (NoSuchAlgorithmException e){ e.printStackTrace(); return null; } } // 加载验签公钥 public static PublicKey loadPublicKey(String base64PublicKey) throws Exception { base64PublicKey = base64PublicKey.replaceAll("\\s+", ""); base64PublicKey = base64PublicKey.replaceAll("[^A-Za-z0-9+/=]", ""); // 1. Base64解码 byte[] certBytes = Base64.decode(base64PublicKey,Base64.DEFAULT); // 2. 解析证书链(PKCS#7/P7B格式) CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream bis = new ByteArrayInputStream(certBytes); List<X509Certificate> certChain = (List<X509Certificate>) cf.generateCertificates(bis); // 3. 提取第一个证书的公钥(通常是终端实体证书) if (certChain.isEmpty()) { throw new IllegalArgumentException("证书链为空!"); } return certChain.get(0).getPublicKey(); } } 分析这两段代码

package com.isa.navi.jni.hmi; import android.content.Context; import android.os.Message; import android.os.StatFs; import android.util.Log; import androidx.annotation.NonNull; import com.isa.navi.jni.hmi.bean.UpdateList; import com.isa.navi.jni.hmi.update.USBListener; import com.isa.navi.library.map.UpdateEnvironment; import com.isa.navi.library.map.UpdateInfoBean; import com.isa.navi.manager.CallBackManager; import com.isa.navi.manager.CarManager; import com.isa.navi.manager.base.BaseHandlerManager; import com.isa.navi.receiver.USBReceiver; import com.isa.navi.utils.GsonUtils; import com.isa.navi.utils.LogUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class HmiJNIImpl extends BaseHandlerManager implements IHmiJNI { private static final String TAG = "UPDATE"; private static final int MSG_DOWNLOAD_STATUS_CHANGE = 1; private final HmiProxyJNI mHmiProxyJNI; private final Format mFormat; private static final String PASSWORD_PREFIX = "DfCs"; private static final String PASSWORD_SUFFIX = "#@"; private static final int LAST_CHARS_LENGTH = 4; private static String updateUSBpath = ""; //zwj-s // 标记 C++ 是否已完成初始化(由 JNI 调用 onCppInitializationCompleted 设置) public volatile boolean mCppInitialized = false; public final Object mLock = new Object(); // 标记 dataVersionCheck 是否已触发(防止重复执行) public volatile boolean mDataVersionCheckTriggered = false; // 缓存最后一次检测结果,用于新注册的监听器补发 public volatile UpdateInfoBean mLastUpdateInfoBean = null; //zwj-s /** * 车辆管理实例,用于管理车辆相关的功能。 */ private CarManager mCarManager; private volatile static HmiJNIImpl mInstance; private boolean is_run = false; private final Set<String> USBPathSet = Collections.synchronizedSet(new HashSet<>()); public static HmiJNIImpl getInstance() { if (mInstance == null) { synchronized (HmiJNIImpl.class) { if (mInstance == null) { mInstance = new HmiJNIImpl(); } } } return mInstance; } private HmiJNIImpl() { super(true); mHmiProxyJNI = new HmiProxyJNI(); mFormat = Format.getInstance(); mCarManager = CarManager.getInstance(); } @Override public void initialize() { super.initialize(); LogUtils.d(TAG, "initialize : "); DfCert.getInstance().getService(); CallBackManager.getInstance().registerUSBEventChangeListener(new USBListener() { @Override public void path(String mountPath) { USBPathSet.add(mountPath); LogUtils.i(TAG,"USBPathSet:" + mountPath); } }); // 初始化C++模块 mHmiProxyJNI.InitializeCpp(); InstanceEngine(); onCppInitializationCompleted();//zwj-s } private int[] getCurrentYearAndQuarter() { int[] result = new int[2]; LocalDate currentDate = LocalDate.now(); // 获取当前年份 int currentYear = currentDate.getYear(); // 获取当前月份 Month currentMonth = currentDate.getMonth(); // 根据月份确定当前季度 int currentQuarter; switch (currentMonth) { case JANUARY: case FEBRUARY: case MARCH: currentQuarter = 1; break; case APRIL: case MAY: case JUNE: currentQuarter = 2; break; case JULY: case AUGUST: case SEPTEMBER: currentQuarter = 3; break; case OCTOBER: case NOVEMBER: case DECEMBER: currentQuarter = 4; break; default: currentQuarter = 0; } result[0] = currentYear; result[1] = currentQuarter; return result; } private int[] parseMapInfo(String mapInfo) { int[] result = new int[2]; // 检查输入字符串的长度是否为6(4位年份 + 2位季度) if (mapInfo == null || mapInfo.length() != 6) { LogUtils.e(TAG, "Input string must be in the format YYYYMM where YYYY is the year and MM is the quarter as a two-digit number."); return result; } // 提取年份(前4位) String yearStr = mapInfo.substring(0, 4); result[0] = Integer.parseInt(yearStr); // 提取季度(后2位),并验证它在1到4的范围内 String quarterStr = mapInfo.substring(4, 6); int quarter = Integer.parseInt(quarterStr); if (quarter < 1 || quarter > 4) { LogUtils.e(TAG, "Quarter must be between 1 and 4."); return result; } result[1] = quarter; return result; } //zwj-s /** * C++ 层初始化完成的回调方法 * 必须由 JNI 在 InitializeCpp() 执行完毕后调用 * 此方法会触发全局的地图版本检测 */ public void onCppInitializationCompleted() { synchronized (mLock) { if (mCppInitialized) { LogUtils.d(TAG, "onCppInitializationCompleted: Already initialized, skip"); return; } mCppInitialized = true; LogUtils.i(TAG, "C++ initialization completed. Triggering dataVersionCheck..."); } // 触发一次全局版本检测 dataVersionCheck(); } //zwj-s public void dataVersionCheck() { //zwj-s synchronized (mLock) { if (!mCppInitialized) { LogUtils.e(TAG, "dataVersionCheck: C++ not initialized yet. Will be skipped or deferred."); return; } if (mDataVersionCheckTriggered) { LogUtils.d(TAG, "dataVersionCheck: Already triggered, skipping duplicate call."); return; } mDataVersionCheckTriggered = true; LogUtils.i(TAG, "Starting dataVersionCheck execution..."); } //zwj-s Thread dataVersionCheckThread = new Thread(new Runnable() { @Override public void run() { try { UpdateInfoBean bean = new UpdateInfoBean(); String localVersion = mHmiProxyJNI.getMapInfo(); if(localVersion.isEmpty()) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); return; } int[] currentYearAndQuarter = getCurrentYearAndQuarter(); int[] localYearAndQuarter = parseMapInfo(localVersion); int yearDifference = currentYearAndQuarter[0] - localYearAndQuarter[0]; int QuarterDifference = currentYearAndQuarter[1] - localYearAndQuarter[1]; if(yearDifference * 4 + QuarterDifference > 3) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); } } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 dataVersionCheckThread.setName("ISA_NAVI_dataVersionCheck"); // 启动线程 dataVersionCheckThread.start(); } public void setContext(Context context) { if(mContext == null) { mContext = context; } language.getInstance().setContext(mContext); } @Override protected void handleMessage(Message msg) { switch (msg.what) { case MSG_DOWNLOAD_STATUS_CHANGE: handleDownloadStatusChange((String) msg.obj); break; default: break; } } private List<String> JsonArrayToListExample(JSONArray jsonArray) { List<String> stringList = new ArrayList<>(); try { // 遍历JSONArray for (int i = 0; i < jsonArray.length(); i++) { // 由于我们假设JSONArray只包含字符串,所以我们可以直接getString String item = jsonArray.getString(i); // 将字符串添加到列表中 stringList.add(item); } } catch (JSONException e) { e.printStackTrace(); } return stringList; } private UpdateInfoBean jsonToUpdateInfoBean(String json) { UpdateInfoBean result = null; try { // 解析整个JSON字符串为一个JSONObject JSONObject rootObject = new JSONObject(json); // 解析mUpdateMessage对象 JSONObject mUpdateMessage = rootObject.getJSONObject("mUpdateMessage"); String country = mUpdateMessage.getString("country"); double downloadSize = mUpdateMessage.getDouble("downloadSize"); boolean isCurrentCountry = mUpdateMessage.getBoolean("isCurrentCountry"); double progress = mUpdateMessage.getDouble("progress"); double totalSize = mUpdateMessage.getDouble("totalSize"); int updateStatus = mUpdateMessage.getInt("updateStatus"); String version = mUpdateMessage.getString("version"); // 解析mUpdateNotification对象 JSONObject mUpdateNotification = rootObject.getJSONObject("mUpdateNotification"); JSONArray updateException = mUpdateNotification.getJSONArray("UpdateException"); JSONArray updateFailedList = mUpdateNotification.getJSONArray("UpdateFailedList"); boolean updateResult = mUpdateNotification.getBoolean("UpdateResult"); JSONArray updateSuccessfulList = mUpdateNotification.getJSONArray("UpdateSuccessfulList"); // 解析mapStatus int mapStatus = rootObject.getInt("mapStatus"); UpdateInfoBean.UpdateNotification temp1 = new UpdateInfoBean.UpdateNotification(updateResult,JsonArrayToListExample(updateException),JsonArrayToListExample(updateSuccessfulList),JsonArrayToListExample(updateFailedList)); UpdateInfoBean.UpdateMessage temp2 = new UpdateInfoBean.UpdateMessage(country, version, totalSize, (int) progress, isCurrentCountry); temp2.setUpdateStatus(updateStatus); temp2.setDownloadSize(downloadSize); result = new UpdateInfoBean(mapStatus, temp1, temp2); } catch (Exception e) { LogUtils.e(TAG, "JSON parsing failed:" + e.toString()); } return result; } private void handleDownloadStatusChange(String json) { UpdateInfoBean bean = jsonToUpdateInfoBean(json); LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } } private void handleDownloadStatusChange(UpdateInfoBean bean) { LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { //zwj-s if(bean.getUpdateStatus() == 4) { // 缓存最新状态,用于后续新注册者补发 mLastUpdateInfoBean = bean; } //zwj-end List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } else { LogUtils.e(TAG, "UpdateInfoBean is null!"); } } public void sendUpdateMessage(String json) { LogUtils.d(TAG, "sendUpdateMessage : " + json); Message message = Message.obtain(); message.what = MSG_DOWNLOAD_STATUS_CHANGE; message.obj = json; mHandler.sendMessage(message); } @Override public void updateMap() { LogUtils.d(TAG, "activeUpdateStart : "); // 检查更新过程是否已经在运行,防止重复创建线程 if(is_run) { LogUtils.i(TAG, "更新程序已经在进行"); return; }else { is_run = true; } // 创建一个新线程来执行主动更新 Thread updateMapThread = new Thread(new Runnable() { @Override public void run() { try { // 调用JNI方法开始主动更新 mHmiProxyJNI.updateMap(); LogUtils.d(TAG, "activeUpdateStart end: "); } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 updateMapThread.setName("ISA_NAVI_UpdateMapThread"); // 启动线程 updateMapThread.start(); } public void updateMapend() { is_run = false; } @Override public void suspendUpdate(boolean suspend) { mHmiProxyJNI.suspendUpdate(suspend); } @Override public void cancelUpdate() { mHmiProxyJNI.cancelUpdate(); } /** * 启动USB检测线程,用于检测插入的U盘并判断是否有可用的升级文件。 */ public void startUSBDetection() { LogUtils.i(TAG, "startUSBDetection START"); // setUSBPath("/data/KVM_EU_202404_T1.2.zip"); Thread USBDetectionThread = new Thread(new Runnable(){ @Override public void run() { UpdateInfoBean bean = new UpdateInfoBean(); boolean isHaveUSBData = false; //遍历检测到的U盘路径 if(USBPathSet.isEmpty()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } int set_num = 0; for(String usbPath : USBPathSet ) { //判断U盘路径是否真实存在 set_num ++; if(USBReceiver.isMounted(usbPath)) { File usbFile = new File(usbPath); File[] listFiles = usbFile.listFiles(); if(listFiles == null) { continue; } File newfile = null; for(File file : listFiles) { //判断U盘内是否含有升级文件 if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) { isHaveUSBData = true; if(newfile == null) { newfile = file; } else { if(Format.getInstance().isNewerVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) { newfile = file; } } } } //未检测到U盘指定路径下是否有数据,触发提示弹窗 if(!isHaveUSBData || newfile == null) { LogUtils.e(TAG, "The USB drive does not contain an upgrade package!"); bean.setUpdateStatus(3); handleDownloadStatusChange(bean); return; }else{ // 从文件名中提取版本号 String localDataPath = Format.getInstance().extractVersion(getCountryDbPath()); LogUtils.i(TAG, "file name:" + newfile.getName()); LogUtils.i(TAG, "localDataPath:" + localDataPath); if(localDataPath.isEmpty()) { String mapinfo = getMapInfo(); if(!mapinfo.isEmpty()) { String[] newVersionParts = new String[2]; Format.getInstance().extractVersion(newfile.getName().replaceAll("\\.\\w+$", ""), newVersionParts); String newUpdateVersion = newVersionParts[0]; String newReleasesVersion = newVersionParts[1]; LogUtils.i(TAG, "localDataPath.isEmpty() newUpdateVersion: " + newUpdateVersion); LogUtils.i(TAG, "localDataPath.isEmpty() newReleasesVersion: " + newReleasesVersion); if(Integer.parseInt(newUpdateVersion) > Integer.parseInt(mapinfo) || (Integer.parseInt(newUpdateVersion) == Integer.parseInt(mapinfo) && Double.parseDouble(newReleasesVersion) > 1)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } } else { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } return; } // 判断U盘内数据版本是否大于车机数据版本 if(Format.getInstance().isNewerVersion(newfile.getName().replaceAll("\\.\\w+$", ""), localDataPath)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } return; } } else { if(set_num == USBPathSet.size()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } } } } }); USBDetectionThread.setName("ISA_NAVI_USBDetectionThread"); USBDetectionThread.start(); } public String getMapInfo() { String result = mHmiProxyJNI.getMapInfo(); LogUtils.i(TAG,"MapInfo:" + result); return result; } public String getUpdateVersion() { String result = mHmiProxyJNI.getUpdateVersion(); LogUtils.i(TAG,"UpdateVersion:" + result); return result; } public UpdateEnvironment checkUpdateEnvironment() { String json = mHmiProxyJNI.checkUpdateEnvironment(); LogUtils.d(TAG,"checkUpdateEnvironment JSON:" + json); UpdateEnvironment result = GsonUtils.fromJson(json, UpdateEnvironment.class); if(result == null) { LogUtils.e(TAG, "GsonUtils.fromJson(json, UpdateEnvironment.class) Fail!"); return null; } if (!result.getResult()) { List<String> temp_list = new ArrayList<>(); for (int i : result.getInt_reason()) { String str = language.getInstance().getEnvironment(i); if(str != null && !str.isEmpty()) { temp_list.add(str); } } // 更新temp对象中的reason字段 result.setReason(temp_list); } LogUtils.i(TAG,"checkUpdateEnvironment :" + result); return result; } public String getUpdatePurpose() { return language.getInstance().getUpdatePurpose(); } public String getUpdateContent() { return language.getInstance().getUpdateContent(); } public String getUserManual() { return language.getInstance().getUserManual(); } public String getUpdatedContentDescription() { return language.getInstance().getUpdatedContentDescription(); } public int getEstimatedTime() { return 30; } private void InstanceEngine() { Thread InstanceEngine = new Thread(new Runnable(){ @Override public void run() { LogUtils.d("ISA_NAVI", "InstanceEngine start!"); mHmiProxyJNI.instanceEngine(); } }); InstanceEngine.setName("ISA_NAVI_InstanceEngine"); InstanceEngine.start(); } // 获取后四位字符 @NonNull private static String getLastFourChars(String mapInfo) { if (mapInfo == null || mapInfo.isEmpty()) { LogUtils.e(TAG, "mapInfo cannot be null or empty"); return "0000"; } return mapInfo.length() >= LAST_CHARS_LENGTH ? mapInfo.substring(mapInfo.length() - LAST_CHARS_LENGTH) : "0000"; } // 生成密码 @NonNull private String generatePassword() { String mapInfo = getMapInfo(); String lastFourChars = getLastFourChars(mapInfo); return PASSWORD_PREFIX + lastFourChars + PASSWORD_SUFFIX; } // 校验密码 public boolean validatePassword(String inputPassword) { if (inputPassword == null) { return false; } String generatedPassword = generatePassword(); LogUtils.i(TAG, "generatedPassword:" + generatedPassword); LogUtils.i(TAG, "inputPassword:" + inputPassword); return inputPassword.equals(generatedPassword); } public void setUSBPath(String USBPath) { mHmiProxyJNI.setUSBPath(USBPath); } public boolean unzipFile(String zipFilePath, String destinationDirPath) { return ZipUtils.getInstance().unzip(zipFilePath, destinationDirPath); } public long getFileSize(String filePath) { File file = new File(filePath); if(!file.isFile() || !file.exists()) { return 0; } LogUtils.d(TAG, "getFileSize:" + file.length()); return file.length(); } public long getAvailableSpace(String filePath) { StatFs stat = new StatFs(filePath); long availableBlocks = stat.getAvailableBlocksLong(); long blockSize = stat.getBlockSizeLong(); LogUtils.i(TAG, "[AvailableSpace]:" + availableBlocks * blockSize + "字节"); return availableBlocks * blockSize; } public String getFail(int i) { return language.getInstance().getFail(i); } public boolean decryptFile(String encryptedFilePath, String decryptedFilePath) { return new DecryptUtil().decryptFile(encryptedFilePath, decryptedFilePath, DfCert.getInstance().getKEY_AES_256()); } public boolean verifySignature(String filePath, String signFilePath) { try { return DecryptUtil.verifySignature(filePath, signFilePath, DfCert.getInstance().getPUBLIC_KEY()); } catch (Exception e) { return false; } } /** * 获取高压电池电量百分比。 * * 此方法通过调用mCarManager.getHVPercent来获取高压电池的电量百分比。如果调用成功,则返回电量百分比值; * 如果调用失败或发生异常,则捕获异常并打印堆栈跟踪,最后返回0。 * * @return 一个浮点数,表示高压电池的电量百分比(0-100);如果获取失败或发生异常,则返回0。 */ public float getHVPercent() { float HVPercent = 0; try { HVPercent = mCarManager.getHVPercent(); // LogUtils.i(TAG, "zwx:HVPercent_test:HVPercent_get:"+HVPercent); if(HVPercent > 100 || HVPercent < 0) { HVPercent = 0; } } catch (Exception e) { e.printStackTrace(); } return HVPercent; } boolean isMounted() { return USBReceiver.isMounted(updateUSBpath); } public String getCountryDbPath() { return mHmiProxyJNI.getCountryDbPath(); } public boolean dataVerification(String verifyDataPath) { LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath); String signPath = "/data/data/com.isa.navi/sign/"; if(!mapControl()) { return false; } //解压加密数据 LogUtils.i(TAG, "verifyDataPath[" + verifyDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(verifyDataPath, signPath)) { //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //提取签名文件以及加密文件的绝对路径 String[] temp = ZipUtils.getInstance().findEncryptedZipFiles(signPath); String aesDaraPath = temp[1]; String signDataPtah = temp[0]; LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath); LogUtils.i(TAG, "signDataPtah" + signDataPtah); if(!mapControl()) { return false; } //验证签名 if(!verifySignature(aesDaraPath, signDataPtah)) { LogUtils.e(TAG, "verifySignature fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //对称解密Zip if(!decryptFile(aesDaraPath, verifyDataPath)) { LogUtils.e(TAG, "decryptFile fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } LogUtils.i(TAG, "dataVerification success"); return true; } /** * 删除指定目录及其所有内容 * * @param directory 指定的目录路径 * @return 如果删除成功返回 true,否则返回 false */ public boolean deleteDirectory(String directory) { File dir = new File(directory); if (!dir.exists()) { LogUtils.e("目录不存在: " + directory); return false; // 目录不存在,视为删除失败 } if (!dir.isDirectory()) { LogUtils.e("指定路径不是一个目录: " + directory); return false; // 路径不是目录,无法删除 } return deleteRecursive(dir); // 递归删除目录 } /** * 递归删除文件或目录 * * @param file 文件或目录 * @return 如果删除成功返回 true,否则返回 false */ private boolean deleteRecursive(File file) { if (file.isDirectory()) { // 如果是目录,递归删除其所有子文件和子目录 File[] files = file.listFiles(); if (files != null) { for (File subFile : files) { if (!deleteRecursive(subFile)) { return false; // 如果某个子文件或子目录删除失败,则整体失败 } } } } // 删除当前文件或空目录 if (!file.delete()) { LogUtils.e("无法删除文件或目录: " + file.getAbsolutePath()); return false; // 删除失败 } LogUtils.i("已成功删除: " + file.getAbsolutePath()); return true; // 删除成功 } public boolean mapControl() { LogUtils.d(TAG, "mapControl JAVA start"); return mHmiProxyJNI.mapControl(); } public boolean zipDbFiles(String sourceDir, String outputZipPath) { return ZipUtils.getInstance().zipDbFiles(sourceDir, outputZipPath); } public boolean zip(String sourcePath, String outputZipPath) { return ZipUtils.getInstance().zip(sourcePath, outputZipPath, true); } // Used to load the 'native-lib' library on application startup. static { LogUtils.d(TAG, "loadLibrary IsaEngineJni " + System.currentTimeMillis()); System.loadLibrary("IsaEngineJni"); } } 带有:/zwj-s注释的与回调强相关,请你分析这里实现了什么

#### 一、命名空间与程序结构相关 #### 1. namespace - **定义**:用于声明命名空间,是组织代码的容器。 - **作用**:解决类、方法等成员的命名冲突,将相关代码归类(如按模块、功能划分)。 - **语法**:namespace 命名空间名 { /* 代码 */ } - 示例 csharp namespace MyApp.DataAccess { // 数据访问层命名空间 public class UserRepository { /* 数据库操作代码 */ } } - **注意**:命名空间可嵌套(如namespace A.B.C),访问时需用完整路径(或using导入)。 #### 2. internal(访问修饰符) - **定义**:指定成员(类、方法、字段等)仅在**同一程序集(Assembly)** 内可见。 - **适用场景**:需在项目内部共享,但不对外暴露的代码(如内部工具类)。 - 示例 csharp internal class InternalHelper { // 仅当前程序集可访问 internal void DoWork() { /* 内部逻辑 */ } } #### 3. class - **定义**:用于声明类,是面向对象编程的基本单位,封装数据(字段 / 属性)和行为(方法)。 - **特性**:类是对象的模板,可通过new创建实例;支持继承、多态等特性。 - 示例 csharp public class Person { // 定义"人"类 public string Name { get; set; } // 属性(数据) public void SayHello() { Console.WriteLine($"Hello, {Name}"); } // 方法(行为) } // 创建实例 Person p = new Person { Name = "张三" }; p.SayHello(); // 输出:Hello, 张三 #### 4. static - **定义**:用于修饰类、方法、字段等,标识其为 “静态成员”,属于类本身而非实例。 - 特性 - 静态成员不依赖对象实例,可直接通过类名.成员访问; - 静态类不能实例化,仅包含静态成员(如工具类)。 - 示例 csharp public static class MathTool { // 静态类 public static int Add(int a, int b) => a + b; // 静态方法 } // 调用:无需实例化 int sum = MathTool.Add(2, 3); // 结果:5 #### 5. void - **定义**:用于方法返回值声明,表示方法**无返回值**。 - 示例 csharp public void PrintMessage() { // 无返回值方法 Console.WriteLine("这是一个void方法"); } #### 6. const(常量) - **定义**:声明编译时常量,值在编译时确定,且**不可修改**。 - 特性 - 必须在声明时初始化; - 默认静态,可通过类名.常量名访问; - 仅支持值类型(如 int、string 字面量)。 - 示例 csharp public class Constants { public const double Pi = 3.14159; // 编译时确定 public const string AppName = "MyApp"; } #### 7. readonly(只读字段) - **定义**:声明运行时常量,值可在声明时或**构造函数中初始化**,初始化后不可修改。 - 特性 - 支持运行时动态赋值(如从配置文件读取); - 非静态readonly字段依赖实例,静态readonly字段属于类。 - 示例 csharp public class User { public readonly string Id; // 实例只读字段 public static readonly string Version; // 静态只读字段 static User() { // 静态构造函数初始化 Version = "1.0.0"; } public User(string id) { // 实例构造函数初始化 Id = id; } } ### **二、类型与转换相关** #### 1. 基本类型(sbyte/byte/int/long 等) - 定义:C# 内置的值类型,用于存储数值、字符等数据,按范围和精度划分: - 整数类型:sbyte(8 位有符号)、byte(8 位无符号)、short(16 位)、int(32 位)、long(64 位)等; - 浮点类型:float(32 位单精度)、double(64 位双精度)、decimal(高精度 decimal,适合货币); - 字符类型:char(16 位 Unicode 字符)。 - 示例 csharp int age = 25; // 整数 double weight = 62.5; // 双精度浮点 decimal price = 99.99m; // 高精度(需加m后缀) char gender = '男'; // 字符 #### 2. string - **定义**:引用类型,用于存储字符串(字符序列),**不可变**(修改时会创建新对象)。 - 核心特性 - 支持+拼接(但频繁拼接效率低,推荐StringBuilder); - 可通过索引访问单个字符(如str[0]); - 常用方法见 “字符串处理” 分类。 #### 3. object - **定义**:所有类型的基类(包括值类型、引用类型),任何类型都可隐式转换为object。 - 特性: - 用于存储任意类型数据(如集合中混合存储不同类型); - 包含ToString()、Equals()、GetHashCode()等基础方法。 - 示例 csharp object obj1 = 123; // int隐式转换为object(装箱) object obj2 = "hello"; // string隐式转换为object #### 4. var - **定义**:隐式类型关键字,编译器根据赋值自动推断变量类型(仅用于局部变量)。 - 特性: - 简化代码(无需显式声明类型); - 类型在编译时确定,仍为强类型(非动态)。 - 示例 csharp var list = new List<int>(); // 推断为List<int> var name = "张三"; // 推断为string #### 5. dynamic - **定义**:动态类型,编译时不检查类型,运行时动态解析(类似弱类型)。 - **适用场景**:处理动态数据(如 JSON 反序列化、COM 交互)。 - **注意**:牺牲编译时类型安全,可能导致运行时错误。 - 示例 csharp dynamic data = "test"; data = 123; // 运行时允许变更类型 Console.WriteLine(data * 2); // 运行时解析为123*2=246 #### 6. 显式类型转换 (type) - **定义**:强制将一种类型转换为另一种类型(需手动指定目标类型)。 - **适用场景**:从大范围类型转为小范围类型(可能丢失数据),如double→int。 - 示例 csharp double x = 3.14; int y = (int)x; // 显式转换,结果为3(丢失小数部分) ### **三、运算符相关** #### 1. 算术运算符(+ - * / %) - 作用:执行数值计算: - +:加法(或字符串拼接); - -:减法; - *:乘法; - /:除法(整数除法会截断小数); - %:取模(求余数)。 - 示例 csharp int a = 10, b = 3; Console.WriteLine(a / b); // 3(整数除法) Console.WriteLine(a % b); // 1(10 ÷ 3 余数为1) #### 2. 自增 / 自减(++ --) - 作用:对变量值加 1 或减 1,分前缀( ++x )和后缀( x++ ): - 前缀:先修改值,再使用; - 后缀:先使用值,再修改。 - 示例 csharp int x = 5; Console.WriteLine(x++); // 输出5(先使用,后+1),x变为6 Console.WriteLine(++x); // 输出7(先+1,后使用),x变为7 #### 3. 赋值与复合赋值(= += -= *= /= %=) - 作用: - =:直接赋值; - 复合赋值:赋值时同时执行运算(简化代码)。 - 示例 csharp int a = 5; a += 3; // 等价于a = a + 3 → a=8 a *= 2; // 等价于a = a * 2 → a=16 #### 4. 关系运算符(== != > < >= <=) - **作用**:比较两个值的关系,返回bool(true/false)。 - 示例 csharp int x = 5, y = 10; bool isGreater = x > y; // false bool isEqual = x == y; // false #### 5. 逻辑运算符(&& || !) - 作用:组合或反转布尔表达式: - &&(逻辑与):两边都为true才返回true(短路求值:左边为false时右边不执行); - ||(逻辑或):至少一边为true返回true(短路求值:左边为true时右边不执行); - !(逻辑非):反转布尔值。 - 示例 csharp bool a = true, b = false; Console.WriteLine(a && b); // false Console.WriteLine(a || b); // true Console.WriteLine(!a); // false ### **四、流程控制相关** #### 1. if / else if / else - **作用**:根据条件执行不同代码块。 - 语法 csharp if (条件1) { /* 条件1为true时执行 */ } else if (条件2) { /* 条件1为false、条件2为true时执行 */ } else { /* 所有条件为false时执行 */ } - 示例 csharp int score = 85; if (score >= 90) Console.WriteLine("优秀"); else if (score >= 60) Console.WriteLine("及格"); else Console.WriteLine("不及格"); // 输出:及格 #### 2. for 循环 - **作用**:已知循环次数时使用,通过计数器控制循环。 - **语法**:for (初始化; 条件; 迭代) { /* 循环体 */ } - 示例 csharp for (int i = 0; i < 3; i++) { // 循环3次 Console.WriteLine(i); // 输出:0、1、2 } #### 3. while 循环 - **作用**:条件满足时重复执行循环体(先判断条件,再执行)。 - **语法**:while (条件) { /* 循环体 */ } - 示例 csharp int i = 0; while (i < 3) { // 条件为true时执行 Console.WriteLine(i); // 0、1、2 i++; } #### 4. do-while 循环 - **作用**:至少执行一次循环体,再判断条件(先执行,后判断)。 - **语法**:do { /* 循环体 */ } while (条件); - 示例 csharp int i = 3; do { Console.WriteLine(i); // 输出3(即使条件不满足,仍执行一次) i++; } while (i < 3); #### 5. foreach 循环 - **作用**:遍历集合或数组中的每个元素(无需手动控制索引)。 - **语法**:foreach (元素类型 变量名 in 集合) { /* 循环体 */ } - 示例 csharp string[] fruits = { "苹果", "香蕉", "橙子" }; foreach (string fruit in fruits) { Console.WriteLine(fruit); // 依次输出数组元素 } #### 6. break / continue - **break**:立即跳出当前循环(不再执行后续迭代); - **continue**:跳过当前迭代的剩余代码,直接进入下一次迭代。 - 示例 csharp for (int i = 0; i < 5; i++) { if (i == 2) break; // 跳出循环 Console.WriteLine(i); // 输出:0、1 } for (int i = 0; i < 5; i++) { if (i == 2) continue; // 跳过当前迭代 Console.WriteLine(i); // 输出:0、1、3、4 } ### **五、字符串处理相关** #### 1. string.Format - **定义**:将变量、数值等插入字符串的指定位置,生成格式化字符串。 - 参数: - 第一个参数:格式字符串(含占位符{0}, {1}...); - 后续参数:替换占位符的实际值。 - **返回值**:格式化后的新字符串。 - 示例 csharp string name = "张三"; int age = 20; string msg = string.Format("姓名:{0},年龄:{1}", name, age); // 结果:"姓名:张三,年龄:20" #### 2. string.Equals - **定义**:比较两个字符串的**内容**是否完全一致(区分大小写)。 - **参数**:两个待比较的字符串。 - **返回值**:bool(true表示内容相同)。 - 示例 csharp bool isSame = string.Equals("abc", "ABC"); // false(区分大小写) bool isSame2 = string.Equals("abc", "abc"); // true #### 3. string.IndexOf - **定义**:查找子字符串或字符在当前字符串中**首次出现的索引**(从 0 开始)。 - **参数**:待查找的子串或字符。 - **返回值**:索引(int),未找到返回-1。 - 示例 csharp string str = "hello world"; int index = str.IndexOf("world"); // 6("world"从索引6开始) int notFound = str.IndexOf("xyz"); // -1 #### 4. string.Contains - **定义**:判断当前字符串是否**包含指定子字符串**。 - **参数**:待查找的子串。 - **返回值**:bool(true表示包含)。 - 示例 csharp bool hasHello = "hello world".Contains("hello"); // true bool hasAbc = "hello world".Contains("abc"); // false #### 5. string.Replace - **定义**:将字符串中所有指定的 “旧子串” 替换为 “新子串”,返回新字符串(原字符串不变,因 string 不可变)。 - **参数**:旧子串、新子串。 - **返回值**:替换后的新字符串。 - 示例 csharp string str = "hello world"; string newStr = str.Replace("world", "C#"); // 结果:"hello C#" #### 6. string.ToUpper / ToLower - **定义**:将字符串转换为全大写(ToUpper)或全小写(ToLower)。 - **返回值**:转换后的新字符串。 - 示例 csharp string str = "Hello World"; string upper = str.ToUpper(); // "HELLO WORLD" string lower = str.ToLower(); // "hello world" #### 7. StringBuilder - **定义**:System.Text命名空间下的可变字符串类,用于高效拼接、修改字符串(避免string不可变导致的性能损耗)。 - 核心方法: - Append(str):追加内容; - Insert(index, str):在指定索引插入内容; - ToString():转换为string类型。 - 示例 csharp var sb = new StringBuilder(); sb.Append("a"); sb.Append("b"); sb.Insert(0, "prefix_"); // 在开头插入 string result = sb.ToString(); // "prefix_ab" ### **六、数组与集合相关** #### 1. 数组方法(Array.Sort / Reverse / IndexOf / Clear) - **Array.Sort**:对数组元素排序(默认升序)。 csharp int[] arr = {3, 1, 2}; Array.Sort(arr); // 结果:{1, 2, 3} - **Array.Reverse**:反转数组元素顺序。 csharp int[] arr = {1, 2, 3}; Array.Reverse(arr); // 结果:{3, 2, 1} - **Array.IndexOf**:查找元素在数组中首次出现的索引(未找到返回-1)。 csharp int[] arr = {1, 2, 3}; int index = Array.IndexOf(arr, 2); // 1 - **Array.Clear**:清空数组元素(设置为默认值,如0、null)。 csharp int[] arr = {1, 2, 3}; Array.Clear(arr, 0, arr.Length); // 结果:{0, 0, 0} #### 2. 数组条件查找(Array.Find / FindAll / Exists) - **Array.Find**:返回数组中**第一个满足条件**的元素。 csharp int[] nums = {1, 2, 3, 4}; int firstEven = Array.Find(nums, x => x % 2 == 0); // 2 - **Array.FindAll**:返回数组中**所有满足条件**的元素(数组)。 csharp int[] evens = Array.FindAll(nums, x => x % 2 == 0); // {2, 4} - **Array.Exists**:判断数组中**是否存在满足条件**的元素(返回bool)。 csharp bool hasOdd = Array.Exists(nums, x => x % 2 != 0); // true #### 3. List(泛型列表) - **定义**:动态大小的泛型集合,支持自动扩容,比数组更灵活。 - 核心方法: - Add(item):添加元素; - Remove(item):移除指定元素; - Insert(index, item):在指定位置插入元素; - Contains(item):判断是否包含元素; - Count:获取元素数量。 - 示例 csharp var list = new List<string>(); list.Add("苹果"); list.Add("香蕉"); list.Remove("苹果"); // 结果:{"香蕉"} #### 4. Dictionary(字典) - **定义**:键值对集合,通过键(TKey)快速查找值(TValue),键唯一。 - 核心方法: - Add(key, value):添加键值对; - Remove(key):移除指定键的键值对; - ContainsKey(key):判断键是否存在; - TryGetValue(key, out value):安全获取值(避免键不存在时抛异常)。 - 示例 csharp var dict = new Dictionary<int, string>(); dict.Add(1, "苹果"); dict.Add(2, "香蕉"); string fruit = dict[1]; // "苹果" bool hasKey = dict.ContainsKey(3); // false #### 5. ArrayList(非泛型动态数组) - **定义**:可存储任意类型元素的动态数组(非泛型,需手动类型转换)。 - **核心方法**:与List类似(Add、Remove、Insert等),但元素类型为object。 - **注意**:因非泛型,存在装箱 / 拆箱开销,推荐优先使用List。 - 示例 csharp var list = new ArrayList(); list.Add(123); // 装箱(int→object) list.Add("hello"); int num = (int)list[0]; // 拆箱(object→int) ### **七、面向对象相关** #### 1. 访问修饰符(public / private / protected / internal) - **public**:全局可见(任何位置可访问); - **private**:仅当前类内部可见; - **protected**:当前类及派生类可见; - **internal**:同一程序集可见。 - 示例 csharp public class Person { public string Name; // 全局可见 private int age; // 仅Person类内部可见 protected string Id; // Person及派生类可见 } #### 2. this 关键字 - 定义:指代当前类的实例对象,用于: - 区分同名的字段和参数; - 调用当前类的其他构造函数(this(参数)); - 作为参数传递当前实例。 - 示例 csharp public class Car { private string name; public Car(string name) { this.name = name; // 区分字段和参数 } public void Print() { Console.WriteLine(this.name); // 访问当前实例的字段 } } #### 3. base 关键字 - 定义:指代当前类的基类(父类)实例,用于: - 调用父类的构造函数(base(参数)); - 访问父类的成员(方法、属性等)。 - 示例 csharp public class Animal { public void Eat() { Console.WriteLine("动物进食"); } } public class Dog : Animal { public void Eat(string food) { base.Eat(); // 调用父类的Eat方法 Console.WriteLine($"狗吃{food}"); } } #### 4. virtual / override(虚方法与重写) - **virtual**:在父类中标记方法为 “可被重写”; - **override**:在子类中重写父类的虚方法,实现多态。 - 示例 csharp public class Animal { public virtual void MakeSound() { Console.WriteLine("动物叫"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("汪汪叫"); } // 重写 } // 多态调用 Animal animal = new Dog(); animal.MakeSound(); // 输出:汪汪叫(运行时调用子类重写的方法) #### 5. abstract(抽象类 / 方法) - **抽象类**:用abstract修饰,不能实例化,可包含抽象方法和具体方法; - **抽象方法**:用abstract修饰,无方法体,强制子类实现。 - 示例 csharp public abstract class Shape { // 抽象类 public abstract double GetArea(); // 抽象方法(无实现) } public class Circle : Shape { public double Radius; public override double GetArea() { // 必须实现抽象方法 return Math.PI * Radius * Radius; } } #### 6. sealed(密封) - 作用: - 修饰类:禁止被继承; - 修饰方法:禁止子类重写(需配合override使用)。 - 示例 csharp public sealed class SealedClass { } // 不能被继承 public class Base { public virtual void DoWork() { } } public class Derived : Base { public sealed override void DoWork() { } // 禁止子类重写 } ### **八、委托与事件相关** #### 1. delegate(委托) - **定义**:引用类型,封装具有相同签名的方法(类似 “函数指针”,但类型安全)。 - **作用**:将方法作为参数传递,实现回调、事件等。 - 示例 csharp // 声明委托类型(无返回值,接受string参数) delegate void PrintDelegate(string msg); // 定义匹配签名的方法 static void Print(string s) { Console.WriteLine(s); } // 委托绑定方法并调用 PrintDelegate print = Print; print("Hello 委托"); // 输出:Hello 委托 #### 2. 系统委托(Action / Func / Predicate) - **Action**:无返回值的委托(支持 0-16 个参数)。 csharp Action<string> log = (msg) => Console.WriteLine(msg); log("日志信息"); - **Func**:有返回值的委托(最后一个泛型参数为返回类型)。 csharp Func<int, int, int> add = (a, b) => a + b; int sum = add(2, 3); // 5 - **Predicate**:返回bool的委托(用于条件判断)。 csharp Predicate<int> isEven = x => x % 2 == 0; bool result = isEven(4); // true #### 3. 多播委托(+= / -=) - **定义**:一个委托实例可绑定多个方法(通过+=添加,-=移除),调用时按顺序执行所有方法。 - 示例 csharp Action action = () => Console.Write("A"); action += () => Console.Write("B"); // 添加第二个方法 action(); // 输出:AB action -= () => Console.Write("A"); // 移除第一个方法 action(); // 输出:B #### 4. event(事件) - **定义**:基于委托的封装,用于发布 - 订阅模式(如按钮点击事件)。 - **特性**:仅允许通过+=订阅、-=取消订阅,禁止外部直接调用。 - 示例 csharp public class Button { public event Action OnClick; // 声明事件 public void Click() { OnClick?.Invoke(); // 触发事件(安全调用) } } // 订阅事件 var btn = new Button(); btn.OnClick += () => Console.WriteLine("按钮被点击"); btn.Click(); // 输出:按钮被点击 ### **九、异步与并发相关** #### 1. async / await - **async**:修饰方法,标记其包含异步操作(返回Task/Task); - **await**:等待异步操作完成,不阻塞当前线程(仅在async方法中使用)。 - 示例 csharp public async Task<int> GetDataAsync() { // 模拟耗时操作(如网络请求) return await Task.Run(() => { Thread.Sleep(1000); // 模拟耗时 return 100; }); } // 调用 int result = await GetDataAsync(); // 等待结果,不阻塞主线程 #### 2. Task - **定义**:表示异步操作的单元,用于替代传统的Thread,更高效。 - 核心方法 : - Task.Run(action):在线程池执行异步操作; - Wait():阻塞等待任务完成; - Result:获取返回值(阻塞); - ContinueWith():任务完成后执行后续操作。 - 示例 csharp Task<int> task = Task.Run(() => 1 + 2); task.ContinueWith(t => Console.WriteLine(t.Result)); // 输出3 #### 3. lock - **定义**:确保同一时间只有一个线程执行锁定块内的代码,解决多线程共享资源竞争问题。 - 示例 csharp private int count = 0; private object lockObj = new object(); // 锁对象 void Increment() { lock (lockObj) { // 锁定,确保线程安全 count++; } } ### **十、文件操作相关** #### 1. File 类方法(Create / WriteAllText / ReadAllText) - **File.Create(path)**:创建文件,返回FileStream(需手动关闭或用using)。 - **File.WriteAllText(path, content)**:一次性写入文本(自动创建文件,覆盖原有内容)。 - **File.ReadAllText(path)**:一次性读取文件所有文本。 - 示例 csharp string path = "test.txt"; File.WriteAllText(path, "Hello File"); // 写入 string content = File.ReadAllText(path); // 读取:"Hello File" #### 2. StreamReader / StreamWriter - **StreamReader**:读取文本文件(支持逐行读取)。 csharp using (var sr = new StreamReader("test.txt")) { string line; while ((line = sr.ReadLine()) != null) { // 逐行读取 Console.WriteLine(line); } } - **StreamWriter**:写入文本文件(支持追加)。 csharp using (var sw = new StreamWriter("test.txt", true)) { // true:追加模式 sw.WriteLine("追加一行"); } #### 3. Path.Combine - **定义**:安全拼接路径(自动处理不同系统的路径分隔符,如\或/)。 - 示例 csharp string path = Path.Combine("C:", "Users", "test.txt"); // Windows:"C:\Users\test.txt";Linux:"C:/Users/test.txt"

package com.isa.navi.library.map; import android.inputmethodservice.Keyboard; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import androidx.annotation.NonNull; import com.isa.navi.library.constant.Const; import com.isa.navi.library.manager.BaseProxy; import com.isa.navi.library.map.DataCheckInfo; import com.isa.navi.library.map.UpdateEnvironment; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CopyOnWriteArrayList; /** * @author haoj * @description description * @date 2023-06-20 */ public class MapProxy extends BaseProxy<IMapProxyAIDL> implements IMapProxy { private static final String TAG = Const.ISA_NAVI + "MapProxy"; private List<IMapListener.IMapChangeListener> mIUpdateCallbackListeners = new CopyOnWriteArrayList<>(); private IMapDownloadCallbackAIDL mIMapDownloadCallBackAIDL; private volatile static MapProxy mInstance; public static MapProxy getInstance() { if (mInstance == null) { synchronized (MapProxy.class) { if (mInstance == null) { mInstance = new MapProxy(); } } } return mInstance; } @Override protected String getProxyName() { return Const.SVR_MAP_REPOSITORY; } @Override protected IMapProxyAIDL getProxyService(IBinder iBinder) { return IMapProxyAIDL.Stub.asInterface(iBinder); } @Override public void updateMap() { try { if (isAvailable()) { mService.updateMap(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void suspendUpdate(boolean suspend) { try { Log.d(TAG, "suspendUpdateMap suspend : " + suspend + ", isAvailable : " + isAvailable()); if (isAvailable()) { mService.suspendUpdate(suspend); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void cancelUpdateMap() { try { Log.d(TAG, "cancelUpdateMap : " + ", isAvailable : " + isAvailable()); if (isAvailable()) { mService.cancelUpdate(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void addMapChangeListener(IMapListener.IMapChangeListener listener) { Log.d(TAG, "addDataChangeListener callback:" + listener + ", isAvailable : " + isAvailable()); try { if (isAvailable()) { if ((listener != null) && !mIUpdateCallbackListeners.contains(listener)) { mIUpdateCallbackListeners.add(listener); } if (mIUpdateCallbackListeners.size() == 1) { mIMapDownloadCallBackAIDL = new IMapDownloadCallbackAIDL.Stub() { @Override public void onMapDownloadStatusChange(List<UpdateInfoBean> bean) throws RemoteException { Log.d(TAG, "onMapDownloadStatusChange:" + bean + " ,size:" + mIUpdateCallbackListeners.size()); try { Iterator<IMapListener.IMapChangeListener> iterator = mIUpdateCallbackListeners.iterator(); while (iterator.hasNext()) { IMapListener.IMapChangeListener l = iterator.next(); l.onMapChange(bean); } } catch (Exception e) { e.printStackTrace(); } } }; mService.registerMapDownloadListener(mIMapDownloadCallBackAIDL); } } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void removeMapChangeListener(IMapListener.IMapChangeListener listener) { Log.d(TAG, "removeDataChangeListener callback:" + listener + ", isAvailable : " + isAvailable()); try { if (isAvailable()) { if ((listener != null) && mIUpdateCallbackListeners.contains(listener)) { mIUpdateCallbackListeners.remove(listener); } if (mIUpdateCallbackListeners.size() == 0 && mIMapDownloadCallBackAIDL != null) { mService.unregisterMapDownloadListener(mIMapDownloadCallBackAIDL); } mIMapDownloadCallBackAIDL = null; } } catch (RemoteException e) { e.printStackTrace(); } } private static String calculateMD5(String filePath) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); FileInputStream fis = new FileInputStream(filePath); byte[] buffer = new byte[8192]; int read = 0; while ((read = fis.read(buffer)) > 0) { digest.update(buffer, 0, read); } byte[] md5sum = digest.digest(); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < md5sum.length; i++) { String hex = Integer.toHexString(0xff & md5sum[i]); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } catch (Exception e) { e.printStackTrace(); return ""; } } private Map<String, String> getDetectionInfo(String md5_file_path) throws JSONException { Map<String, String> dataMap = new HashMap<>(); boolean isUsbReady = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); if(!isUsbReady) { return dataMap; } JSONObject jsonObject = getJsonObject(md5_file_path); JSONArray jsonArray = jsonObject.getJSONArray("ISAData"); for(int i = 0; i< jsonArray.length(); i++) { JSONObject jsonObject1 = jsonArray.getJSONObject(i); String dataName = jsonObject1.getString("data_name"); String md5 = jsonObject1.getString("md5"); dataMap.put(dataName, md5); } return dataMap; } @NonNull private static JSONObject getJsonObject(String md5_file_path) throws JSONException { File file = new File(md5_file_path); StringBuilder content = new StringBuilder(); try{ BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } reader.close(); } catch (IOException e) { throw new RuntimeException(e); } JSONObject jsonObject = new JSONObject(content.toString()); return jsonObject; } public DataCheckInfo checkISAData(String md5_file_path) { DataCheckInfo info = new DataCheckInfo(); md5_file_path += "/ISADataCheckInfo.txt"; Log.d(TAG, "checkISAData md5_file_path = " + md5_file_path); //获取文件信息,用于校验数据 Map<String, String> md5_file_map = null; try { md5_file_map = getDetectionInfo(md5_file_path); } catch (JSONException e) { e.printStackTrace(); } //如果文件中的信息不足32个则返回报错信息 if(md5_file_map.isEmpty() || md5_file_map.size() != 32) { info.setIs_result(false); info.setFailure(2); info.setReasom_chinese("ISADataCheckInfo.txt文件信息缺失"); info.setReasom_engine("Insufficient information in ISADataCheckInfo.txt"); Log.d(TAG, "checkISAData md5_file_map 信息不足"); return info; } // public String get_Global_m_data_address() { // return mHmiProxyJNI.get_Global_m_data_address(); // } //在数据分区下,检索数据 String data_path = "/map/"; File directory = new File(data_path); Map<String, String> data_MD5_map = new HashMap<>(); if(directory.exists() && directory.isDirectory()) { File[] files = directory.listFiles(); if (files != null) { for(File file :files) { if(file.isFile()) { String file_name = file.getName(); if(file_name.indexOf("CarDBBIN") != -1 || file_name.indexOf("Country_CarDB") != -1) { String MD5 = ""; MD5 = calculateMD5(data_path+file_name); if(MD5 == "") { info.setIs_result(false); info.setFailure(4); info.setReasom_chinese("无法计算分区内数据MD5值"); info.setReasom_engine("Unable to calculate MD5 of data within the partition"); return info; } Log.d(TAG, "checkISAData file_name:" + file_name); Log.d(TAG, "checkISAData MD5:" + MD5); data_MD5_map.put(file_name, MD5); } } } } else { info.setIs_result(false); info.setFailure(3); info.setReasom_chinese("无法访问数据分区"); info.setReasom_engine("Unable to read data partition"); Log.d(TAG, "checkISAData 无法读取数据分区目录"); return info; } } else { info.setIs_result(false); info.setFailure(8); info.setReasom_chinese("数据分区不存在"); info.setReasom_engine("The data partition does not exist"); Log.d(TAG, "checkISAData 数据分区目录不存在"); return info; } Log.d(TAG, "data_MD5_map 组织完毕!"); //如果数据不足32个,则返回报错 if(data_MD5_map.size() != 32) { info.setIs_result(false); info.setFailure(5); info.setReasom_chinese("数据分区目录下数据缺失"); info.setReasom_engine("Data is missing in the data partition directory"); Log.d(TAG, "checkISAData 数据分区目录目录下数据不全"); return info; } //遍历data_MD5_map集合,跟校验信息进行判断 for(Map.Entry<String, String> entry: data_MD5_map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); String md5_file_map_value = md5_file_map.get(key); if(md5_file_map_value != null) { Log.d(TAG, "value:" + value); Log.d(TAG, "md5_file_map_value:" + md5_file_map_value); if(!value.equals(md5_file_map_value)) { Log.d(TAG, "value size:" + value.length()); Log.d(TAG, "md5_file_map_value size:" + md5_file_map_value.length()); info.setIs_result(false); info.setFailure(6); info.setReasom_chinese(key + " 该国家数据校验不通过"); info.setReasom_engine(key + " verification failed"); Log.d(TAG, "checkISAData " + key + "该国家数据校验不通过"); } } else { info.setIs_result(false); info.setFailure(7); info.setReasom_chinese(key + " 无法获取分区内数据MD5值"); info.setReasom_engine(key + " MD5 value calculation failed"); Log.d(TAG, "checkISAData MD5值计算失败"); return info; } } info.setIs_result(true); return info; } public String getInfoAfterUpdates() { String result = ""; // try { // if (isAvailable()) { // result = mService.getInfoAfterUpdates(); // } // } catch (RemoteException e) { // e.printStackTrace(); // } return result; } public void startUSBDetection() { try { Log.d(TAG, "getVersion : " + ", isAvailable : " + isAvailable()); if (isAvailable()) { mService.startUSBDetection(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public String getMapInfo() { String result = ""; try { if (isAvailable()) { result = mService.getMapInfo(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdateVersion() { String result = ""; try { if (isAvailable()) { result = mService.getUpdateVersion(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public UpdateEnvironment checkUpdateEnvironment() { UpdateEnvironment result = null; try { if (isAvailable()) { result = mService.checkUpdateEnvironment(); Log.i(TAG, "result.getResult()" + result.getResult()); Log.i(TAG, "result.getReason()" + result.getReason()); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdatePurpose() { String result = ""; try { if (isAvailable()) { result = mService.getUpdatePurpose(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdateContent() { String result = ""; try { if (isAvailable()) { result = mService.getUpdateContent(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUserManual() { String result = ""; try { if (isAvailable()) { result = mService.getUserManual(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public String getUpdatedContentDescription() { String result = ""; try { if (isAvailable()) { result = mService.getUpdatedContentDescription(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public int getEstimatedTime() { int result = 0; try { if (isAvailable()) { result = mService.getEstimatedTime(); } } catch (RemoteException e) { e.printStackTrace(); } return result; } @Override public boolean validatePassword(String inputPassword) { try { if (isAvailable()) { return mService.validatePassword(inputPassword); } } catch (Exception e) { e.printStackTrace(); } return false; } } 我要在这个类导入package com.isa.navi.jni.hmi; import android.content.Context; import android.os.Message; import android.os.StatFs; import android.util.Log; import androidx.annotation.NonNull; import com.isa.navi.jni.hmi.bean.UpdateList; import com.isa.navi.jni.hmi.update.USBListener; import com.isa.navi.library.map.UpdateEnvironment; import com.isa.navi.library.map.UpdateInfoBean; import com.isa.navi.manager.CallBackManager; import com.isa.navi.manager.CarManager; import com.isa.navi.manager.base.BaseHandlerManager; import com.isa.navi.receiver.USBReceiver; import com.isa.navi.utils.GsonUtils; import com.isa.navi.utils.LogUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class HmiJNIImpl extends BaseHandlerManager implements IHmiJNI { private static final String TAG = "UPDATE"; private static final int MSG_DOWNLOAD_STATUS_CHANGE = 1; private final HmiProxyJNI mHmiProxyJNI; private final Format mFormat; private static final String PASSWORD_PREFIX = "DfCs"; private static final String PASSWORD_SUFFIX = "#@"; private static final int LAST_CHARS_LENGTH = 4; private static String updateUSBpath = ""; /** * 车辆管理实例,用于管理车辆相关的功能。 */ private CarManager mCarManager; private volatile static HmiJNIImpl mInstance; private boolean is_run = false; private final Set<String> USBPathSet = Collections.synchronizedSet(new HashSet<>()); public static HmiJNIImpl getInstance() { if (mInstance == null) { synchronized (HmiJNIImpl.class) { if (mInstance == null) { mInstance = new HmiJNIImpl(); } } } return mInstance; } private HmiJNIImpl() { super(true); mHmiProxyJNI = new HmiProxyJNI(); mFormat = Format.getInstance(); mCarManager = CarManager.getInstance(); } @Override public void initialize() { super.initialize(); LogUtils.d(TAG, "initialize : "); DfCert.getInstance().getService(); CallBackManager.getInstance().registerUSBEventChangeListener(new USBListener() { @Override public void path(String mountPath) { USBPathSet.add(mountPath); LogUtils.i(TAG,"USBPathSet:" + mountPath); } }); // 初始化C++模块 mHmiProxyJNI.InitializeCpp(); InstanceEngine(); } private int[] getCurrentYearAndQuarter() { int[] result = new int[2]; LocalDate currentDate = LocalDate.now(); // 获取当前年份 int currentYear = currentDate.getYear(); // 获取当前月份 Month currentMonth = currentDate.getMonth(); // 根据月份确定当前季度 int currentQuarter; switch (currentMonth) { case JANUARY: case FEBRUARY: case MARCH: currentQuarter = 1; break; case APRIL: case MAY: case JUNE: currentQuarter = 2; break; case JULY: case AUGUST: case SEPTEMBER: currentQuarter = 3; break; case OCTOBER: case NOVEMBER: case DECEMBER: currentQuarter = 4; break; default: currentQuarter = 0; } result[0] = currentYear; result[1] = currentQuarter; return result; } private int[] parseMapInfo(String mapInfo) { int[] result = new int[2]; // 检查输入字符串的长度是否为6(4位年份 + 2位季度) if (mapInfo == null || mapInfo.length() != 6) { LogUtils.e(TAG, "Input string must be in the format YYYYMM where YYYY is the year and MM is the quarter as a two-digit number."); return result; } // 提取年份(前4位) String yearStr = mapInfo.substring(0, 4); result[0] = Integer.parseInt(yearStr); // 提取季度(后2位),并验证它在1到4的范围内 String quarterStr = mapInfo.substring(4, 6); int quarter = Integer.parseInt(quarterStr); if (quarter < 1 || quarter > 4) { LogUtils.e(TAG, "Quarter must be between 1 and 4."); return result; } result[1] = quarter; return result; } public void dataVersionCheck() { Thread dataVersionCheckThread = new Thread(new Runnable() { @Override public void run() { try { UpdateInfoBean bean = new UpdateInfoBean(); String localVersion = mHmiProxyJNI.getMapInfo(); if(localVersion.isEmpty()) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); return; } int[] currentYearAndQuarter = getCurrentYearAndQuarter(); int[] localYearAndQuarter = parseMapInfo(localVersion); int yearDifference = currentYearAndQuarter[0] - localYearAndQuarter[0]; int QuarterDifference = currentYearAndQuarter[1] - localYearAndQuarter[1]; if(yearDifference * 4 + QuarterDifference > 3) { bean.setUpdateStatus(4); handleDownloadStatusChange(bean); LogUtils.i(TAG, "Detected no updates for a long time"); } } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 dataVersionCheckThread.setName("ISA_NAVI_dataVersionCheck"); // 启动线程 dataVersionCheckThread.start(); } public void setContext(Context context) { if(mContext == null) { mContext = context; } language.getInstance().setContext(mContext); } @Override protected void handleMessage(Message msg) { switch (msg.what) { case MSG_DOWNLOAD_STATUS_CHANGE: handleDownloadStatusChange((String) msg.obj); break; default: break; } } private List<String> JsonArrayToListExample(JSONArray jsonArray) { List<String> stringList = new ArrayList<>(); try { // 遍历JSONArray for (int i = 0; i < jsonArray.length(); i++) { // 由于我们假设JSONArray只包含字符串,所以我们可以直接getString String item = jsonArray.getString(i); // 将字符串添加到列表中 stringList.add(item); } } catch (JSONException e) { e.printStackTrace(); } return stringList; } private UpdateInfoBean jsonToUpdateInfoBean(String json) { UpdateInfoBean result = null; try { // 解析整个JSON字符串为一个JSONObject JSONObject rootObject = new JSONObject(json); // 解析mUpdateMessage对象 JSONObject mUpdateMessage = rootObject.getJSONObject("mUpdateMessage"); String country = mUpdateMessage.getString("country"); double downloadSize = mUpdateMessage.getDouble("downloadSize"); boolean isCurrentCountry = mUpdateMessage.getBoolean("isCurrentCountry"); double progress = mUpdateMessage.getDouble("progress"); double totalSize = mUpdateMessage.getDouble("totalSize"); int updateStatus = mUpdateMessage.getInt("updateStatus"); String version = mUpdateMessage.getString("version"); // 解析mUpdateNotification对象 JSONObject mUpdateNotification = rootObject.getJSONObject("mUpdateNotification"); JSONArray updateException = mUpdateNotification.getJSONArray("UpdateException"); JSONArray updateFailedList = mUpdateNotification.getJSONArray("UpdateFailedList"); boolean updateResult = mUpdateNotification.getBoolean("UpdateResult"); JSONArray updateSuccessfulList = mUpdateNotification.getJSONArray("UpdateSuccessfulList"); // 解析mapStatus int mapStatus = rootObject.getInt("mapStatus"); UpdateInfoBean.UpdateNotification temp1 = new UpdateInfoBean.UpdateNotification(updateResult,JsonArrayToListExample(updateException),JsonArrayToListExample(updateSuccessfulList),JsonArrayToListExample(updateFailedList)); UpdateInfoBean.UpdateMessage temp2 = new UpdateInfoBean.UpdateMessage(country, version, totalSize, (int) progress, isCurrentCountry); temp2.setUpdateStatus(updateStatus); temp2.setDownloadSize(downloadSize); result = new UpdateInfoBean(mapStatus, temp1, temp2); } catch (Exception e) { LogUtils.e(TAG, "JSON parsing failed:" + e.toString()); } return result; } private void handleDownloadStatusChange(String json) { UpdateInfoBean bean = jsonToUpdateInfoBean(json); LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } } private void handleDownloadStatusChange(UpdateInfoBean bean) { LogUtils.i(TAG, "handleDownloadStatusChange : " + bean); if (bean != null) { // 仅当status=8时检查is_run,其他状态直接放行 if(bean.getUpdateStatus() != 8 || is_run) { List<UpdateInfoBean> list = new ArrayList<>(); list.add(bean); CallBackManager.getInstance().notifyUpdateStatus(list); } } else { LogUtils.e(TAG, "UpdateInfoBean is null!"); } } public void sendUpdateMessage(String json) { LogUtils.d(TAG, "sendUpdateMessage : " + json); Message message = Message.obtain(); message.what = MSG_DOWNLOAD_STATUS_CHANGE; message.obj = json; mHandler.sendMessage(message); } @Override public void updateMap() { LogUtils.d(TAG, "activeUpdateStart : "); // 检查更新过程是否已经在运行,防止重复创建线程 if(is_run) { LogUtils.i(TAG, "更新程序已经在进行"); return; }else { is_run = true; } // 创建一个新线程来执行主动更新 Thread updateMapThread = new Thread(new Runnable() { @Override public void run() { try { // 调用JNI方法开始主动更新 mHmiProxyJNI.updateMap(); LogUtils.d(TAG, "activeUpdateStart end: "); } catch (Exception e) { e.printStackTrace(); } } }); // 设置线程名称 updateMapThread.setName("ISA_NAVI_UpdateMapThread"); // 启动线程 updateMapThread.start(); } public void updateMapend() { is_run = false; } @Override public void suspendUpdate(boolean suspend) { mHmiProxyJNI.suspendUpdate(suspend); } @Override public void cancelUpdate() { mHmiProxyJNI.cancelUpdate(); } /** * 启动USB检测线程,用于检测插入的U盘并判断是否有可用的升级文件。 */ public void startUSBDetection() { LogUtils.i(TAG, "startUSBDetection START"); // setUSBPath("/data/KVM_EU_202404_T1.2.zip"); Thread USBDetectionThread = new Thread(new Runnable(){ @Override public void run() { UpdateInfoBean bean = new UpdateInfoBean(); boolean isHaveUSBData = false; //遍历检测到的U盘路径 if(USBPathSet.isEmpty()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } int set_num = 0; for(String usbPath : USBPathSet ) { //判断U盘路径是否真实存在 set_num ++; if(USBReceiver.isMounted(usbPath)) { File usbFile = new File(usbPath); File[] listFiles = usbFile.listFiles(); if(listFiles == null) { continue; } File newfile = null; for(File file : listFiles) { //判断U盘内是否含有升级文件 if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) { isHaveUSBData = true; if(newfile == null) { newfile = file; } else { if(Format.getInstance().isNewerVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) { newfile = file; } } } } //未检测到U盘指定路径下是否有数据,触发提示弹窗 if(!isHaveUSBData || newfile == null) { LogUtils.e(TAG, "The USB drive does not contain an upgrade package!"); bean.setUpdateStatus(3); handleDownloadStatusChange(bean); return; }else{ // 从文件名中提取版本号 String localDataPath = Format.getInstance().extractVersion(getCountryDbPath()); LogUtils.i(TAG, "file name:" + newfile.getName()); LogUtils.i(TAG, "localDataPath:" + localDataPath); if(localDataPath.isEmpty()) { String mapinfo = getMapInfo(); if(!mapinfo.isEmpty()) { String[] newVersionParts = new String[2]; Format.getInstance().extractVersion(newfile.getName().replaceAll("\\.\\w+$", ""), newVersionParts); String newUpdateVersion = newVersionParts[0]; String newReleasesVersion = newVersionParts[1]; LogUtils.i(TAG, "localDataPath.isEmpty() newUpdateVersion: " + newUpdateVersion); LogUtils.i(TAG, "localDataPath.isEmpty() newReleasesVersion: " + newReleasesVersion); if(Integer.parseInt(newUpdateVersion) > Integer.parseInt(mapinfo) || (Integer.parseInt(newUpdateVersion) == Integer.parseInt(mapinfo) && Double.parseDouble(newReleasesVersion) > 1)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } } else { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } return; } // 判断U盘内数据版本是否大于车机数据版本 if(Format.getInstance().isNewerVersion(newfile.getName().replaceAll("\\.\\w+$", ""), localDataPath)) { LogUtils.i(TAG,"Trigger USB drive update!"); bean.setUpdateStatus(0); handleDownloadStatusChange(bean); //设置U盘更新路径 updateUSBpath = usbPath; setUSBPath(newfile.getAbsolutePath()); LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath()); } else { LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!"); bean.setUpdateStatus(1); handleDownloadStatusChange(bean); } return; } } else { if(set_num == USBPathSet.size()) { //未检测到U盘,触发提示弹窗 LogUtils.e(TAG, "No USB drive detected!"); bean.setUpdateStatus(2); handleDownloadStatusChange(bean); return; } } } } }); USBDetectionThread.setName("ISA_NAVI_USBDetectionThread"); USBDetectionThread.start(); } public String getMapInfo() { String result = mHmiProxyJNI.getMapInfo(); LogUtils.i(TAG,"MapInfo:" + result); return result; } public String getUpdateVersion() { String result = mHmiProxyJNI.getUpdateVersion(); LogUtils.i(TAG,"UpdateVersion:" + result); return result; } public UpdateEnvironment checkUpdateEnvironment() { String json = mHmiProxyJNI.checkUpdateEnvironment(); LogUtils.d(TAG,"checkUpdateEnvironment JSON:" + json); UpdateEnvironment result = GsonUtils.fromJson(json, UpdateEnvironment.class); if(result == null) { LogUtils.e(TAG, "GsonUtils.fromJson(json, UpdateEnvironment.class) Fail!"); return null; } if (!result.getResult()) { List<String> temp_list = new ArrayList<>(); for (int i : result.getInt_reason()) { String str = language.getInstance().getEnvironment(i); if(str != null && !str.isEmpty()) { temp_list.add(str); } } // 更新temp对象中的reason字段 result.setReason(temp_list); } LogUtils.i(TAG,"checkUpdateEnvironment :" + result); return result; } public String getUpdatePurpose() { return language.getInstance().getUpdatePurpose(); } public String getUpdateContent() { return language.getInstance().getUpdateContent(); } public String getUserManual() { return language.getInstance().getUserManual(); } public String getUpdatedContentDescription() { return language.getInstance().getUpdatedContentDescription(); } public int getEstimatedTime() { return 30; } private void InstanceEngine() { Thread InstanceEngine = new Thread(new Runnable(){ @Override public void run() { LogUtils.d("ISA_NAVI", "InstanceEngine start!"); mHmiProxyJNI.instanceEngine(); } }); InstanceEngine.setName("ISA_NAVI_InstanceEngine"); InstanceEngine.start(); } // 获取后四位字符 @NonNull private static String getLastFourChars(String mapInfo) { if (mapInfo == null || mapInfo.isEmpty()) { LogUtils.e(TAG, "mapInfo cannot be null or empty"); return "0000"; } return mapInfo.length() >= LAST_CHARS_LENGTH ? mapInfo.substring(mapInfo.length() - LAST_CHARS_LENGTH) : "0000"; } // 生成密码 @NonNull private String generatePassword() { String mapInfo = getMapInfo(); String lastFourChars = getLastFourChars(mapInfo); return PASSWORD_PREFIX + lastFourChars + PASSWORD_SUFFIX; } // 校验密码 public boolean validatePassword(String inputPassword) { if (inputPassword == null) { return false; } String generatedPassword = generatePassword(); LogUtils.i(TAG, "generatedPassword:" + generatedPassword); LogUtils.i(TAG, "inputPassword:" + inputPassword); return inputPassword.equals(generatedPassword); } public void setUSBPath(String USBPath) { mHmiProxyJNI.setUSBPath(USBPath); } public boolean unzipFile(String zipFilePath, String destinationDirPath) { return ZipUtils.getInstance().unzip(zipFilePath, destinationDirPath); } public long getFileSize(String filePath) { File file = new File(filePath); if(!file.isFile() || !file.exists()) { return 0; } LogUtils.d(TAG, "getFileSize:" + file.length()); return file.length(); } public long getAvailableSpace(String filePath) { StatFs stat = new StatFs(filePath); long availableBlocks = stat.getAvailableBlocksLong(); long blockSize = stat.getBlockSizeLong(); LogUtils.i(TAG, "[AvailableSpace]:" + availableBlocks * blockSize + "字节"); return availableBlocks * blockSize; } public String getFail(int i) { return language.getInstance().getFail(i); } public boolean decryptFile(String encryptedFilePath, String decryptedFilePath) { return new DecryptUtil().decryptFile(encryptedFilePath, decryptedFilePath, DfCert.getInstance().getKEY_AES_256()); } public boolean verifySignature(String filePath, String signFilePath) { try { return DecryptUtil.verifySignature(filePath, signFilePath, DfCert.getInstance().getPUBLIC_KEY()); } catch (Exception e) { return false; } } /** * 获取高压电池电量百分比。 * * 此方法通过调用mCarManager.getHVPercent来获取高压电池的电量百分比。如果调用成功,则返回电量百分比值; * 如果调用失败或发生异常,则捕获异常并打印堆栈跟踪,最后返回0。 * * @return 一个浮点数,表示高压电池的电量百分比(0-100);如果获取失败或发生异常,则返回0。 */ public float getHVPercent() { float HVPercent = 0; try { HVPercent = mCarManager.getHVPercent(); if(HVPercent > 100 || HVPercent < 0) { HVPercent = 0; } } catch (Exception e) { e.printStackTrace(); } return HVPercent; } boolean isMounted() { return USBReceiver.isMounted(updateUSBpath); } public String getCountryDbPath() { return mHmiProxyJNI.getCountryDbPath(); } public String get_Global_m_data_address() { return mHmiProxyJNI.get_Global_m_data_address(); } public boolean dataVerification(String verifyDataPath) { LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath); String signPath = get_Global_m_data_address() + "sign/"; if(!mapControl()) { return false; } //解压加密数据 LogUtils.i(TAG, "verifyDataPath[" + verifyDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(verifyDataPath, signPath)) { //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //提取签名文件以及加密文件的绝对路径 String[] temp = ZipUtils.getInstance().findEncryptedZipFiles(signPath); String aesDaraPath = temp[1]; String signDataPtah = temp[0]; LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath); LogUtils.i(TAG, "signDataPtah" + signDataPtah); if(!mapControl()) { return false; } //验证签名 if(!verifySignature(aesDaraPath, signDataPtah)) { LogUtils.e(TAG, "verifySignature fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } if(!mapControl()) { return false; } //对称解密Zip if(!decryptFile(aesDaraPath, verifyDataPath)) { LogUtils.e(TAG, "decryptFile fail!"); //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } return false; } //删除解密验签中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除解密验签中间文件!"); } LogUtils.i(TAG, "dataVerification success"); return true; } /** * 删除指定目录及其所有内容 * * @param directory 指定的目录路径 * @return 如果删除成功返回 true,否则返回 false */ public boolean deleteDirectory(String directory) { File dir = new File(directory); if (!dir.exists()) { LogUtils.e("目录不存在: " + directory); return false; // 目录不存在,视为删除失败 } if (!dir.isDirectory()) { LogUtils.e("指定路径不是一个目录: " + directory); return false; // 路径不是目录,无法删除 } return deleteRecursive(dir); // 递归删除目录 } /** * 递归删除文件或目录 * * @param file 文件或目录 * @return 如果删除成功返回 true,否则返回 false */ private boolean deleteRecursive(File file) { if (file.isDirectory()) { // 如果是目录,递归删除其所有子文件和子目录 File[] files = file.listFiles(); if (files != null) { for (File subFile : files) { if (!deleteRecursive(subFile)) { return false; // 如果某个子文件或子目录删除失败,则整体失败 } } } } // 删除当前文件或空目录 if (!file.delete()) { LogUtils.e("无法删除文件或目录: " + file.getAbsolutePath()); return false; // 删除失败 } LogUtils.i("已成功删除: " + file.getAbsolutePath()); return true; // 删除成功 } public boolean mapControl() { LogUtils.d(TAG, "mapControl JAVA start"); return mHmiProxyJNI.mapControl(); } public boolean zipDbFiles(String sourceDir, String outputZipPath) { return ZipUtils.getInstance().zipDbFiles(sourceDir, outputZipPath); } public boolean zip(String sourcePath, String outputZipPath) { return ZipUtils.getInstance().zip(sourcePath, outputZipPath, true); } // Used to load the 'native-lib' library on application startup. static { LogUtils.d(TAG, "loadLibrary IsaEngineJni " + System.currentTimeMillis()); System.loadLibrary("IsaEngineJni"); } }

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.huawei.it.jalor5.upload.support.impl; import com.huawei.his.jalor.helper.StoreHelper; import com.huawei.it.env.support.EnvHolder; import com.huawei.it.jalor5.core.annotation.JalorOperation; import com.huawei.it.jalor5.core.annotation.SecurityPolicy; import com.huawei.it.jalor5.core.config.ApplicationConfigProperties; import com.huawei.it.jalor5.core.exception.ApplicationException; import com.huawei.it.jalor5.core.exception.ExceptionHandler; import com.huawei.it.jalor5.core.io.CheckResult; import com.huawei.it.jalor5.core.io.FileInfoVO; import com.huawei.it.jalor5.core.io.IFileContentHandler; import com.huawei.it.jalor5.core.ioc.Jalor; import com.huawei.it.jalor5.core.log.ILogger; import com.huawei.it.jalor5.core.log.JalorLoggerFactory; import com.huawei.it.jalor5.core.util.CollectionUtil; import com.huawei.it.jalor5.core.util.FileUtil; import com.huawei.it.jalor5.core.util.JsonUtil; import com.huawei.it.jalor5.core.util.PathUtil; import com.huawei.it.jalor5.core.util.StringUtil; import com.huawei.it.jalor5.core.util.XssStringUtil; import com.huawei.it.jalor5.htmlarea.HtmlAreaApplicationException; import com.huawei.it.jalor5.logs.LogVO; import com.huawei.it.jalor5.logs.service.ILogService; import com.huawei.it.jalor5.registry.RegistryVO; import com.huawei.it.jalor5.registry.service.IRegistryQueryService; import com.huawei.it.jalor5.upload.support.FolderSplitType; import com.huawei.it.jalor5.upload.support.IUploadFileConsumer; import com.huawei.it.jalor5.upload.support.IUploadSupportService; import com.huawei.it.jalor5.upload.support.UploadException; import com.huawei.it.jalor5.upload.support.UploadSettingVO; import com.huawei.it.jalor5.web.support.internal.impl.RequestUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileCountLimitExceededException; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.RequestContext; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.servlet.ServletRequestContext; import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component("/servlet/upload") public class UploadServlet extends HttpServlet { private static final String UPLOAD_ERROR = "huawei.jalor5.upload.support.00010001"; private static final Set<String> FILE_TYPE = new HashSet(Arrays.asList("der", "cer", "txt", "sql", "pfx", "jks", "crl", "pem")); private static final int FORM_BUFFER_SIZE = 2000; private static final long serialVersionUID = -460601782542501990L; private static int currentUploading = 0; private static int defaultMaxUploading = 10000; private static final ILogger LOGGER = JalorLoggerFactory.getLogger(UploadServlet.class); @Autowired( required = false ) private transient IUploadSupportService uploadSupportService; @Autowired( required = false ) private transient ILogService logService; @Autowired( required = false ) private transient IRegistryQueryService registryService; public UploadServlet() { } @JalorOperation( policy = SecurityPolicy.AllSystemUser ) public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { HttpServletRequest httpRequest = RequestUtil.getHttpServletRequest(request); LOGGER.debug("Upload Query String:{0}", new Object[]{httpRequest.getQueryString()}); String uploadType = request.getParameter("ulType"); Map<String, String> queryParams = RequestUtil.getRequestQueryParams(httpRequest); try { this.checkUploadingTimes(); Map<String, String> convertedMap = CollectionUtil.convert(request.getParameterMap()); convertedMap.put("taskType", queryParams.get("taskType")); Object obj = this.processUpload(httpRequest, uploadType, convertedMap); String json = JsonUtil.getJsonString(obj); LOGGER.debug("Object return:{0}", new Object[]{json}); String pattern = null; String uploadCleanXSS = "false"; if (null != this.registryService) { RegistryVO registryVO = this.registryService.findRegistryByPathNoAssertInternal("App.Security.XssFilterMatcher", false); if (null != registryVO) { pattern = registryVO.getValue(); } uploadCleanXSS = this.registryService.findValueByPath("App.Security.UploadCleanXSS", false, "false"); } response.setContentType("text/html"); if ("true".equalsIgnoreCase(uploadCleanXSS)) { response.getWriter().write(XssStringUtil.cleanXSS(json, pattern)); } else { response.getWriter().write(json); } } catch (UploadException ex) { LOGGER.error(ex); throw new ServletException(ex); } catch (ApplicationException ex) { LOGGER.error(ex); throw new ServletException(new UploadException("huawei.jalor5.upload.support.00010001", ex)); } catch (RuntimeException | FileUploadException ex) { LOGGER.error(ex); throw new ServletException(new UploadException("huawei.jalor5.upload.support.00010001", ex, ExceptionHandler.getFriendlyFault(ex).getMessage())); } finally { this.completeUploadingTimes(); } } private void completeUploadingTimes() { synchronized(UploadServlet.class) { if (currentUploading > 0) { --currentUploading; } } } private void checkUploadingTimes() { if (null != this.registryService) { int maxTimes = Integer.parseInt(this.registryService.findValueByPath("App.Security.MaxUploadingTimes", true, defaultMaxUploading)); synchronized(UploadServlet.class) { ++currentUploading; if (currentUploading > maxTimes) { throw new IllegalArgumentException("当前系统正在处理的上传次数超过最大上限,若需要加大上限,请修改数据字典App.Security.MaxUploadingTimes值"); } } } } private Object processUpload(HttpServletRequest request, String uploadType, Map<String, String> parameters) throws ApplicationException, FileUploadException { IUploadFileConsumer uls = this.uploadSupportService.findUploadConsumer(uploadType); if (!uls.validatePrivilege(parameters)) { throw new UploadException("huawei.jalor5.security.00010001"); } else { DiskFileItemFactory factory = new DiskFileItemFactory(); UploadSettingVO uploadSetting = uls.getUploadSetting(parameters); factory.setSizeThreshold(uploadSetting.getMaxMemory()); File file = new File(uploadSetting.getTempRepository()); PathUtil.makeDirs(file); factory.setRepository(file); ServletFileUpload upload = new ServletFileUpload(factory); upload.setSizeMax(uploadSetting.getMaxFileSize() + 2000L); if ("HtmlAreaImage".equals(uploadType)) { String value = this.registryService.findValueByPath("App.Security.HtmlArea", false, "1"); if ("1".equals(value)) { throw new HtmlAreaApplicationException("huawei.jalor5.Htmlarea.00020007"); } } RequestContext requestContext = new ServletRequestContext(request); long rLength = (long)requestContext.getContentLength(); long setLength = upload.getSizeMax(); LOGGER.debug("Upload image ok?:realLength={0},set={1}", new Object[]{rLength, setLength}); if (rLength > setLength) { throw new UploadException("huawei.jalor5.upload.support.00010004", new Object[]{rLength, setLength}); } else { List<FileItem> items = upload.parseRequest(request); this.validateFileCount(items, uploadSetting.getMaxFileCount()); return this.processFileItems(parameters, uploadSetting, uls, items); } } } private void validateFileCount(List<FileItem> items, int maxFileCount) throws FileCountLimitExceededException { if (!CollectionUtil.isNullOrEmpty(items) && maxFileCount != -1) { long fileCount = 0L; for(FileItem fileItem : items) { if (!fileItem.isFormField()) { ++fileCount; } if (fileCount > (long)maxFileCount) { throw new FileCountLimitExceededException("attachment", (long)maxFileCount); } } } } private Object processFileItems(Map<String, String> parameters, UploadSettingVO uploadSetting, IUploadFileConsumer consumer, List<FileItem> items) throws ApplicationException { Iterator<FileItem> iter = items.iterator(); List<FileInfoVO> files = new ArrayList(); while(iter.hasNext()) { FileItem item = (FileItem)iter.next(); if (!item.isFormField()) { if (item.getSize() == 0L) { throw new UploadException("huawei.jalor5.upload.support.00010003"); } this.validateExtention(uploadSetting, item); FileInfoVO fileInfo = new FileInfoVO(); String fileStorePath = MessageFormat.format("{0}/{1}", consumer.getUploadSetting(parameters).getRepository(), this.generatePathPart(uploadSetting)); PathUtil.makeDirs(fileStorePath); String fileStore = MessageFormat.format("{0}/{1}.jlr", fileStorePath, UUID.randomUUID().toString()); File uploadedFile = FileUtils.getFile(new String[]{fileStore}); Map<String, IFileContentHandler> downloadHandlersMap = Jalor.getContext().getBeansOfType(IFileContentHandler.class); if (downloadHandlersMap.size() > 0) { try { item.write(uploadedFile); fileInfo.setDisplayName(FileUtil.getFileName(item.getName())); fileInfo.setFileSize(item.getSize()); if (fileStore.contains("..")) { throw new IllegalArgumentException("fileStore path [" + fileStore + "] not support string [..]"); } fileInfo.setFilePath(fileStore); } catch (Exception ex) { LOGGER.error2("写文件异常,fileName:[{0}],exception:[{1}]", new Object[]{item.getName(), ex.getMessage()}); } } this.executeFileContentHandler(parameters, fileInfo, uploadedFile, downloadHandlersMap); fileStore = this.writeFile(item, uploadedFile); fileInfo.setDisplayName(FileUtil.getFileName(item.getName())); fileInfo.setFileSize(item.getSize()); fileInfo.setFilePath(fileStore); files.add(fileInfo); } } this.sendlogMessage((String)parameters.get("ulType"), files); return consumer.processFiles(parameters, files); } private void executeFileContentHandler(Map<String, String> parameters, FileInfoVO fileInfo, File uploadedFile, Map<String, IFileContentHandler> downloadHandlersMap) throws ApplicationException { for(Map.Entry<String, IFileContentHandler> entry : downloadHandlersMap.entrySet()) { Object result = ((IFileContentHandler)entry.getValue()).handler(parameters, fileInfo, "upload"); if (result != null && result instanceof CheckResult) { CheckResult reslut = (CheckResult)result; if (!reslut.isContinuation()) { if (uploadedFile.exists()) { uploadedFile.delete(); } throw new UploadException("huawei.jalor5.upload.support.00010006"); } } } } private void sendlogMessage(String type, List<FileInfoVO> files) throws ApplicationException { LogVO logVO = new LogVO(); logVO.buildAotoAttribute(); logVO.setClazz("UploadServlet"); logVO.setOperation("Upload"); logVO.setLogType(0); StringBuffer message = new StringBuffer(); for(FileInfoVO fileInfoVO : files) { message.append(", " + fileInfoVO.getDisplayName()); } String mess = null; if (message.length() > 1) { mess = message.substring(1, message.length()); } logVO.setStatus(1); logVO.setModule(type + " Upload"); logVO.setServerName(RequestUtil.getServerName()); logVO.setMessage("upload file name is " + mess); if (this.logService != null) { this.logService.asyncCreateLog(logVO); } else { try { LOGGER.info("logMessage: " + JsonUtil.objectToJson(logVO)); } catch (IOException ex) { LOGGER.info(ex); } } } private String writeFile(FileItem item, File uploadedFile) throws UploadException { try { if (!uploadedFile.exists()) { item.write(uploadedFile); } String region = ""; String bucketName = ""; try { PathUtil.canonicalPath(uploadedFile.getPath()); return StoreHelper.store(uploadedFile.getPath()); } catch (Exception ex) { LOGGER.error(ex); return uploadedFile.getPath(); } } catch (Exception ex) { throw new UploadException("huawei.jalor5.upload.support.00010001", ex); } } private void validateExtention(UploadSettingVO uploadSetting, FileItem item) throws UploadException { if (!this.isFileTypesAllowed(item.getName(), uploadSetting.getFileTypesAllowed())) { LOGGER.debug("File type {0} is not valid for {1}", new Object[]{FileUtil.getExtension(item.getName()), item.getName()}); throw new UploadException("huawei.jalor5.upload.support.00010002", StringUtil.join(uploadSetting.getFileTypesAllowed(), ",")); } else if (PathUtil.isInvalidPath(item.getName())) { throw new UploadException("huawei.jalor5.upload.support.00010002", "Path contains .."); } else if ("true".equals(ApplicationConfigProperties.getContextProperty("checkRealsFile")) && !this.checkRealFileType(uploadSetting, item)) { throw new UploadException("huawei.jalor5.upload.support.00010002", StringUtil.join(uploadSetting.getFileTypesAllowed(), ",")); } } private boolean isFileTypesAllowed(String fileName, List<String> fileTypesAllowed) { for(String fileType : fileTypesAllowed) { if (StringUtil.toLower(fileName).endsWith("." + StringUtil.toLower(fileType))) { return true; } } return false; } private String generatePathPart(UploadSettingVO uploadSetting) { if (uploadSetting.getFileSplitType().equals(FolderSplitType.ByDay)) { SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd"); return dayFormat.format(new Date()); } else { return com.huawei.it.jalor5.core.request.impl.RequestContext.getCurrent().getUser().getUserAccount(); } } private boolean checkRealFileType(UploadSettingVO uploadSetting, FileItem item) { LOGGER.info("check file real_code."); InputStream inputStream = null; label183: { boolean ex; try { if (!this.fileTypeInWhitelist(item)) { inputStream = item.getInputStream(); byte[] byt = new byte[4]; inputStream.read(byt, 0, byt.length); String code = this.bytesToHexString(byt); LOGGER.info("The file real_code is :" + code); inputStream.close(); HashMap<String, String> mimeTypeMap = null; try { mimeTypeMap = (HashMap)Jalor.getContext().getBean("HashMap." + EnvHolder.getApplication(), HashMap.class); } catch (Exception var24) { LOGGER.info("can't find Hash.Bean, We will load from local! Now,loading from HashMap.default.."); mimeTypeMap = (HashMap)Jalor.getContext().getBean("HashMap.default", HashMap.class); } String realType = (String)mimeTypeMap.get(code); if (StringUtil.isNullOrEmpty(realType)) { LOGGER.error("The file real_code cannot be discovered. Please configure it."); boolean var29 = false; return var29; } List<String> allowFileType = uploadSetting.getFileTypesAllowed(); boolean checkFirstStep = StringUtil.isNullOrEmpty(realType) ? false : this.checkFileType(realType, allowFileType); if (!checkFirstStep) { break label183; } boolean var10 = true; return var10; } ex = true; } catch (Exception ex) { LOGGER.error("UploadServlet check file real Type ERROR,the message is :" + ex); break label183; } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException var23) { } } } return ex; } LOGGER.error("The file real_code error ,Please configure it."); return false; } private boolean fileTypeInWhitelist(FileItem item) { String endItem = StringUtil.toLower(item.getName().substring(item.getName().lastIndexOf(".") + 1)); String whitelist = ApplicationConfigProperties.getContextProperty("whiteListUlType"); if (!StringUtil.isNullOrEmpty(whitelist) && whitelist.indexOf(endItem) != -1) { return true; } else { return FILE_TYPE.contains(endItem); } } private boolean checkFileType(String type, List<String> allowType) { boolean result = false; if (!StringUtil.isNullOrEmpty(type) && allowType.size() >= 1) { for(String t : allowType) { if (type.toLowerCase(Locale.ROOT).contains(t.toLowerCase(Locale.ROOT))) { result = true; break; } } return result; } else { return result; } } private String bytesToHexString(byte[] src) { StringBuilder sbBuilder = new StringBuilder(); if (src != null && src.length > 0) { for(int i = 0; i < src.length; ++i) { String str = Integer.toHexString(src[i] & 255).toUpperCase(Locale.getDefault()); if (str.length() < 2) { sbBuilder.append(0); } sbBuilder.append(str); } return sbBuilder.toString(); } else { return null; } } }

<UserControl x:Class="MES.Client.Tooling.UserControls.ModifyMeasuringFaliureTracing" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:local="clr-namespace:MES.Client.Tooling.UserControls" xmlns:hc1="clr-namespace:Minio.DataModel;assembly=Minio" d:DataContext="{d:DesignInstance Type=local:ModifyMeasuringFaliureTracingViewModel}" mc:Ignorable="d" d:DesignHeight="350" d:DesignWidth="600"> <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/MES.Client.UICommon;component/Styles/CommStyle.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources> <hc:DialogContainer> <Border CornerRadius="3" Background="{DynamicResource DialogPrimaryBrush}"> <Grid TextBlock.Foreground="{DynamicResource DefaultFontColorBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Border CornerRadius="4 4 0 0" Background="{DynamicResource DialogTitleBrush}" BorderThickness="0" TextBlock.Foreground="{DynamicResource DefaultFontColorBrush}"> <Grid Margin="0 10"> <TextBlock Text="{Binding Title,FallbackValue=量具失效追溯管理}" FontWeight="SemiBold" FontSize="14" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20 0 0 0"/> <Button Style="{StaticResource CloseButtonStyle}"/> </Grid> </Border> <Grid Grid.Row="1" Margin="0 10"> <ScrollViewer VerticalScrollBarVisibility="Auto"> <UniformGrid Columns="1"> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[ToolCode],FallbackValue=ToolCode}" VerticalAlignment="Center" Text="{Binding SelectData.ToolCode}" hc:InfoElement.Necessary="True"/> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[DeviceName],FallbackValue=DeviceName}" VerticalAlignment="Center" Text="{Binding SelectData.DeviceName}" hc:InfoElement.Necessary="True"/> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[Manufacturer],FallbackValue=Manufacturer}" VerticalAlignment="Center" Text="{Binding SelectData.Manufacturer}"/> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[ModelSpec],FallbackValue=ModelSpec}" VerticalAlignment="Center" Text="{Binding SelectData.ModelSpec}"/> <hc:DateTimePicker hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[FailureDate],FallbackValue=FailureDate}" VerticalAlignment="Center" SelectedDateTime="{Binding SelectData.FailureDate}" hc:InfoElement.Necessary="True"/> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[FailurePhenomenon],FallbackValue=FailurePhenomenon}" VerticalAlignment="Center" Text="{Binding SelectData.FailurePhenomenon}" /> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[IsInvolveProduct],FallbackValue=IsInvolveProduct}" VerticalAlignment="Center" Text="{Binding SelectData.IsInvolveProduct}" /> <hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" Margin="10 0 10 10" hc:InfoElement.Title="{Binding Langs[Remark],FallbackValue=Remark}" VerticalAlignment="Center" Text="{Binding SelectData.Remark}"/> <hc:Upload hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Title="{Binding Langs[ProcessingRecords],FallbackValue=失效产品处理记录}" FileType="*" MaxCount="5" UploadUrl="{Binding UploadUrl}" Files="{Binding SelectData.ProcessingRecords, Mode=TwoWay}" hc:InfoElement.Placeholder="{Binding Langs[UploadTip],FallbackValue=处理记录请自行上传}"/> </UniformGrid> </ScrollViewer> </Grid> <DockPanel Grid.Row="2" Margin="13 0"> <Button Margin="10" Width="130" Background="{DynamicResource AddBtnBrush}" Foreground="White" BorderBrush="{DynamicResource AddBtnBrush}" Content="{Binding Langs[Confirm]}" DockPanel.Dock="Right" HorizontalAlignment="Right" Command="{Binding SaveCommand}"/> <Button Margin="10" Width="130" Content="{Binding Langs[Cancel]}" DockPanel.Dock="Right" HorizontalAlignment="Right" Command="hc:ControlCommands.Close"/> </DockPanel> </Grid> </Border> </hc:DialogContainer> </UserControl> <UserControl x:Class="MES.Client.Tooling.UserControls.MeasuringFaliureTracing" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:control="clr-namespace:MES.Client.UICommon.Controls;assembly=MES.Client.UICommon" hc:Dialog.Token="{Binding DialogToken}" d:DataContext="{d:DesignInstance Type=usercontrols:MeasuringFaliureTracingViewModel,IsDesignTimeCreatable=True}" xmlns:enum="clr-namespace:MES.Client.Toolkit.Enums;assembly=MES.Client.Toolkit" xmlns:usercontrols="clr-namespace:MES.Client.Tooling.UserControls" xmlns:sync="http://schemas.syncfusion.com/wpf" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/MES.Client.UICommon;component/Styles/CommStyle.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources> <hc:DialogContainer> <Grid Background="{DynamicResource MESPrimaryBrush}"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" Margin="10"> <TextBlock Text="量具失效追溯管理" FontWeight="DemiBold" VerticalAlignment="Center" FontSize="16" Margin="21 0 0 0"/> <Border BorderThickness="1 0 0 0" BorderBrush="{DynamicResource MESBorderBrush}" Margin="20 0" Height="24"/> <control:IconButton Visibility="{Binding MenuID,Converter={StaticResource getPermissonToVisibilityConverter},ConverterParameter={x:Static enum:PermissonEnum.Add }}" IBForeground="{DynamicResource PaginationSelectedBrush}" Background="{DynamicResource AddBtnBrush}" StrText="{Binding Langs[Add]}" Icon="" BorderThickness="0" FontSize="14" Margin="0 0 10 0" Command="{Binding ModifyCommand}"/> </StackPanel> <hc:Card Grid.Row="1" Margin="10 0 10 10" BorderThickness="1"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <DockPanel TextBlock.FontSize="14"> <hc:TextBox Margin="13" hc:InfoElement.Title="{Binding Langs[ToolCode],FallbackValue=ToolCode}" Text="{Binding SearchData.ToolCode}" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" hc:InfoElement.TitlePlacement="Left" HorizontalAlignment="Left" Width="150"/> <hc:TextBox Margin="13" hc:InfoElement.Title="{Binding Langs[DeviceName],FallbackValue=DeviceName}" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}" hc:InfoElement.TitlePlacement="Left" Width="150" Text="{Binding SearchData.DeviceName}"/> <hc:DateTimePicker Margin="13" SelectedDateTime ="{Binding SearchData.StartFailureDate}" Style="{StaticResource DateTimePickerPlus}" hc:InfoElement.Title="{Binding Langs[StartFailureDate],FallbackValue=StartFailureDate}" hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}"/> <hc:DateTimePicker Margin="13" SelectedDateTime="{Binding SearchData.EndFailureDate}" Style="{StaticResource DateTimePickerPlus}" hc:InfoElement.Title="{Binding Langs[EndFailureDate],FallbackValue=EndFailureDate}" hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Placeholder="{Binding Langs[PleaseEnter]}"/> <control:IconButton Visibility="{Binding MenuID,Converter={StaticResource getPermissonToVisibilityConverter},ConverterParameter={x:Static enum:PermissonEnum.Query }}" StrText="{Binding Langs[Search]}" Icon="" FontSize="14" HorizontalAlignment="Left" Margin="32 0 0 0" Command="{Binding SearchCommand}"/> </DockPanel> <control:MESSfDataGrid SelectedItems="{Binding SelectedItems,Mode=TwoWay}" SelectionMode="Extended" IsShowChooseBtn="True" MenuID="{Binding MenuID}" Token="{Binding DialogToken}" Margin="10 0 10 5" ItemsSource="{Binding DataList}" Grid.Row="1" Langs="{Binding Langs}" EditCommand="{Binding ModifyCommand}" DeleteCommand="{Binding DeleteCommand}" ImportCommand="{Binding SearchCommand}" SolTypeName="VENDOR" IsImport="{Binding MenuID,Converter={StaticResource getPermissonToBoolConverter},ConverterParameter={x:Static enum:PermissonEnum.Import }}" IsDel="{Binding MenuID,Converter={StaticResource getPermissonToBoolConverter},ConverterParameter={x:Static enum:PermissonEnum.Delete }}" IsEdit="{Binding MenuID,Converter={StaticResource getPermissonToBoolConverter},ConverterParameter={x:Static enum:PermissonEnum.Modify }}" IsDetail="False"> <control:MESSfDataGrid.Columns> <sync:GridTextColumn MappingName="Id" HeaderText="{Binding Langs[Id],FallbackValue=Id}" IsHidden="True"/> <sync:GridTextColumn MappingName="ItemNo" HeaderText="{Binding Langs[ItemNo],FallbackValue=ItemNo}" IsHidden="True"/> <sync:GridTextColumn MappingName="ToolCode" HeaderText="{Binding Langs[ToolCode],FallbackValue=ToolCode}" IsHidden="False"/> <sync:GridTextColumn MappingName="DeviceName" HeaderText="{Binding Langs[DeviceName],FallbackValue=DeviceName}" IsHidden="False"/> <sync:GridTextColumn MappingName="Manufacturer" HeaderText="{Binding Langs[Manufacturer],FallbackValue=Manufacturer}" IsHidden="False"/> <sync:GridTextColumn MappingName="ModelSpec" HeaderText="{Binding Langs[ModelSpec],FallbackValue=ModelSpec}" IsHidden="False"/> <sync:GridDateTimeColumn CustomPattern="yyyy-MM-dd HH:mm:ss" Pattern="CustomPattern" MappingName="FailureDate" HeaderText="{Binding Langs[FailureDate],FallbackValue=FailureDate}" IsHidden="False"/> <sync:GridTextColumn MappingName="FailurePhenomenon" HeaderText="{Binding Langs[FailurePhenomenon],FallbackValue=FailurePhenomenon}" IsHidden="False"/> <sync:GridTextColumn MappingName="IsInvolveProduct" HeaderText="{Binding Langs[IsInvolveProduct],FallbackValue=IsInvolveProduct}" IsHidden="False"/> <sync:GridTextColumn MappingName="ProcessingRecords" HeaderText="{Binding Langs[ProcessingRecords],FallbackValue=失效产品处理记录}" IsHidden="False"/> <sync:GridTextColumn MappingName="Remark" HeaderText="{Binding Langs[Remark],FallbackValue=Remark}" IsHidden="False"/> </control:MESSfDataGrid.Columns> </control:MESSfDataGrid> <control:YLHPagination Margin="10 5" Grid.Row="2" PageIndex="{Binding PageIndex,Mode=TwoWay}" TotalCount="{Binding TotalCount}" PageSize="{Binding PageSize,Mode=TwoWay}" TotalPages="{Binding TotalPages,Mode=TwoWay}"/> </Grid> </hc:Card> </Grid> </hc:DialogContainer> </UserControl>修改里面的上传文件那块代码 <hc:Upload hc:InfoElement.TitlePlacement="Left" hc:InfoElement.TitleWidth="100" hc:InfoElement.Title="{Binding Langs[ProcessingRecords],FallbackValue=失效产品处理记录}" FileType="*" MaxCount="5" UploadUrl="{Binding UploadUrl}" Files="{Binding SelectData.ProcessingRecords, Mode=TwoWay}" hc:InfoElement.Placeholder="{Binding Langs[UploadTip],FallbackValue=处理记录请自行上传}"/>using HandyControl.Tools.Extension; using MES.Client.Tooling.Models.MeasuringFaliureTracing; using MES.Client.Toolkit.Comm; using MES.Client.Toolkit.Enums; using MES.Client.Toolkit.HTTP; using MES.Client.UICommon.Controls; using MES.Common.Toolkit.Helper; using System; using System.Collections.Generic; using System.Text; using System.Windows.Controls; using System.Windows.Input; namespace MES.Client.Tooling.UserControls { /// /// ModifyMeasuringFaliureTracing.xaml 的交互逻辑 /// public partial class ModifyMeasuringFaliureTracing : UserControl { public ModifyMeasuringFaliureTracing(MeasuringFailureTracingModel masModel, Dictionary<string, string> langs, OperateStateEmun stateEmun) { InitializeComponent(); DataContext = new ModifyMeasuringFaliureTracingViewModel(masModel, langs, stateEmun); } } public class ModifyMeasuringFaliureTracingViewModel : ViewModelBase, IDialogResultable<bool> { private MeasuringFailureTracingModel selectData = new(); private Dictionary<string, string> langs = new(); private string title = string.Empty; public string UploadUrl { get; set; } = "api/Tooling/MeasuringFaliureTracing/UnLoad"; public string ProcessingRecords { get; set; } = string.Empty; public MeasuringFailureTracingModel SelectData { get => selectData; set { selectData = value; PCEH(); } } public Dictionary<string, string> Langs { get => langs; set { langs = value; PCEH(); } } public bool Result { get; set; } = false; public Action? CloseAction { get; set; } /// /// 保存 /// public ICommand SaveCommand { get; set; } public string Title { get => title; set { title = value; PCEH(); } } /// /// 操作类型 /// public OperateStateEmun StateEmun { get; set; } public ModifyMeasuringFaliureTracingViewModel(MeasuringFailureTracingModel itemtModel, Dictionary<string, string> langs, OperateStateEmun stateEmun) { StateEmun = stateEmun; SelectData = itemtModel.Clone(); //if (stateEmun == OperateStateEmun.Add) //{ // SelectData.LastAnalysisDate ??= DateTime.Today.AddDays(-30); // SelectData.NextRemindDate ??= DateTime.Today.AddDays(30); //} Langs = langs; Initialize(); SaveCommand = new CommandBase(async l => { if (!Check()) return; string ret = string.Empty; int toolCodeLength = getStringLength(SelectData.ToolCode); if (toolCodeLength > 40) { Message.Warnning(Langs.LangGetValue("ToolCode") + Langs.LangGetValue("CharacterLengthIsLong")); return; } int deviceNameLength = getStringLength(SelectData.DeviceName); if (deviceNameLength > 40) { Message.Warnning(Langs.LangGetValue("DeviceName") + Langs.LangGetValue("CharacterLengthIsLong")); return; } if (!string.IsNullOrEmpty(SelectData.Manufacturer)) { int manufacturerLength = getStringLength(SelectData.Manufacturer); if (manufacturerLength > 40) { Message.Warnning(Langs.LangGetValue("Manufacturer") + Langs.LangGetValue("CharacterLengthIsLong")); return; } } if (!string.IsNullOrEmpty(SelectData.ModelSpec)) { int modelSpecLength = getStringLength(SelectData.ModelSpec); if (modelSpecLength > 100) { Message.Warnning(Langs.LangGetValue("ModelSpec") + Langs.LangGetValue("CharacterLengthIsLong")); return; } } if (!string.IsNullOrEmpty(SelectData.FailurePhenomenon)) { int failurePhenomenonLength = getStringLength(SelectData.FailurePhenomenon); if (failurePhenomenonLength > 200) { Message.Warnning(Langs.LangGetValue("FailurePhenomenon") + Langs.LangGetValue("CharacterLengthIsLong")); return; } } if (!string.IsNullOrEmpty(SelectData.IsInvolveProduct)) { int isInvolveProductLength = getStringLength(SelectData.IsInvolveProduct); if (isInvolveProductLength > 1) { Message.Warnning(Langs.LangGetValue("IsInvolveProduct") + Langs.LangGetValue("CharacterLengthIsLong")); return; } } if (!string.IsNullOrEmpty(SelectData.Remark)) { int remarkLength = getStringLength(SelectData.Remark); if (remarkLength > 300) { Message.Warnning(Langs.LangGetValue("Remark") + Langs.LangGetValue("CharacterLengthIsLong")); return; } } ret = await APIHelper.PostSendAsync(ApiRoutes.SaveMeasuringFaliureTracing, SelectData); if (ret is not null) { string successMsg = StateEmun == OperateStateEmun.Add ? Langs.LangGetValue("添加成功") : Langs.LangGetValue("修改成功"); MESGrowl.MESSuccess(successMsg); Result = true; CloseAction?.Invoke(); } }); } public int getStringLength(string str) { if (str.Equals(string.Empty)) return 0; int strlen = 0; ASCIIEncoding strData = new ASCIIEncoding(); // 将字符串转换为ASCII编码的字节数字 byte[] strBytes = strData.GetBytes(str); for (int i = 0; i <= strBytes.Length - 1; i++) { if (strBytes[i] == 63) // 中文都将编码为ASCII编码63,即"?"号 strlen += 2; strlen++; } return strlen; } private bool Check() { if (string.IsNullOrEmpty(SelectData.ToolCode)) { Message.Warnning(Langs.LangGetValue("ToolCode") + Langs.LangGetValue("IsInvalid")); return false; } // 检查DeviceName(设备名称)是否为空 if (string.IsNullOrEmpty(SelectData.DeviceName)) { Message.Warnning(Langs.LangGetValue("DeviceName") + Langs.LangGetValue("IsNull")); return false; } //// 检查LastAnalysisDate(上次分析日期)是否有值 //if (!SelectData.LastAnalysisDate.HasValue) //{ // Message.Warnning(Langs.LangGetValue("LastAnalysisDate") + Langs.LangGetValue("IsNull")); // return false; //} // 检查TestItem(测试项目)是否为空 if (!SelectData.FailureDate.HasValue) { Message.Warnning(Langs.LangGetValue("FailureDate") + Langs.LangGetValue("IsNull")); return false; } return true; } private void Initialize() { switch (StateEmun) { case OperateStateEmun.Add: Title = Langs.LangGetValue("新增"); break; case OperateStateEmun.Edit: Title = Langs.LangGetValue("修改"); break; case OperateStateEmun.Detail: break; default: break; } } } }using HandyControl.Controls; using HandyControl.Tools.Extension; using MES.Client.Models.Dtos; using MES.Client.Models.Models; using MES.Client.Tooling.Dto.MeasuringFaliureTracing; using MES.Client.Tooling.Models.MeasuringFaliureTracing; using MES.Client.Toolkit.Comm; using MES.Client.Toolkit.Enums; using MES.Client.Toolkit.HTTP; using MES.Client.UICommon.Comm; using MES.Client.UICommon.Controls; using MES.Client.UICommon.Helper; using MES.Common.Toolkit.Helper; using Microsoft.Win32; using Newtonsoft.Json; using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.IO; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; namespace MES.Client.Tooling.UserControls { /// /// MeasuringFaliureTracing.xaml 的交互逻辑 /// public partial class MeasuringFaliureTracing : UserControl { private MeasuringFaliureTracingViewModel model; public MeasuringFaliureTracing() { InitializeComponent(); DataContext = model = new MeasuringFaliureTracingViewModel(); Loaded += MeasuringFaliureTracing_Loaded; } //} private void MeasuringFaliureTracing_Loaded(object sender, RoutedEventArgs e) { if (!string.IsNullOrEmpty(model.MenuID)) return; model.MenuID = string.IsNullOrEmpty(Uid) ? "" : Uid; } } public class MeasuringFaliureTracingViewModel : CommonListViewModel<MeasuringFaliureTracingInDto, MeasuringFailureTracingModel> { private MeasuringFailureTracingModel selectedModel = new(); /// /// 选中的MSA测量记录 /// public MeasuringFailureTracingModel SelectedModel { get => selectedModel; set { selectedModel = value; PCEH(); } } //private ObservableCollection<MeasuringFailureTracingModel> selectedItems = new(); //public ObservableCollection<MeasuringFailureTracingModel> SelectedItems //{ // get => selectedItems; // set // { // SetProperty(ref selectedItems, value); // SelectedIds = value.Select(item => item.Id).ToList(); // } //} //// 2. 选中的 ID 列表(传给后端) //private List<string> selectedIds = new(); //public List<string> SelectedIds //{ // get => selectedIds; // set => SetProperty(ref selectedIds, value); //} /// /// 查询条件 /// private MeasuringFaliureTracingInDto searchData = new(); public MeasuringFaliureTracingInDto SearchData { get => searchData; set { searchData = value; PCEH(); } } public MeasuringFaliureTracingViewModel() { PageIndex = 1; PageSize = 20; /// /// 查询 /// SearchCommand = new CommandBase(async l => { var loading = Dialog.Show(new UICommon.Controls.LoadingCircle(), DialogToken); try { SearchParameter = new MeasuringFaliureTracingInDto { Id = SearchData.Id, ToolCode = SearchData.ToolCode, FailureDate = SearchData.FailureDate, DeviceName = SearchData.DeviceName }; SearchParameter.PageIndex = PageIndex; SearchParameter.PageSize = PageSize; SearchParameter.StartFailureDate = SearchData.StartFailureDate; SearchParameter.EndFailureDate = SearchData.EndFailureDate; var ret = await APIHelper.PostSendDtoAsync( ApiRoutes.GetMeasuringFaliureTracingList, SearchParameter ); if (ret != null) { TotalCount = ret.TotalCount; TotalPages = ret.TotalPages; var dataListJson = ret?.DataList?.ToJson() ?? "[]"; DataList = JsonConvert.DeserializeObject<ObservableCollection<MeasuringFailureTracingModel>>(dataListJson) ?? new(); if (l != null && !string.IsNullOrEmpty(l.ToString())) SelectedItem = DataList.FirstOrDefault(f => f.ToolCode == l.ToString()); } } catch (Exception ex) { Message.Error($"查询失败:{ex.Message}"); } finally { loading.Close(); } }); ModifyCommand = new CommandBase(async l => { bool flag = false; if (l != null) { flag = await Dialog.Show(new ModifyMeasuringFaliureTracing((MeasuringFailureTracingModel)l, Langs, OperateStateEmun.Edit), DialogToken).GetResultAsync<bool>(); } else { flag = await Dialog.Show(new ModifyMeasuringFaliureTracing(new MeasuringFailureTracingModel(), Langs, OperateStateEmun.Add), DialogToken).GetResultAsync<bool>(); } if (flag) { SearchCommand?.Execute(null); } }); DeleteCommand = new CommandBase(async l => { if (l is MeasuringFailureTracingModel deleteItem) { if (Message.Ask(Langs.LangGetValue("DeleteConfirm"))) { List<IdInDto> inDto = new(); inDto.Add(new IdInDto { Id = deleteItem.Id }); var ret = await APIHelper.PostSendAsync(ApiRoutes.DeleteMeasuringFaliureTracing, inDto); if (ret != null) { DataList.Remove(deleteItem); TotalCount -= 1; } } } }); ExportCommand = new CommandBase(async l => { try { var exportParam = new MeasuringFaliureTracingInDto { ToolCode = SearchData.ToolCode, DeviceName = SearchData.DeviceName, StartFailureDate = SearchData.StartFailureDate, EndFailureDate = SearchData.EndFailureDate, IsExport = true }; var exportData = await APIHelper.PostSendDtoAsync>( ApiRoutes.ExportMeasuringFaliureTracingPageData, exportParam ); if (exportData == null || !exportData.Any()) { Message.Warnning(Langs.LangGetValue("NoDataToExport")); return; } DataTable ConvertToDataTable<T>(IEnumerable<T> dataList) { var dt = new DataTable(); if (dataList == null || !dataList.Any()) return dt; var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in props) { dt.Columns.Add(prop.Name, typeof(string)); } foreach (var item in dataList) { var row = dt.NewRow(); foreach (var prop in props) { object value = prop.GetValue(item); if (value == null) { value = string.Empty; } else if (value is DateTime date) { value = date.ToString("yyyy-MM-dd HH:mm:ss"); } else if (value is IEnumerable<string> stringList) { value = string.Join(", ", stringList); } else { value = value.ToString(); } row[prop.Name] = value; } dt.Rows.Add(row); } return dt; } var dataTable = ConvertToDataTable(exportData); bool DataTableToExcel(DataTable dt, string filePath) { try { IWorkbook workbook = filePath.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase) ? (IWorkbook)new XSSFWorkbook() : new HSSFWorkbook(); ISheet sheet = workbook.CreateSheet("ExportData"); int rowIndex = 0; var headerRow = sheet.CreateRow(rowIndex++); for (int i = 0; i < dt.Columns.Count; i++) { headerRow.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName); } foreach (DataRow dataRow in dt.Rows) { var row = sheet.CreateRow(rowIndex++); for (int i = 0; i < dt.Columns.Count; i++) { object cellValue = dataRow[i] is DBNull ? string.Empty : dataRow[i]; if (cellValue is DateTime date) { row.CreateCell(i).SetCellValue(date.ToString("yyyy-MM-dd HH:mm:ss")); } else { row.CreateCell(i).SetCellValue(cellValue?.ToString()); } } } for (int i = 0; i < dt.Columns.Count; i++) { sheet.AutoSizeColumn(i); } using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { workbook.Write(fs,false); } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Excel导出失败: {ex.Message}"); return false; } } var saveDialog = new SaveFileDialog { Title = Langs.LangGetValue("SelectExportPath"), Filter = "Excel文件|*.xls;*.xlsx", FileName = $"{DateTime.Now:yyyyMMdd}_MeasuringFailureExport.xlsx", RestoreDirectory = true, DefaultExt = "xlsx", OverwritePrompt = true }; if (saveDialog.ShowDialog() == true) { bool exportSuccess = DataTableToExcel(dataTable, saveDialog.FileName); if (exportSuccess) { Message.Info(Langs.LangGetValue("ExportSuccess")); } else { Message.Error(Langs.LangGetValue("ExportFailed_FileWrite")); } } } catch (Exception ex) { Message.Error($"{Langs.LangGetValue("ExportFailed")}: {ex.Message}"); } }); } public override void CommViewModelLoad() { SearchCommand?.Execute(null); } } }

最新推荐

recommend-type

zbs220_uvcMjpeg_6752_1757657366628.zip

zbs220_uvcMjpeg_6752_1757657366628.zip
recommend-type

简单演示如何在MATLAB中使用dlib人脸检测和对齐_Simple demo showing how to use d

简单演示如何在MATLAB中使用dlib人脸检测和对齐_Simple demo showing how to use dlib face detection and alignment in MATLAB.zip
recommend-type

小区电动车停放管理系统 SpringBoot3+Vue.js3 2025毕业设计

本项目编号:25258,题目:小区电动车停放管理系统 录屏:https://www.bilibili.com/video/BV1cXhCzRE6V 前端技术:Vue.js3(管理后台+用户前台) 后端技术:SpringBoot3 数据库:MySQL8 启动教程:https://www.bilibili.com/video/BV1dkAme8EmW
recommend-type

zx-github_android-mvp-mvvm-flytour_40760_1757658203185.zip

zx-github_android-mvp-mvvm-flytour_40760_1757658203185.zip
recommend-type

基于自建数据集的PyTorch U-Net视觉寻路小车训练

本资源包含一个基于 PyTorch 框架实现的 U-Net 深度学习模型,用于训练和验证视觉寻路小车任务。 主要特点: 模型架构 使用经典的 U-Net 编码器–解码器结构,适配图像分割与路径预测任务。 网络支持多层特征融合,能够有效提取小车行驶场景中的道路信息。 数据集 数据集为 自建采集,涵盖多种小车行驶场景(直道、弯道、障碍物等)。 每张图像均配有标注,用于训练模型进行 车道/路径分割。 训练流程 提供 数据预处理、模型训练与验证脚本。 支持 GPU 加速,可根据硬件条件选择单卡或多卡训练。 训练日志与可视化结果(loss 曲线、预测可视化)均可保存。 应用场景 用于小车的 自主导航与路径规划研究。 可扩展至其他 机器人视觉感知任务。
recommend-type

RaspberryMatic与Docker整合:CCU2固件容器化操作指南

### Docker与CCU2固件整合 #### 知识点1:Docker容器技术 Docker是一种开源的容器化平台,它允许开发者将应用及其依赖打包到一个可移植的容器中,该容器可以在任何支持Docker的机器上运行。Docker容器和传统的虚拟机不同,它不需要完整的操作系统镜像,而是利用宿主机的操作系统内核,实现了轻量级的隔离,启动速度快,资源消耗低。 #### 知识点2:CCU2固件与OpenHAB CCU2(CCU代表Comet Control Unit)固件通常用在HomeMatic智能家居自动化系统中,它负责管理和控制HomeMatic的设备。CCU2运行的是一个基于Linux的自定义系统,专门优化用于与HomeMatic硬件和软件通信。当把CCU2固件用于Docker容器时,意味着你可以在任何支持Docker的设备上,通过容器化的方式部署和运行CCU2环境,从而支持HomeMatic设备的控制。 #### 知识点3:RaspberryMatic RaspberryMatic是为树莓派量身打造的一个项目,它允许用户在树莓派上运行CCU2固件。项目提供了一整套的HomeMatic体验,包括备份功能、Dutty-Cycle、LAN GW等。RaspberryMatic的一个显著优点是支持多种架构,包括x86_64/amd64、ARM和ARM64。 #### 知识点4:Docker容器部署脚本 "docker-ccu"项目提供了一套脚本,这些脚本能够自动化创建一个Docker容器来运行CCU2固件。通常这类脚本命名为`deploy.sh`,开发者或者最终用户可以通过运行这些脚本来快速部署和启动Docker容器,而无需手动配置和启动容器的每一个步骤。 #### 知识点5:数据备份与迁移 在使用Docker容器进行部署时,用户可能需要在不同环境下迁移数据或者保留原有数据。脚本中提到了数据保留的问题,如果用户之前使用的是其他方式部署,比如非Docker方式或者使用了特定的docker卷或者容器名称,那么在调用`deploy.sh`脚本部署时,需要对设置进行相应的调整,以保证数据的完整性。 #### 知识点6:仓库维护与开源社区 项目维护者提到了不再计划继续更新该存储库,并提出了将仓库设置为只读模式的想法。这在开源社区中是比较常见的情况,尤其是在维护者有新的兴趣点或者由于个人时间限制时。在此情况下,开源项目可以通过社区协作来继续维护,或者寻求其他维护者的接手。 #### 知识点7:Shell脚本编写 由于项目中提到了一个叫做`deploy.sh`的脚本文件,这说明脚本是用Shell语言编写的。Shell脚本非常适合于执行自动化任务,比如配置环境、启动服务、管理文件系统等,因此在自动化部署或系统管理中经常被使用。了解Shell脚本编写,对于自动化管理Docker容器等任务至关重要。 #### 知识点8:社区支持和反馈 项目维护者在描述中提到,如果在一个月内没有收到任何关于将官方CCU作为容器使用的反馈,将会把仓库设置为只读模式。这表明了开源社区中项目的发展很大程度上依赖于社区成员的反馈和支持。因此,了解如何与开源项目互动,提交问题、建议和补丁,是参与开源社区的重要途径。 #### 知识点9:固件概念与兼容性 CCU2固件特别设计用于某些特定硬件,但通过Docker化的方式,开发者可以跨平台运行CCU2固件,这增加了固件的兼容性。Docker的隔离性允许用户在一个通用的软件层面上运行原本可能受限于特定硬件的固件,从而扩展了固件的应用场景。 #### 知识点10:操作系统架构支持 项目支持包括x86_64/amd64、ARM和ARM64在内的多种架构,说明了Docker容器在不同硬件平台上的高度可移植性。这一特点使得开发者可以在各种硬件上部署相同的环境,简化了跨平台应用的开发和部署。 #### 结语 该文档提供了一个关于如何将特定固件整合入Docker容器的方案,并说明了项目维护者对于未来发展的规划。这些内容不仅对有志于尝试或扩展该项目的个人有指导意义,同时也展示了开源社区协作以及Docker技术在部署和管理复杂系统环境中的重要性和便利性。
recommend-type

手把手封装SDK:C#如何高效集成汉印D35BT打印功能

# 摘要 本文围绕C# SDK封装与汉印D35BT打印机集成的技术实践展开,系统阐述了SDK封装的理论基础、架构设计及面向对象设计原则的应用。文章分析了汉印D35BT打印机的通信协议与API调用方式,并详细介绍了在C#中实现蓝牙设备交互与数据发送的方法。通过核心打印功能的类封装、异步任务处理机制的设计,提升了SDK的易用性与扩展性。结合WinForm项目示例验证功能完整性后,进一步探讨了SDK的性能优化策略、测试方法及发布流程,构建了从设计、实现到部署的完整技术路径。 # 关键字 SDK封装;蓝牙通信;面向对象设计;异步打印;API调用;NuGet包发布 参考资源链接:[C#开
recommend-type

VM虚拟机ubuntu桥接主机无线网络

### 配置 VMware Ubuntu 桥接模式连接无线网络 在 VMware 中配置 Ubuntu 虚拟机通过桥接模式连接主机的无线网络,需要确保虚拟机与主机处于同一网络段,并能够通过主机的无线网卡直接访问外部网络。以下是详细的配置步骤: #### VMware 设置桥接模式 1. **以管理员权限运行 VMware**,进入 **编辑 > 虚拟网络编辑器**。 2. 在 **虚拟网络编辑器** 界面中,找到 **VMnet0(桥接模式)** 的设置部分。 3. 在 **“桥接到”** 的下拉菜单中,选择主机的无线网卡设备。 4. 勾选 **“自动设置桥接”** 选项,确保 VMwar
recommend-type

Ruby on Rails跳蚤市场应用开发详解

根据提供的文件信息,我们可以从中提炼出以下知识点: ### 标题知识点 - **freemarket_sample_72h** - 标题暗示这是一份关于名为“freemarket”的跳蚤市场应用程序的72小时开发样例或原型。 - 样例名称“freemarket_sample_72h”可能用于内部标识或者版本控制,表明该样本是在有限的时间内(即72小时内)完成的。 ### 描述知识点 - **网站结构** - 首页:应用程序的入口点,通常包含总体介绍和导航链接。 - 产品页面:展示产品的列表或者详细信息。 - 展览页:可能指专门展示某些特殊产品或促销产品的页面。 - 应用信息:关于应用程序的基本信息,如版本号、开发团队、联系方式等。 - 应用概述:对应用程序功能和目标用户群体的简介。 - **用户账户信息** - 测试账号:为开发者或测试者提供的虚拟用户账号信息,以便进行应用程序的内部测试。 - 购买者信息:提供了邮箱地址、密码以及购买卡信息,是进行交易和购买所必需的。 - 卖家信息:提供了卖家的邮箱地址和密码,用于登录卖家账户进行产品上架和管理。 - **功能列表** - 新用户注册:允许新用户创建账户。 - 登录功能:用户可以使用凭证登录应用程序。 - 产品列表功能:展示所有可购买的产品。 - 产品购买功能:用户可以购买产品,涉及到支付信息的处理。 - 产品类别注册和显示:允许用户查看不同的产品分类。 - 产品详细信息显示:展示每个产品的详细信息,如描述、价格等。 - 编辑和删除列出的产品:赋予管理员或卖家权利更新或移除产品信息。 - **开发环境** - Ruby 2.5.1:这是Ruby编程语言的一个版本。 - Ruby on Rails 5.4.2:这是一个使用Ruby语言编写的开源Web应用框架。 - MySQL 14.14:这是一个流行的开源关系型数据库管理系统。 - Github:一个用于代码托管和版本控制的平台。 - AWS:亚马逊提供的云服务平台,包括EC2(弹性计算云)和S3(简单存储服务)。 - Capistrano:是一个开源的自动化部署工具,常用于Ruby on Rails项目。 - **开发周期和工作时间** - 开发时间:约4周,说明了项目从开始到完成所需的时间。 - 每天平均工作时间:大约9小时,表明项目的紧凑和开发团队的努力。 - 开发系统人数:4,指出了参与该项目的开发人员数量。 - 敏捷类型:可能指的是一种开发过程,强调快速迭代和响应变化。 ### 标签知识点 - **Ruby** - 这个标签直接指向了Ruby编程语言,说明该应用程序是使用Ruby开发的。 ### 压缩包子文件的文件名称列表知识点 - **freemarket_sample_72h-master** - 这是源代码压缩包的文件名称,指示了一个版本控制中的主分支(master)。 通过这些信息,我们可以了解到该应用程序是一个在线跳蚤市场,它允许用户注册、登录、查看和购买产品,并且提供了产品管理功能。项目采用Ruby语言和Ruby on Rails框架开发,并使用了多种技术和平台,如MySQL数据库、Github、AWS云服务以及Capistrano部署工具。项目开发遵循敏捷开发原则,并在一个紧凑的时间表中完成。
recommend-type

【C#条码打印实战技巧】:汉印D35BT数据格式转换全攻略

# 摘要 本文围绕C#语言实现条码打印的技术方案展开,重点以汉印D35BT打印机为实践对象,系统性地讲解了条码打印中数据格式的基本原理与处理方法。文章分析了条码打印的数据流向、通信协议与命令集结构,探讨了在C#开发环境下实现数据格式转换、命令封装与容错机制的关键技术。通过完整的打印流程实现、性能优化策略以及常见问题排查方法的介绍,帮助开发者构建高效稳定的条码打印应用。同时,文章还展望了条码打印技术在多协议支持、云服务集成与企业级系统对接方面的拓展方向。 # 关键字 条码打印;数据格式;C#开发;通信协议;命令封装;容错机制 参考资源链接:[C#开发汉印D35BT条码打印机源代码