class, classloder, dex 详解
class与dex文件什么是class文件class文件是一种能够被JVM识别,加载并且执行的文件格式。 ? class文件的作用class文件的作用是记录一个类文件的所有信息。 例如记住了当前类的引用this、父类super等等。class文件记录的信息往往比java文件多。 ? class文件的结构
? class文件的弊端
? 什么是dex文件能够被DVM或者Art虚拟机执行并且加载的文件格式。 ? dex文件的作用dex文件的作用是记录整个工程(通常是一个Android工程)的所有类文件的信息。 ? dex文件的结构
? class文件与dex文件的比较
结构对比图如下: ?
![]() ? 类加载机制是做热修复以及插件化的很重要的理论基础。 ? Java中的ClassLoader回顾如下图所示: ?
![]() ClassLoader的特性ClassLoader的主要特性是双亲委托机制,即加载一个类的时候,先判断已经存在的类是否被加载过,如果没有,先去委托父亲去加载。如果父亲都没有加载成功的话,那么最终由自己加载。最终这个类最终没有合适的CLassLoader加载,那么就会抛出异常,相关的ClassLoader源码如下: protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException { // 先判断这个类是否已经被加载过 Class c = findLoadedClass(name); if (c == null) { 如果没有被加载过,那么委托父亲去加载 long t0 = System.nanoTime(); try { if (parent != ) { c = parent.loadClass(name,1)">false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { ClassNotFoundException thrown if class not found from the non-null parent class loader } 如果父亲没有加载,那么最终由自己(实现类)负责加载 ) { If still not found,then invoke findClass in order to find the class. long t1 = System.nanoTime(); c = findClass(name); this is the defining class loader; record the stats } } return c; } 其中CLassLoader的findClass方法是空实现,需要自己继承然后实现: protected Class<?> findClass(String name) ClassNotFoundException { throw new ClassNotFoundException(name); } ? 这种双亲委托机制有两种好处:
? Android中的ClassLoader整体概述Android中的ClassLoader的整体架构继承关系如下: ?
![]() 其中,ClassLoader分别为:
其中,BaseDexClassLoader是PathClassLoader以及DexClassLoader的父类,PathClassLoader以及DexClassLoader的逻辑都在这个父类中实现。 BootClassLoader和PathClassLoader是一个普通的APP所需要的(定制ROM另外说),最基本的ClassLoader,通过下面的代码可以证明: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ClassLoader cl = this.getClassLoader(); Log.e(TAG,"onCreate: " + cl); while (cl.getParent() != ) { cl = cl.getParent(); Log.e(TAG,1)"> 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方法了: ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); Class c = pathList.findClass(name,suppressedExceptions); ) { ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class "" + name + "" on path: " + pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } throw cnfe; } c; } 这里面看到,实际上就是调用了PathList的findClass方法: public Class findClass(String name,List<Throwable> suppressed) { (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != ) { Class clazz = dex.loadClassBinaryName(name,definingContext,suppressed); if (clazz != clazz; } } } if (dexElementsSuppressedExceptions != ) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return ; } 这个方法就是遍历初始化创建好的内部Element[]数组里面的DexFile对象,最终是通过DexFile的loadClassBinaryName进行加载的: public Class loadClassBinaryName(String name,ClassLoader loader,1)">return defineClass(name,loader,mCookie,1)">,suppressed);
}
defineClass的实现如下: private static Class defineClass(String name,Object cookie,DexFile dexFile,List<Throwable> suppressed) { Class result = ; { result = defineClassNative(name,cookie,dexFile); } (NoClassDefFoundError e) { if (suppressed != ) { suppressed.add(e); } } (ClassNotFoundException e) { ) { suppressed.add(e); } } result; } 到最后,defineClassNative是一个native方法: static native Class defineClassNative(String name,DexFile dexFile) defineClassNative 是通过C/C++实现,这个方法去dex文件中查找指定name的类,并且拼接成Class字节码,返回给Java层。通过native实现是为了提高效率。 整个加载流程一气呵成,如下图所示: ![]() 实现动态加载如下面所示,演示了最简单的动态加载的方案: public class MainActivity extends AppCompatActivity { @Override onCreate(Bundle savedInstanceState) { .onCreate(savedInstanceState); setContentView(R.layout.activity_main); String apkPath = getExternalCacheDir().getAbsolutePath() + "/bundle.apk"; loadApk(apkPath); } loadApk(String apkPath) { File optFile = getDir("opt" 通过DexClassLoader加载制定的APK文件 DexClassLoader dexClassLoader = new DexClassLoader(apkPath,optFile.getAbsolutePath(),getClassLoader()); { 通过反射去使用对象 Class clz = dexClassLoader.loadClass("com.loubinfeng.www.boundle.Printer"); if (clz != ) { Object instance = clz.newInstance(); Method method = clz.getMethod("print"); method.invoke(instance); } } (Exception e) { e.printStackTrace(); } } } ?实现动态加载的难点实现动态加载难点比较多:
参考文章: http://www.jianshu.com/p/37cad7a901b1 http://www.jianshu.com/p/2eb518941681 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |