概括:
讲解插件化之前讲解下java反射的相关知识,为此我查阅了《组件化开发框架》这本书,他对java反射做了详细的讲解。为此抛砖引玉回顾下java反射的基础知识。
反射机制的关键使用类
java.lang.class----类的创建
java.lang.reflect.Constructor ---反射类中构造方法
java.lang.reflect.Field -- 反射属性
java.lang.reflect.Method ---反射方法
java.lang.reflect.Modifier ---访问修饰符的信息
主要的反射机制的类就是这五个类,如何使用呢?
实战:
首先我们创建一个utils类,如下
```
public class Utils {
public void shot(){
System.out.println("i`m a shot");
}
}
```
我们使用反射该怎么调用shot方法并打印里面的消息呢?
```
try {
Class utilsClazz = Class.forName("com.zhaofan.hotfix.utils.Utils");
Object utils = utilsClazz.newInstance();
Method method = utilsClazz.getDeclaredMethod("shot");
method.invoke(utils);
}catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}catch (InvocationTargetException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
```
如果我们把方法改成私有的 ,public 改成private我们还能访问到方法内的内容吗?我们可以先试下
结果是不能访问的,并抛异常如下
java.lang.IllegalAccessException: Class java.lang.Class<com.zhaofan.hotfix.MainActivity> cannot access private method void com.zhaofan.hotfix.utils.Utils.shot()
翻译过来就是我们不能访问私有的方法
那么我们怎么能访问到这个方法呢 ,可以通过方法method.setAccessible(true)
我们修改一下代码
```
try {
Class utilsClazz = Class.forName("com.zhaofan.hotfix.utils.Utils");
Object utils = utilsClazz.newInstance();
Method method = utilsClazz.getDeclaredMethod("shot");
method.setAccessible(true);
method.invoke(utils);
}catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}catch (InvocationTargetException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
```
打印结果:com.zhaofan.hotfix I/System.out: i`m a shot
这个是在同一个目录结构下可以这样使用反射来获取到私有方法或者不能够访问的类使用的
如果是不同目录结构的我们又该怎么如何实现反射来获取到我们想要的呢?
其实也很简单,首先在你的使用反射的模块下依赖使用需要反射的类的模块,也就是在gradle下引用
implementation project(':hotfix_plugin')
java文件转换成class文件通过classLoader加载,通过JVM将这些class文件转换成字节码,字节码翻译(interpert)成机器码
DexClassLoader
讲解DexClassLoader之前讲解下Dex,dex就是包含了项目中所有的类,也就是android中的所有的Class文件包装在dex包下
dex也包含普通的dex和odex
dex:普通的dex
odex:optimized dex
dex:classes.dex
odex:odex/classes.dex
然后我们结合okio来模拟实现,我们是如何使用插件来完成集成的,
在讲解之前,我们来看下我们的apk结构,我们会看到我们所有的Class文件都是在classes.dex包文件下,也就是说我们所有的class文件都是在dex文件中的。首先看下代码如下
```
File file =new File(getCacheDir()+"/plugin_app-debug.apk");```
try (Source source = Okio.source(getAssets().open("apk/plugin_apk-debug.apk"));
BufferedSink sink = Okio.buffer(Okio.sink(file))){
sink.writeAll(source);
}catch (IOException e) {
e.printStackTrace();
}
DexClassLoader dexClassLoader =new DexClassLoader(file.getPath(),getCacheDir().getPath(),null,null);
Class debugClazz =null;
try {
debugClazz = dexClassLoader.loadClass("com.zhaofan.plugin_apk.utils.Utils");
Constructor constructor = debugClazz.getDeclaredConstructors()[0];
Object utils = constructor.newInstance();
Method method = debugClazz.getDeclaredMethod("shout");
method.invoke(utils);
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (InvocationTargetException e) {
e.printStackTrace();
}
把文件放在asset下,通过okio把文件写入到file文件中,通过DexClassLoader加载
file文件,然后通过loadClass方法加载Class类,通过反射,我们就能够找到我们想要访问到的方法。这样我们就完成了一个简单的插件化开发的示例,但在项目中使用的就相对来说就比较复杂的多。原理都大致相同,通过简单的示例完成对android插件化的讲解是不是很“耐撕”。
代码附上:https://github.com/ptrtony/EasyPlugin.git
网友评论