Android扫描dex中的接口实现类,class, classloder, dex 详解

Android中的ClassLoader整体概述

Android中的ClassLoader的整体架构继承关系如下:

283d3f97bbcc458dfd3890c2d06d9f5e.png

其中,ClassLoader分别为:

BootClassLoader:与Java中的Bootstrap ClassLoader类似,主要加载Android Framework中的字节码文件。

PathClassLoader:与Java中的App ClassLoader类似,主要加载已经安装到系统中的APK中的字节码文件。

DexClassLoader:与Java中的Customer ClassLoader类似,主要加载自定义路径下的APK或者JAR中的字节码文件(Android中主要是指dex文件,即classes.dex)。

其中,BaseDexClassLoader是PathClassLoader以及DexClassLoader的父类,PathClassLoader以及DexClassLoader的逻辑都在这个父类中实现。

BootClassLoader和PathClassLoader是一个普通的APP所需要的(定制ROM另外说),最基本的ClassLoader,通过下面的代码可以证明:

@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ClassLoader cl= this.getClassLoader();

Log.e(TAG,"onCreate: " +cl);while (cl.getParent() != null) {

cl=cl.getParent();

Log.e(TAG,"onCreate: " +cl);

}

}

打印结果只有BootClassLoader和PathClassLoader:

09-06 14:29:01.571 3165-3165/com.nan.loadapkdemo E/MainActivity: onCreate: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.nan.loadapkdemo-1/base.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_dependencies_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_0_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_1_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_2_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_3_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_4_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_5_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_6_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_7_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_8_apk.apk", zip file "/data/app/com.nan.loadapkdemo-1/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.nan.loadapkdemo-1/lib/x86, /system/lib, /vendor/lib]]]09-06 14:29:01.571 3165-3165/com.nan.loadapkdemo E/MainActivity: onCreate: java.lang.BootClassLoader@b173b34

上面就分析完了classes.dex文件是如何通过ClassLoader的初始化装载进来的,下面继续分析一个类是如何通过PathClassLoader或者DexClassLoader进行加载的,这时候就需要看父类BaseDexClassLoader的findClass方法了:

@Overrideprotected Class> findClass(String name) throwsClassNotFoundException {

List suppressedExceptions = new ArrayList();

Class c=pathList.findClass(name, suppressedExceptions);if (c == null) {

ClassNotFoundException cnfe= new ClassNotFoundException("Didn't find class "" + name + "" on path: " +pathList);for(Throwable t : suppressedExceptions) {

cnfe.addSuppressed(t);

}throwcnfe;

}returnc;

}

这里面看到,实际上就是调用了PathList的findClass方法:

public Class findClass(String name, Listsuppressed) {for(Element element : dexElements) {

DexFile dex=element.dexFile;if (dex != null) {

Class clazz=dex.loadClassBinaryName(name, definingContext, suppressed);if (clazz != null) {returnclazz;

}

}

}if (dexElementsSuppressedExceptions != null) {

suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));

}return null;

}

这个方法就是遍历初始化创建好的内部Element[]数组里面的DexFile对象,最终是通过DexFile的loadClassBinaryName进行加载的:

public Class loadClassBinaryName(String name, ClassLoader loader, Listsuppressed) {return defineClass(name, loader, mCookie, this, suppressed);

}

defineClass的实现如下:

private staticClass defineClass(String name, ClassLoader loader, Object cookie,

DexFile dexFile, Listsuppressed) {

Class result= null;try{

result=defineClassNative(name, loader, cookie, dexFile);

}catch(NoClassDefFoundError e) {if (suppressed != null) {

suppressed.add(e);

}

}catch(ClassNotFoundException e) {if (suppressed != null) {

suppressed.add(e);

}

}returnresult;

}

到最后,defineClassNative是一个native方法:

private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile)

defineClassNative 是通过C/C++实现,这个方法去dex文件中查找指定name的类,并且拼接成Class字节码,返回给Java层。通过native实现是为了提高效率。

整个加载流程一气呵成,如下图所示:

eccd43a44ee9668ddc7330653a6f8346.png

实现动态加载

如下面所示,演示了最简单的动态加载的方案:

public class MainActivity extendsAppCompatActivity {

@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

String apkPath= getExternalCacheDir().getAbsolutePath() + "/bundle.apk";

loadApk(apkPath);

}private voidloadApk(String apkPath) {

File optFile= getDir("opt", MODE_PRIVATE);//通过DexClassLoader加载制定的APK文件

DexClassLoader dexClassLoader = new DexClassLoader(apkPath, optFile.getAbsolutePath(), null, getClassLoader());try{//通过反射去使用对象

Class clz = dexClassLoader.loadClass("com.loubinfeng.www.boundle.Printer");if (clz != null) {

Object instance=clz.newInstance();

Method method= clz.getMethod("print");

method.invoke(instance);

}

}catch(Exception e) {

e.printStackTrace();

}

}

}

实现动态加载的难点

实现动态加载难点比较多:

资源的访问问题

四大组件的生命周期管理问题

插件ClassLoader的管理

so库的动态加载等等

参考文章:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值