最后
以前一直是自己在网上东平西凑的找,找到的东西也是零零散散,很多时候都是看着看着就没了,时间浪费了,问题却还没得到解决,很让人抓狂。
后面我就自己整理了一套资料,还别说,真香!
资料有条理,有系统,还很全面,我不方便直接放出来,大家可以先看看有没有用得到的地方吧。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1、java虚拟机与Dalvik虚拟机
Dalvik虚拟机是安卓早期版本的虚拟机,每一个应用程序对应一个单独的Dalvik虚拟机,这种设计的好处是当一个进程的虚拟机挂掉不会影响其他进程;java虚拟机中执行的是class文件,而Dalvik虚拟机执行的是dex文件;Java虚拟机是基于栈的虚拟机,而Dalvik虚拟机是基于寄存器的虚拟机;
基于栈的虚拟机:
每个线程运行时都会创建一个线程独享的栈空间,方法调用时会将代表方法的栈帧在此栈中入栈和出栈,并且栈帧中的操作都是在操作数栈中进行的;
基于寄存器的虚拟机:
基于寄存器的虚拟机与基于栈的虚拟机最大的不同之处就是操作不在操作数栈上进行了,而是在一个虚拟寄存器中运行,这些寄存器也存在栈中,本质就是数组,用来暂存指令、数据和地址;基于寄存器的虚拟机指令操作明显变少了,省去了很多移动操作,但其指令也相对复杂了;
2、ART与Dalvik
最明显的区别就是Dalvik支持JIT(just in time)即时编译,这个功能会将一些热点代码进行提前编译或者编译成本机机器码;Dalvik在安装应用的时候将dex提前编译odex文件;而ART支持AOT(ahead of time)提前编译,在安装应用时ART使用设备自带的dex2oat工具来编译应用,dex中的字节码将被编译成本地机器码,这种方式的缺点就是导致apk安装很慢,因为需要多执行一步编译成本地机器码的操作;
从android7开始取消了安装编译成机器码的操作,在执行过程中进行JIT操作,并将信息存入配置文件中,在设备闲置并且充电状态下,会将配置文件中记录的信息进行AOT编译,待下次运行时直接使用;
二、Android类加载器ClassLoader
classloader的作用简单来说就是加载class文件,提供给程序运行时使用,每个class文件内部都有一个ClassLoader字段用来标识自己是由哪个类加载器加载的;
Android中使用的主要类加载器继承结构如下所示:
ClassLoader 抽象类,类加载器的父类
BootClassLoader ClassLoader的内部类,继承自ClassLoader,主要用来加载AndroidFramework中的类;
BaseDexClassLoader
PathClassLoader Android应用程序类加载器,我们项目中用到的除了系统中的类一般用这个加载器进行加载,可以用来加载指定dex,以及jar、zip、apk中的dex文件;
DexCLassLoader 用来加载指定dex,以及jar、zip、apk中的dex文件,是一个额外提供的动态类加载器;
PathClassLoader和DexCLassLoader共同父类是BaseDexClassLoader,只不过创建DexClassLoader需要传递一个optimizedDirectory参数,这个参数用来确定odex的目录,不过这个参数在api26也已经被废弃了;创建PathClassLoader时这个参数传为null,表示使用默认的路径,为:/data/dalvik-cache;这样其实使用PathClassLoader和DexClassLoader就没什么区别了;
三、双亲委托机制与类加载流程分析
1、当我们加载一个类,会去调用CLassLoader中的loadClass方法:
protected Class<?> loadClass(String name, boolean resolve) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
}
if (c == null) {
c = findClass(name);
}
}
return c;
}
2、在loadClass方法中,首先会去检查是否已经加载过,如果加载过c不为空则直接返回;如果没有加载过先去调用parent的loadclass方法,这样就会递归调用父加载器(父加载器是在创建加载器的时候作为参数传进来的),如果parent为空,就会去调用BootClassLoader;如果在所有父加载器中都没有成功加载,才会调用自己的findclass方法自己加载;
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class c = pathList.findClass(name, suppressedExceptions);
return c;
}
3、ClassLoader类中的findClass是一个空实现,实际调用的是BaseDexClassLoader中的findClass方法,我们在方法中可以看实现非常简单,就是调用了DexPathList的findClass方法:
public Class<?> findClass(String name, List suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
return null;
}
4、在这个方法中,会去循环遍历dexElements,然后调用Element的findClass方法;那么这个dexElements是啥呢?在DexPathList构造方法中对其进行了赋值:
public DexPathList(ClassLoader definingContext, String dexPath,
String librarySearchPath, File optimizedDirectory) {
// save dexPath for BaseDexClassLoader
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
suppressedExceptions, definingContext);
}
5、splitDexPath --> splitPaths ,会返回一个List;
private static List splitPaths(String searchPath, boolean directoriesOnly) {
List result = new ArrayList<>();
if (searchPath != null) {
for (String path : searchPath.split(File.pathSeparator)) {
if (directoriesOnly) {
try {
StructStat sb = Libcore.os.stat(path);
if (!S_ISDIR(sb.st_mode)) {
continue;
}
} catch (ErrnoException ignored) {
continue;
}
}
result.add(new File(path));
}
}
return result;
}
6、makeDexElements方法会返回一个Element数组,可以看到在此方法中通过File构建DexFile,然后通过DexFile构建Element,最后将Elements数组返回:
private static Element[] makeDexElements(List files, File optimizedDirectory,
List suppressedExceptions, ClassLoader loader) {
Element[] elements = new Element[files.size()];
int elementsPos = 0;
/*
- Open all files and load the (direct or contained) dex files up front.
*/
for (File file : files) {
if (file.isDirectory()) {
// We support directories for looking up resources. Looking up resources in
// directories is useful for running libcore tests.
elements[elementsPos++] = new Element(file);
} else if (file.isFile()) {
String name = file.getName();
if (name.endsWith(DEX_SUFFIX)) {
// Raw dex file (not inside a zip/jar).
try {
DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
if (dex != null) {
elements[elementsPos++] = new Element(dex, null);
}
} catch (IOException suppressed) {
System.logE("Unable to load dex file: " + file, suppressed);
suppressedExceptions.add(suppressed);
}
} else {
DexFile dex = null;
try {
dex = loadDexFile(file, optimizedDirectory, loader, elements);
} catch (IOException suppressed) {
/*
-
IOException might get thrown “legitimately” by the DexFile constructor if
-
the zip file turns out to be resource-only (that is, no classes.dex file
-
in it).
-
Let dex == null and hang on to the exception to add to the tea-leaves for
-
when findClass returns null.
*/
suppressedExceptions.add(suppressed);
}
if (dex == null) {
elements[elementsPos++] = new Element(file);
} else {
elements[elementsPos++] = new Element(dex, file);
}
}
} else {
System.logW("ClassLoader referenced unknown path: " + file);
}
}
if (elementsPos != elements.length) {
elements = Arrays.copyOf(elements, elementsPos);
}
return elements;
}
7、现在我们知道了dexElements是啥了,我们回到步骤3,调用了Element的findClass
最后
给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
exElements是啥了,我们回到步骤3,调用了Element的findClass
最后
给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;
[外链图片转存中…(img-gnTGX6pm-1715445779470)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!