在 插件化知识梳理(6) - Small 源码分析之 Hook 原理 这一章的学习完成以后,下一步咱们将进入插件化加载的精髓,动态加载类的学习,在此以前,咱们须要先准备一些关于类加载的知识。java
Android
当中,支持动态加载的两种方式为:DexClassLoader
和PathClassLoader
。这二者之间的区别为:bash
DexClassLoader
jar、apk、dex
SD
卡目录加载。PathClassLoader
Dalvik
虚拟机上,只能加载已经安装到系统中的Apk
文件,也就是/data/app
目录下的apk
文件。之因此有这个限制是由于PathClassLoader
会去读取data/dalvik-cache
目录下通过优化后的dex
文件,若是文件不存在,那么就会报错。因为手边没有机器,因此没有版本验证。ART
虚拟机上,经过源码当中的注释,能够发现是支持的。
实例的工程目录结构为: app
app
:宿主模块library
:插件模块libraryinterface
:插件接口模块其中,app
和library
模块分别依赖于libraryinterface
,library
和libraryinterface
为Android Library
类型的Module
,下面,咱们开始讲解整个工程的构建过程。ide
接口模块至关因而宿主模块和插件模块所定义的一套标准,宿主模块遵循固定的业务逻辑,而具体的实现则根据插件模块的不一样而不一样。 在接口模块中,咱们定义一个简单的接口IPlugin.java
:工具
public interface IPlugin {
public int getVersion();
}
复制代码
首先,咱们在插件模块的build.gradle
文件中,引入libraryinterface
模块源码分析
dependencies {
//引入接口模块
compile project (':libraryinterface')
}
复制代码
接着,咱们编写一个实现类:学习
public class PluginImpl implements IPlugin {
@Override
public int getVersion() {
return 1;
}
}
复制代码
接下来须要作的就是将该插件模块打包成一个jar
文件,一样是在build.gradle
文件中,建立一个Task
任务:gradle
task makeJar(type: Copy) {
delete 'build/libs/plugin.jar'
from ('build/intermediates/bundles/release/')
into ('../file/')
include ('classes.jar')
rename ('classes.jar','plugin.jar')
}
makeJar.dependsOn(build)
复制代码
首先点击make module
: 优化
./gradlew makeJar
复制代码
就会获得一个plugin.jar
文件,可是这个jar
文件是不可以被动态加载的,由于它内部实际上是.class
文件,咱们经过解压能够看出: ui
Android SDK
自带的
dx
工具进行转换,把它转换为
.dex
,转换后的文件为
plugin_dex.jar
/Users/lizejun/Library/Android/sdk/build-tools/25.0.3/dx --dex --output=file/plugin_dex.jar file/plugin.jar
复制代码
将plugin_dex.jar
解压以后,能够看到它已经被转换成了.dex
文件:
jar
包
push
到手机中的
/sdcard/Plugin
目录下:
首先,宿主模块一样须要依赖于接口模块libraryinterface
:
dependencies {
//引入接口模块
compile project (':libraryinterface')
}
复制代码
在代码当中,咱们经过DexClassLoader/PathClassLoader
动态外部的插件plugin_dex.jar
,经过反射实例化PluginImpl
类,并调用它的getVersion()
方法进行验证:
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv_plug_result);
getPluginA();
}
private void getPluginA() {
File dexOutputDir = getDir("dex1", 0);
String dexPath = Environment.getExternalStorageDirectory().toString() + "/Plugin/plugin_dex.jar";
DexClassLoader loader = new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, getClassLoader());
try {
Class clz = loader.loadClass("com.demo.lizejun.library.PluginImpl");
IPlugin impl = (IPlugin) clz.newInstance();
int version = impl.getVersion();
mTextView.setText("Version=" + version);
} catch (Exception e) {
e.printStackTrace();
}
}
private void getPluginB() {
String dexPath = Environment.getExternalStorageDirectory().toString() + "/Plugin/plugin_dex.jar";
PathClassLoader loader = new PathClassLoader(dexPath, getClassLoader());
try {
Class clz = loader.loadClass("com.demo.lizejun.library.PluginImpl");
IPlugin impl = (IPlugin) clz.newInstance();
int version = impl.getVersion();
mTextView.setText("Version=" + version);
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
最终的结果为: