Android动态加载Java生成的JAR文件实现方法问询
Android动态加载Dex文件中Java类的实现方案
刚好做过类似的需求,给你一套完整的实现方案,完全能满足你想要的调用方式:
1. 前置准备确认
你已经用dx --dex --output=dynamic.dex your.jar把Jar转成了Dex文件,接下来要把这个dynamic.dex放到应用可访问的路径,推荐放在assets目录(打包进APK),或者外部存储(需要对应权限)。
2. 核心实现思路
因为编译时主应用没有动态类的引用,直接new DynamicLoadedClassInJar()会报错,所以我们用接口约定的方式来实现强类型调用,和你想要的写法几乎一致。
步骤1:定义公共接口
在主应用中定义一个接口,同时把这个接口复制一份到你的动态Jar项目中,让动态类实现这个接口:
// 主应用和动态Jar都要包含这个接口,包名、类名、方法必须完全一致 public interface DynamicInterface { void executeBusiness(); String getDynamicData(); }
步骤2:动态Jar中的类实现接口
你的动态Jar里的目标类要实现上面的接口:
// 动态Jar中的类 public class DynamicLoadedClassInJar implements DynamicInterface { @Override public void executeBusiness() { // 这里写你的动态类业务逻辑 Log.d("DynamicClass", "动态类方法执行了"); } @Override public String getDynamicData() { return "我是来自动态加载类的返回值"; } }
步骤3:主应用中加载Dex并实例化类
写一个加载工具类,用DexClassLoader来加载Dex文件,然后实例化类并强转成接口类型:
public class DexClassLoaderHelper { public static DynamicInterface loadDynamicClass(Context context) { try { // 1. 把assets中的dex文件复制到内部存储(因为DexClassLoader不能直接加载assets里的文件) File dexFile = new File(context.getFilesDir(), "dynamic.dex"); if (!dexFile.exists()) { copyAssetToFile(context, "dynamic.dex", dexFile); } // 2. 创建DexClassLoader:参数分别是dex路径、优化dex的存储目录、native库路径、父类加载器 File optimizedDexDir = context.getDir("dex_optimize", Context.MODE_PRIVATE); DexClassLoader dexLoader = new DexClassLoader( dexFile.getAbsolutePath(), optimizedDexDir.getAbsolutePath(), null, // 没有native库就传null context.getClassLoader() ); // 3. 加载目标类并实例化 Class<?> dynamicClass = dexLoader.loadClass("com.your.dynamic.package.DynamicLoadedClassInJar"); // 强转成接口类型,这样就能像普通类一样调用方法了 return (DynamicInterface) dynamicClass.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } } // 辅助方法:把assets文件复制到内部存储 private static void copyAssetToFile(Context context, String assetName, File destFile) throws IOException { InputStream is = context.getAssets().open(assetName); FileOutputStream fos = new FileOutputStream(destFile); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) != -1) { fos.write(buffer, 0, length); } is.close(); fos.close(); } }
步骤4:调用动态类
现在你就能像你期望的那样调用动态类了:
// 在Activity或者其他业务代码中使用 DynamicInterface dynamicObj = DexClassLoaderHelper.loadDynamicClass(this); if (dynamicObj != null) { // 调用动态类的方法 dynamicObj.executeBusiness(); String data = dynamicObj.getDynamicData(); Log.d("MainApp", "动态类返回数据:" + data); }
3. 重要注意事项
- 接口一致性:主应用和动态Jar中的接口必须完全一致(包名、类名、方法签名、返回值都不能变),否则强转时会抛出
ClassCastException。 - 权限问题:如果从外部存储加载Dex,Android 10及以下需要申请
READ_EXTERNAL_STORAGE权限,Android 10+建议使用Scoped Storage。 - 混淆配置:主应用中的
DynamicInterface不能被混淆,要在proguard-rules.pro中添加:-keep interface com.your.main.package.DynamicInterface { *; } - Dex生成验证:确保用dx命令生成的Dex没有错误,可以用
dexdump工具查看Dex中的类是否存在。
内容的提问来源于stack exchange,提问作者Andrean
相关产品推荐
相关产品推荐

