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

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

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

.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

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2023最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 是指将一个应用的功能分离为多个独立的模块,每个模块可以在运行时独立加载和卸载。这样可以优应用的体积和性能,同时也可以实现模块的动态更新和管理。在某些场景下被广泛运用,比如一个大型的应用需要提供不同的功能模块供用户选择,或者在多个应用共享某些通用功能。的实现方式有很多种,其最常用的是利用 Android框架和技术来实现。 通常情况下,一个 Android 应用的所有组件(Activity、Service、BroadcastReceiver、ContentProvider 等)都被编译打包进同一个 APK 文件,这也就意味着只有在应用安装和更新的时候才能进行组件的更新和修改,无法实现动态更新。而技术则打破了这种限制,它将应用的不同组件打包成不同的件(APK)文件,然后在运行时动态加载和卸载。这样一来,我们可以实现在不停止应用的情况下对部分组件进行更新、拓展、甚至是删除。 为了实现,我们需要解决一些技术难点。其最主要的是解决件和宿主的交互问题。在件和宿主间需要进行很多数据传递、资源访问和类加载等操作,如果没有基础的交互机制,是无法成功实现的。因此,在 Android 相关的机制主要包括四个方面:类加载器(ClassLoader)、组件的注册和管理、资源访问和切换主题(Theme)等。 总体来看,技术为 Android 应用带来了更大的可拓展性和灵活性,这对于一些复杂或大型的应用非常重要。同时,基于技术,也催生出了一些全新的业态,比如件市场和件开发社区,为 Android 生态系统带来了更多的创新力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值