java文件在编译后会生成一个class文件,而在Android中会将代码编译后生成多个dex文件,在通过zip打包成一个apk,然而通过对apk的解压会发现其中有一个或者多个 xxxclass.dex文件。
类加载的基本
- BootClassLoader:加载SDK 例如FrameWork的层,如Activity.class
- PathClassLoader: 应用层的类型 例如业务MainActivty类 & 比如熟悉第三方包框架okHttp也是,加载系统中已经安装过的apk。
- DexClassLocader:属于定制开发使用,自定义更加方便适配,可以加载jar/apk/dex,可以从SD卡中加载未安装的apk。
PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
/**
* @param dexPath:dex路径 其中Apk只能加载系统中已经安装过的apk
* @param libraryPath:动态库路径
* @param parent:制定父类加载器,以保证双亲委派机制从而实现每个类只加载一次。
*/
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
DexClassLocader.java
public class DexClassLoader extends BaseDexClassLoader {
/**
* @param dexPath: dex路径 [jar/apk/dex,可以从SD卡中加载未安装的apk ]
* @param optimizedDirectory: 制定输出的dex优化后odex文件,可以为null
* @param librarySearchPath: 动态的库路劲,添加到App动态库搜素列表中
* @param parent: 制定父类加载器,以保证双亲委派机制从而实现每个类只加载一次。
*/
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
双亲委派机制
双亲委派机制当某个类需要加载器加载时,首先会检测当前类是否加载过findLoadedClass(),如果已经加载了那么直接获取并且返回。
如果没有加载就会委托给它的上级 ,当parent不为null,则parent.loadClass()进行加载,依次递归
如果找到了或者是已经加载了就返回,如果没有找到也没加载那么才自己去加载。而这个整个流程就属于双亲委派机制。
源码分析
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
//检查类是否已经加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//递归loadClass
c = parent.loadClass(name, false);
} else {
//如果父类的加载器为空 则说明递归到bootStrapClassloader了
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
//如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
c = findClass(name);
}
}
return c;
}
通过对双亲委派机制的分析,可以得其作用:
- 防止.class被多个加载器所加载,会用到递归询问的方式查询是否加载。
- 防止.class不被篡改,而从保证.class的执行安全。
dex文件的生成指定
package com.example.myapplication;
import android.util.Log;
public class Test {
public static void logStart() {
Log.e("Test", "启动logStart");
}
}
一个class文件要生成xxx.dex,需要借助dx.bat工具,在Android-sdk下:wind路径 Android/sdk/build-tools/api版本/dx.bat 【记得环境变量】
-
可以用Gradle-build或者是Javac编译成class文件
-
F:\Android\app\build\intermediates\javac\debug\classes\com\example\myapplication\Test.class
-
dx --dex --output=xxx.dex com/example/myapplication/Test.class
命令译:dx --dex --output=xxx(生成dex名).dex input.class(目标文件、包含完整包名) 【dx时候cmd要进入到当期com/包名的初始目录】
控制台调用dex中的类
把生成后的test.dex文件放入Sd根目录,通过DexClassLocader或者是PathClassLoader进行类加载:
public void onButton(View view) {
try {
DexClassLoader dexClassLoader = new DexClassLoader("/sdcard/test.dex",
MainActivity.this.getCacheDir().getAbsolutePath(),
null,
MainActivity.this.getClassLoader());
Class<?> clazz = dexClassLoader.loadClass("com.example.myapplication.Test");
Method print = clazz.getMethod("logStart");
print.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
}
网友评论