Android应用中JAR库访问自身内部资源的可行性咨询
Android依赖库读取自身JAR资源是否可行?
我有一个能从自身JAR资源中读取随机文件的JAR库,将其作为依赖添加至Android应用后,运行时该库无法读取自身资源(资源仅供库内部使用,无需Android应用调用)。此前查阅的相关回答均称无法实现,但这些回答距今至少已有10年,Android系统已发生诸多变化,请问当前是否可行?
以下是库访问资源的代码实现:
public static List<String> getAllResourcesFromFolder(String path) throws IOException { List<String> filenames = new ArrayList(); URL dirURL = ResourcesUtils.class.getClassLoader().getResource(path); if (dirURL != null && dirURL.getProtocol().equals("jar")) { JarURLConnection jarConn = (JarURLConnection)dirURL.openConnection(); JarFile jarFile = jarConn.getJarFile(); Enumeration<JarEntry> entries = jarFile.entries(); while(entries.hasMoreElements()) { String name = ((JarEntry)entries.nextElement()).getName(); if (name.startsWith(path) && !name.equals(path + "/")) { String entry = name.substring(path.length() + 1); int checkSubdir = entry.indexOf("/"); if (checkSubdir < 0) { filenames.add(entry); } } } } else if (dirURL != null) { try { return Arrays.asList((new File(dirURL.toURI())).list()); } catch (NullPointerException | URISyntaxException var9) { Exception e = var9; throw new IOException("Unable to convert URL to URI", e); } } return filenames; }
解答
当前是可以实现的,但现有代码在Android环境下存在适配问题,需针对性调整:
问题分析
- 类加载器差异:Android类加载器与标准JVM不同,
ClassLoader.getResource(path)无法稳定定位库内资源——因为Android会将依赖库打包进APK(本质为ZIP文件),而非独立JAR。 - JarFile获取限制:Android 7.0+(API 24)及更高版本中,直接获取
JarFile可能因APK优化(如拆分、DEX优化)出现权限或路径解析问题。
调整方案
方案1:基于类加载器的适配实现
针对库内部资源读取,可通过遍历类加载器返回的资源枚举来实现,避免直接依赖JarFile的路径解析:
public static List<String> getAllResourcesFromFolder(String path) throws IOException { List<String> filenames = new ArrayList<>(); // 统一路径格式,去除开头斜杠 if (path.startsWith("/")) { path = path.substring(1); } // 遍历类加载器下的所有匹配资源 Enumeration<URL> resources = ResourcesUtils.class.getClassLoader().getResources(path); while (resources.hasMoreElements()) { URL url = resources.nextElement(); if ("jar".equals(url.getProtocol())) { try (JarURLConnection connection = (JarURLConnection) url.openConnection()) { try (JarFile jarFile = connection.getJarFile()) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); // 匹配目标路径下的直接子文件(排除目录和子目录内的文件) if (entryName.startsWith(path) && !entry.isDirectory()) { String relativePath = entryName.substring(path.length()); if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } if (!relativePath.contains("/")) { filenames.add(relativePath); } } } } } } } return filenames; }
方案2:直接遍历APK文件(更稳定)
对于Android 8.0+(API 26),可通过Context获取APK路径,直接将APK作为ZIP文件遍历,不受类加载器限制:
public static List<String> getAllResourcesFromFolder(Context context, String path) throws IOException { List<String> filenames = new ArrayList<>(); // 统一路径格式,去除开头斜杠 if (path.startsWith("/")) { path = path.substring(1); } // 获取APK文件路径 String apkPath = context.getPackageCodePath(); try (ZipFile zipFile = new ZipFile(apkPath)) { Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String entryName = entry.getName(); // 匹配库中资源的完整路径(依赖库打包后资源位于BOOT-INF/classes/下) String targetPrefix = "BOOT-INF/classes/" + path; if (entryName.startsWith(targetPrefix) && !entry.isDirectory()) { String relativePath = entryName.substring(targetPrefix.length()); if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } if (!relativePath.contains("/")) { filenames.add(relativePath); } } } } return filenames; }
此方法需传入Context,可通过库内部获取Application上下文的方式实现(如静态持有),稳定性更高。
额外注意事项
- 资源打包路径:确保库中资源放在
src/main/resources/[目标路径]下,打包后路径与代码中传入的path一致。 - Gradle配置:Android库模块默认会将
src/main/resources下的文件打包进AAR的classes.jar中,无需额外配置。
内容的提问来源于stack exchange,提问作者Skepller
相关产品推荐
相关产品推荐

