diff --git a/src/main/java/com/youlai/boot/app/packages/component/ApkMetaParser.java b/src/main/java/com/youlai/boot/app/packages/component/ApkMetaParser.java index 9c0207fd..64511263 100644 --- a/src/main/java/com/youlai/boot/app/packages/component/ApkMetaParser.java +++ b/src/main/java/com/youlai/boot/app/packages/component/ApkMetaParser.java @@ -15,6 +15,7 @@ import net.dongliu.apk.parser.ApkFile; import net.dongliu.apk.parser.bean.ApkMeta; import net.dongliu.apk.parser.bean.Icon; import org.apache.commons.lang3.StringUtils; +import org.glassfish.jaxb.core.v2.TODO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zeroturnaround.zip.ZipUtil; @@ -45,7 +46,10 @@ import java.util.zip.ZipInputStream; */ public class ApkMetaParser { private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() + // manifest.json / info.json 可能包含本类未声明的额外字段(如 locales_name), + // 忽略未知字段避免反序列化失败 + .configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); private static final Logger LOGGER = LoggerFactory.getLogger(ApkMetaParser.class); /** @@ -470,9 +474,7 @@ public class ApkMetaParser { throw new IOException("容器内未找到 manifest.json 文件: " + xapkFile.getName()); } - XapkContent content = readXapkContent(xapkFile); - List apks = content.apks; - byte[] manifestJson = content.manifestJson; + byte[] manifestJson = ZipUtil.unpackEntry(xapkFile, "manifest.json"); XapkJsonInfo xapkJsonInfo = OBJECT_MAPPER.readValue(manifestJson, XapkJsonInfo.class); @@ -498,26 +500,24 @@ public class ApkMetaParser { apkMetaResult.setSplitApkList(fileList); SplitApks baseApk = splitApksList.stream().filter(splitApks -> splitApks.getId().equals("base")).findFirst().orElse(null); + if (baseApk == null) { throw new IOException("容器内未找到 base apk 文件: " + xapkFile.getName()); } - ContainerEntry baseApkEntry = apks.stream().filter(containerEntry -> containerEntry.name.equals(baseApk.getFile())).findFirst().orElse(null); - if (baseApkEntry == null) { - throw new IOException("容器内未找到 base apk 文件: " + xapkFile.getName()); - } + LOGGER.info("baseApk info = " + baseApk); + + byte[] baseApkBytes = ZipUtil.unpackEntry(xapkFile, baseApk.getFile()); // 2) 若经验法则未命中,兜底:逐个尝试解析,第一个拿到 packageName 的即为主包 - ApkMetaResult result = tryParseApkBytes(baseApkEntry.data); - if (result == null) { - throw new IOException("容器内未找到 base apk 文件: " + xapkFile.getName()); + ApkMetaResult result = tryParseApkBytes(baseApkBytes); + if (result != null) { + // 主 APK 解析出的字段优先(覆盖容器 JSON 中的值) + overrideIfDifferent(apkMetaResult, result); + apkMetaResult.setCompileSdk(result.getCompileSdk()); + apkMetaResult.setAbiList(result.getAbiList()); } - // 主 APK 解析出的字段优先(覆盖容器 JSON 中的值) - overrideIfDifferent(apkMetaResult, result); - apkMetaResult.setCompileSdk(result.getCompileSdk()); - apkMetaResult.setAbiList(result.getAbiList()); - return apkMetaResult; } @@ -766,6 +766,8 @@ public class ApkMetaParser { return result; } } catch (Exception e) { + // TODO 2026.08.15 有些会报错 newPosition > limit: (524301 > 228) + LOGGER.error("tryParseApkBytes error", e); return null; } finally { if (tmp != null && tmp.exists()) { diff --git a/src/main/java/com/youlai/boot/app/packages/model/dto/XapkJsonInfo.java b/src/main/java/com/youlai/boot/app/packages/model/dto/XapkJsonInfo.java index 26c480d7..6d983b3b 100644 --- a/src/main/java/com/youlai/boot/app/packages/model/dto/XapkJsonInfo.java +++ b/src/main/java/com/youlai/boot/app/packages/model/dto/XapkJsonInfo.java @@ -6,6 +6,7 @@ import lombok.Data; import java.io.Serializable; import java.util.List; +import java.util.Map; @Data @@ -16,6 +17,10 @@ public class XapkJsonInfo implements Serializable { private int xapk_version; private String package_name; private String name; + /** + * 各语言代码到本地化应用名的映射(如 "zh-CN": "GitHub")。 + */ + private Map locales_name; private long version_code; private String version_name; private int min_sdk_version;