Dalvik虚拟机类加载机制
DexClassLoader和PathClassLoader这两个类都继承自ClassLoader的类加载器,本质上是重载了ClassLoader的findClass方法。
它们在加载一个类之前,会去检查自己以及自己以上的类加载器是否已经加载过这个类,如果已经加载过,就会直接将之返回,而不会重复加载。
DexClassLoader和PathClassLoader其实都是通过DexFile这个类来实现类加载的。
Dalvik虚拟机识别的是dex文件,而不是class文件,因此,我们供类加载的文件也只能是dex文件或者包含dex文件的.apk、.jar或.zip文件。
PathClassLoader是通过构造函数new DexFile(path)来产生DexFile对象的;而DexClassLoader是通过其静态方法loadDex(path, outpath, 0)得到DexFile对象。
这两者的区别在于DexClassLoader需要提供一个可写的outpath路径,用来存放.apk包或者.jar包中的dex文件。
换个说法来说,就是PathClassLoader不能主动从zip包中解析出dex,因此只支持直接操作dex格式文件,或者已经安装的apk(因为已经安装的apk在cache中存在缓存的dex文件)。而DexClassLoader可以支持.apk、.jar和.dex文件,并且会在指定的outpath路径存放解析出的dex文件。
PathClassLoader在加载类时调用的是DexFile的loadClassBinaryName方法,而DexClassLoader调用的是loadClass方法。
例子结构如上图,DynamicTest先编译个apk放到根目录下,再作为一个lib库文件被DynamicDemo引用。
public class ClassLister {
private String mDexPath;
public ClassLister(String apkPath) {
mDexPath = apkPath;
}
public List<Class<?>> getAllClasses(String packageName, String suffix) {
List<String> classNames = getClassNames(packageName, suffix);
ArrayList<Class<?>> allClass = new ArrayList<Class<?>>();
for (String name : classNames) {
try {
allClass.add(Class.forName(name));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return allClass;
}
/**
* 获取所有以包名开头,以默认字符串为后缀的类
*
* @param packageName
* @param suffix
* @return
*/
private List<String> getClassNames(String packageName, String suffix) {
ArrayList<String> classNames = new ArrayList<String>();
DexFile dexFile = null;
try {
dexFile = new DexFile(new File(mDexPath));
Enumeration<String> entries = dexFile.entries();
while (entries.hasMoreElements()) {
String className = entries.nextElement();
if (className.endsWith(suffix)
&& className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dexFile != null) {
try {
dexFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return classNames;
}
/**
* 使用PathClassLoader方法加载类
* @param context
* @param className
* @return
*/
public static Class<?> getClassByPathClassLoader(Context context,
String className) {
Class<?> loadClass = null;
String dexPath = context.getApplicationInfo().sourceDir;
String libPath = context.getApplicationInfo().nativeLibraryDir;
// 第一个参数:apk安装的路径,只能通过appInfo.sourceDir获取
// 第二个参数:C/C++依赖的本地库文件路径,可以为null
// 第三个参数:上一级类加载器
PathClassLoader pathClassLoader = new PathClassLoader(dexPath, libPath,
context.getClassLoader());
try {
loadClass = pathClassLoader.loadClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return loadClass;
}
/**
* 使用DexClassLoader方式加载类
* @param context
* @param className
* @return
*/
public static Class<?> getClassByDexClassLoader(Context context, String className) {
Class<?> loadClass = null;
String dexPath = Environment.getExternalStorageDirectory().toString()
+ File.separator + "DynamicTest.apk";
String dexOutputDirs = context.getApplicationInfo().dataDir;
// 第一个参数:dex压缩文件的路径
// 第二个参数:dex解压缩存放的路径
// 第三个参数:C/C++依赖的本地库文件路径,可以为null
// 第四个参数:上一级类加载器
DexClassLoader dexClassLoader = new DexClassLoader(dexPath,
dexOutputDirs, null, context.getClassLoader());
try {
loadClass = dexClassLoader.loadClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return loadClass;
}
/**
* 获取包中实现了某个接口的所有非抽象类
* @param packageName
* @param interfaceName
* @param suffix
* @return
*/
public List<Class<?>> listConcreteClasses(String packageName,
String interfaceName, String suffix) {
ArrayList<Class<?>> impClasses = new ArrayList<Class<?>>();
try {
List<Class<?>> allClasses = getAllClasses(packageName, suffix);
Class<?> interfaceClass = Class.forName(interfaceName);
for (Class<?> c : allClasses) {
if (isConcreteClassOf(c, interfaceClass)) {
impClasses.add(c);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return impClasses;
}
/**
* 判断sub是否是base的子类,并且sub不是抽象类
* @param sub
* @param base
* @return
*/
private boolean isConcreteClassOf(Class<?> sub, Class<?> base) {
return base.isAssignableFrom(sub) && isNotAbstract(sub);
}
private boolean isNotAbstract(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.ABSTRACT) == 0;
}
}
public class DemoModule {
}
public class DemoTestModule {
}
public interface ILoad {
}
public class DemoLoadImp implements ILoad {
}
public class LoadDemoTestImp implements ILoad {
}
DynamicDemo的MainActivity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClassLister classLister = new ClassLister(getApplicationInfo().sourceDir);
List<Class<?>> allClasses = classLister.getAllClasses("com.example", "Module");
for (Class<?> c : allClasses) {
System.out.println("class:" + c.toString());
}
List<Class<?>> listConcreteClasses = classLister.listConcreteClasses("com.example", "com.example.dynamictest.ILoad", "Imp");
for (Class<?> c : listConcreteClasses) {
System.out.println("imp:" + c.toString());
}
Class<?> classByPathClassLoader = ClassLister.getClassByPathClassLoader(this, "com.example.dynamictest.MainActivity");
if (classByPathClassLoader != null) {
System.out.println("pathClass:" + classByPathClassLoader);
}
Class<?> classByDexClassLoader = ClassLister.getClassByDexClassLoader(this, "com.example.dynamictest.MainActivity");
if (classByDexClassLoader != null) {
System.out.println("dexClass:" + classByDexClassLoader.toString());
}
}
}
输出结果:
class:class com.example.dynamicdemo.DemoModule
class:class com.example.dynamictest.DemoTestModule
imp:class com.example.dynamicdemo.DemoLoadImp
imp:class com.example.dynamictest.LoadDemoTestImp
pathClass:class com.example.dynamictest.MainActivity
dexClass:class com.example.dynamictest.MainActivity