Android插件化学习之路(三)之调用外部.dex文件中的代码

Java程序中,JVM虚拟机是通过类加载器ClassLoader加载.jar文件里面的类的。Android也类似,不过Android用的是Dalvik/ART虚拟机,不是JVM,也不能直接加载.jar文件,而是加载dex文件。

先要通过Android SDK提供的DX工具把.jar文件优化成.dex文件,然后Android的虚拟机才能加载。注意,有的Android应用能直接加载.jar文件,那是因为这个.jar文件已经经过优化,只不过后缀名没改(其实已经是.dex文件)。

Freedom框架,我个人手写的0反射插件化框架

.jar文件优化成.dex文件

首先我们可以通过JDK的编译命令javac把Java代码编译成.class文件,再使用jar命令把.class文件封装成.jar文件,这与编译普通Java程序的时候完全一样。
之后再用Android SDK的DX工具把.jar文件优化成.dex文件(在“android-sdk\build-tools\具体版本\”路径下)

dx --dex --output=target.dex origin.jar // target.dex就是我们要的了

此外,我们可以现把代码编译成APK文件,再把APK里面的.dex文件解压出来,或者直接把APK文件当成.dex使用(只是APK里面的静态资源文件我们暂时还用不到)。至此我们发现,无论加载.jar,还是.apk,其实都和加载.dex是等价的,Android能加载.jar和.apk,是因为它们都包含有.dex,直接加载.apk文件时,ClassLoader也会自动把.apk里的.dex解压出来。

**

加载并调用.dex里面的方法

**
与JVM不同,Android的虚拟机不能用ClassCload直接加载.dex,而是要用DexClassLoader或者PathClassLoader,他们都是ClassLoader的子类,这两者的区别是

  1. DexClassLoader:可以加载jar/apk/dex,可以从SD卡中加载未安装的apk;
  2. PathClassLoader:要传入系统中apk的存放Path,所以只能加载已经安装的apk文件;
    使用前,先看看DexClassLoader的构造方法
 public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
    super((String) null, (File) null, (String) null, (ClassLoader) null);
    throw new RuntimeException("Stub!");
}


注意,我们之前提到的,DexClassLoader并不能直接加载外部存储的.dex文件,而是要先拷贝到内部存储里。这里的dexPath就是.dex的外部存储路径,而optimizedDirectory则是内部路径,libraryPath用null即可,parent则是要传入当前应用的ClassLoader,这与ClassLoader的“双亲代理模式”有关。

File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test_dexloader.jar");// 外部路径
File dexOutputDir = this.getDir("dex", 0);// 无法直接从外部路径加载.dex文件,需要指定APP内部路径作为缓存目录(.dex文件会被解压到此目录)
DexClassLoader dexClassLoader = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, getClassLoader());

到这里,我们已经成功把.dex文件给加载进来了,接下来就是如何调用.dex里面的代码,主要有两种方式。

如何调用.dex里面的代码

使用反射的方式

使用DexClassLoader加载进来的类,我们本地并没有这些类的源码,所以无法直接调用,不过可以通过反射的方法调用,简单粗暴。

DexClassLoader dexClassLoader = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, getClassLoader());
Class libProviderClazz = null;
try{
    libProviderClazz=dexClassLoader.loadClass("com.dexclassloader.MyLoader");
    // 遍历类里所有方法
    Method[]methods=libProviderClazz.getDeclaredMethods();
    for(int i=0;i<methods.length;i++){
    Log.e(TAG,methods[i].toString());
    }
    Method start=libProviderClazz.getDeclaredMethod("func");// 获取方法
    start.setAccessible(true);// 把方法设为public,让外部可以调用
    String string=(String)start.invoke(libProviderClazz.newInstance());// 调用方法并获取返回值
    Toast.makeText(this,string,Toast.LENGTH_LONG).show();
    }catch(Exception exception){
    // Handle exception gracefully here.
    exception.printStackTrace();
    }

使用接口的方式
毕竟.dex文件也是我们自己维护的,所以可以把方法抽象成公共接口,把这些接口也复制到主项目里面去,就可以通过这些接口调用动态加载得到的实例的方法了。

pulic interface IFunc{
    public String func();
}

// 调用
IFunc ifunc = (IFunc)libProviderClazz;
String string = ifunc.func();
Toast.makeText(this, string, Toast.LENGTH_LONG).show();

到这里,我们已经成功从外部路径动态加载一个.dex文件,并执行里面的代码逻辑了。通过从服务器下载最新的.dex文件并替换本地的旧文件,就能初步实现“APP的动态升级了”。

虽然我们已经能调用插件的方法了,但是还有如下问题

  1. 无法使用res目录下的资源,特别是使用XML布局,以及无法通过res资源到达自适应
  2. 无法动态加载新的Activity等组件,因为这些组件需要在Manifest中注册,动态加载无法更改当前APK的Manifest

这些问题的解决方案,将在以后的博客进行说明。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Android开发,Classes.dex是包含了Java字节码的文件,是应用程序的核心文件之一。如果这个文件被反编译,攻击者可以轻松地获取应用程序的核心代码和逻辑,从而进行恶意攻击。因此,加密和混淆Classes.dex文件是非常必要的。 以下是一些常用的加密和混淆Classes.dex文件的方法: 1. 使用ProGuard混淆器 ProGuard是一个开源的Java字节码混淆器,可以对Java字节码进行压缩、优和混淆。它可以删除未使用的代码、重命名类、方法和属性名,并且可以将代码分成多个不同的文件。使用ProGuard混淆器可以有效地保护应用程序的代码不被反编译。 2. 使用DexGuard混淆器 DexGuard是一个专门为Android应用程序设计的混淆器,可以对Java字节码进行更加复杂的混淆和加密。它可以使用高级的代码混淆和加密技术,如字符串加密、类加密、方法加密、代码搬运、代码膨胀等等。使用DexGuard混淆器可以更加有效地保护应用程序的代码不被反编译。 3. 使用加密算法加密Classes.dex文件 可以使用加密算法对Classes.dex文件进行加密,可以使用对称加密算法或非对称加密算法。对称加密算法包括AES、DES、RC4等等,非对称加密算法包括RSA、DSA等等。加密后的Classes.dex文件只有在运行时才能被解密,可以有效地保护应用程序的代码不被反编译。 总之,加密和混淆Classes.dex文件是非常必要的,可以有效地保护应用程序的核心代码和逻辑。使用ProGuard混淆器或DexGuard混淆器都可以实现代码混淆和加密,同时可以使用加密算法加密Classes.dex文件
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的代码家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值