基本类预加载 从linux系统启动到进入android世界之前, 简要流程就是
Bootloader 加载内核,初始化 CPU、内存,挂载文件系统
Linux 内核 启动,初始化设备驱动、挂载 rootfs
,启动 init
进程
init
进程 解析 /init.rc
,启动关键服务(如 ueventd
、vold
),然后启动 Zygote
Zygote 进程 运行 ZygoteInit.main()
,预加载 Java 类,并 fork()
启动 SystemServer
Zygote 是 Android 进程的孵化器 ,几乎所有的应用进程都是由它 fork 出来的。
zygote在native时做的工作(/system/bin/app_process)
(1)创建虚拟机–startVM (2)注册JNI函数–startReg (3)通过JNI获得Java层的com.android.internal.os.ZygoteInit 类,调用main 函数,进入java 世界
zygote在java时做的工作
(4)registerZygoteSocket()建立socket通道,zygote作为通信的服务端,用于响应客户端请求 (5)preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率 (6)通过startSystemServer(),fork得力帮手system_server进程,也是Java Framework的运行载体(下面讲到system server再详细讲解) (7)调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作
preload加载基本类部分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 static void preload (TimingsTraceLog bootTimingsTraceLog) { preloadClasses(); } private static void preloadClasses () { final VMRuntime runtime = VMRuntime.getRuntime(); InputStream is; try { is = new FileInputStream (PRELOADED_CLASSES); } catch (FileNotFoundException e) { Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + "." ); return ; } try { BufferedReader br = new BufferedReader (new InputStreamReader (is), Zygote.SOCKET_BUFFER_SIZE); int count = 0 ; String line; while ((line = br.readLine()) != null ) { line = line.trim(); if (line.startsWith("#" ) || line.equals("" )) { continue ; } Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line); try { Class.forName(line, true , null ); count++; } catch (Throwable t) { } } } catch (IOException e) { Log.e(TAG, "Error reading " + PRELOADED_CLASSES + "." , e); } finally { } }
对于预加载, 在dalvik/art启动时将所有Java基本类和Android系统框架的基本类加载进来
这些类只需要在Zygote进程启动时加载一遍,后续APP或Android运行时环境的进程,都是从Zygote中fork出来,自然会继承zygote加载过的类
双亲委派 Android中的类加载器机制与JVM一样遵循双亲委派模式
所谓双亲委派就是指
加载.class时以递归的方式逐级向上委托给父加载器ParentClassLoader, 父加载器首先判断是否加载过该class
加载过则直接向下返回
未加载过则继续向上提交
直到提交到顶层父加载器, 如果顶层父加载器也没有加载过, 则尝试加载, 如果加载失败则逐级向下交还调用者加载
代码如下/libcore/ojluni/src/main/java/java/lang/ClassLoader.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> c = findLoadedClass(name); if (c == null ) { try { if (parent != null ) { c = parent.loadClass(name, false ); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { } if (c == null ) { c = findClass(name); } } return c; }
findLoadedClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected final Class<?> findLoadedClass(String name) { ClassLoader loader; if (this == BootClassLoader.getInstance()) loader = null ; else loader = this ; return VMClassLoader.findLoadedClass(loader, name); }
VMClassLoader.findLoadedClass(loader, name)
是一个 native 方法 ,直接与 JVM 交互,用于查询 name
这个类是否已经被 loader
加载过。如果已经加载,返回 Class<?>
对象
如果当前 ClassLoader
是 BootClassLoader
,则将 loader
设为 null
,表示请求 JVM 内部查找是否加载过该类, 否则在对应的loader内寻找
如果当前没有找到该类, 则提交给父加载器递归调用LoadClass
, 如果顶层父加载器也没找到就调用findClass
进行加载类
android类加载器 对于android主要关注三个类加载器
系统类的加载器 (BootClassLoader)
当前应用的类加载器 (PathClassLoader)
动态加载一个外部 dex
文件 (DexClassLoader)
BootClassLoader是ClassLoader的子类, PathClassLoader 和 DexClassLoader是BaseDexClassLoader的子类, 而BaseDexClassLoader又是ClassLoader的子类
三者的父加载器关系是DexClassLoader → PathClassLoader → BootClassLoader -> Null
而BootClassLoader是全局唯一的
PathClassLoader实例则是每个APP都拥有一个
demo
一个测试程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.example.classloaderdemo;import android.os.Bundle;import android.util.Log;import androidx.appcompat.app.AppCompatActivity;import dalvik.system.DexClassLoader;import java.io.File;public class MainActivity extends AppCompatActivity { private static final String TAG = "ClassLoaderDemo" ; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ClassLoader pathClassLoader = getClassLoader(); Log.d(TAG, "PathClassLoader: " + pathClassLoader); ClassLoader bootClassLoader = pathClassLoader.getParent(); Log.d(TAG, "BootClassLoader: " + bootClassLoader); String dexPath = "/sdcard/external.dex" ; File optimizedDir = getDir("dex" , MODE_PRIVATE); DexClassLoader dexClassLoader = new DexClassLoader (dexPath, optimizedDir.getAbsolutePath(), null , pathClassLoader); Log.d(TAG, "DexClassLoader: " + dexClassLoader); printClassLoaderHierarchy(dexClassLoader); } private void printClassLoaderHierarchy (ClassLoader classLoader) { while (classLoader != null ) { Log.d(TAG, "ClassLoader: " + classLoader); classLoader = classLoader.getParent(); } Log.d(TAG, "Reached BootClassLoader (null)" ); } }
日志输出如下
1 2 3 4 5 6 7 8 PathClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/\~~qHxEOlv8MqCgwCgGZR5sdQ==/com.example.classloaderdemo-zI8ZQUdmTUh3v21YxUjwhg==/base.apk"],nativeLibraryDirectories=[/data/app/\~~qHxEOlv8MqCgwCgGZR5sdQ==/com.example.classloaderdemo-zI8ZQUdmTUh3v21YxUjwhg==/lib/arm64, /system/lib64, /system/system_ext/lib64, /system/product/lib64, /vendor/lib64]]] BootClassLoader: java.lang.BootClassLoader@31eea01 ClassLoader referenced unknown path: /sdcard/external.dex DexClassLoader: dalvik.system.DexClassLoader[DexPathList[[],nativeLibraryDirectories=[/system/lib64, /system/system_ext/lib64, /system/product/lib64, /vendor/lib64]]] ClassLoader: dalvik.system.DexClassLoader[DexPathList[[],nativeLibraryDirectories=[/system/lib64, /system/system_ext/lib64, /system/product/lib64, /vendor/lib64]]] ClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/\~~qHxEOlv8MqCgwCgGZR5sdQ==/com.example.classloaderdemo-zI8ZQUdmTUh3v21YxUjwhg==/base.apk"],nativeLibraryDirectories=[/data/app/~~qHxEOlv8MqCgwCgGZR5sdQ==/com.example.classloaderdemo-zI8ZQUdmTUh3v21YxUjwhg==/lib/arm64, /system/lib64, /system/system_ext/lib64, /system/product/lib64, /vendor/lib64]]] ClassLoader: java.lang.BootClassLoader@31eea01 Reached BootClassLoader (null)
BootClassLoader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class BootClassLoader extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { return Class.classForName(name, false , null ); } @Override protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(className); if (clazz == null ) { clazz = findClass(className); } return clazz; }
BootClassLoader重写了findClass和loadClass两个方法
当一个类不存在时会调用Class.classForName
进行类的加载, 与加载基本类时的Class.forName()
不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ublic final class Class <T> implements java .io.Serializable, GenericDeclaration, Type, AnnotatedElement { public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName(className, true , ClassLoader.getClassLoader(caller)); } public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { if (loader == null ) { loader = BootClassLoader.getInstance(); } Class<?> result; try { result = classForName(name, initialize, loader); } catch (ClassNotFoundException e) { Throwable cause = e.getCause(); if (cause instanceof LinkageError) { throw (LinkageError) cause; } throw e; } return result; } static native Class<?> classForName(String className, boolean shouldInitialize, ClassLoader classLoader) throws ClassNotFoundException; }
可以发现forName实际上调用的还是classForName
ZygoteInit.preloadClasses()中调用Class.forName(),实际是指定BootClassLoader为类加载器,且只需要在预加载的时候进行类初始化,只需要一次 总之,通过 Class.forName() 或者 Class.classForName() 可以且仅可以直接加载基本类,一旦基本类预加载后,对于应用程序而言,我们虽然不能直接访问BootClassLoader,但可以通过Class.forName/Class.classForName加载
class文件加载 类加载时机
1 2 3 4 5 6 7 8 9 1.隐式加载: (1)创建类的实例,也就是new一个对象 (2)访问某个类或接口的静态变量,或者对该静态变量赋值 (3)调用类的静态方法 (4)反射Class.forName("android.app.ActivityThread") (5)初始化一个类的子类(会首先初始化子类的父类) 2.显示加载: (1)使用LoadClass()加载 (2)使用forName()加载
Class.forName 和 ClassLoader.loadClass加载有何不同: (1)ClassLoader.loadClass也能加载一个类,但是不会触发类的初始化(也就是说不会对类的静态变量,静态代码块进行初始化操作) (2)Class.forName这种方式,不但会加载一个类,还会触发类的初始化阶段,也能够为这个类的静态变量,静态代码块进行初始化操作
PathClassLoader PathClassLoader 是作为应用程序的系统类加载器,也是在 Zygote 进程启动的时候初始化的
基本流程为:
1 2 3 4 ZygoteInit.main() -> ZygoteInit.forkSystemServer() -> ZygoteInit.handleSystemServerProcess() -> ZygoteInit.createPathClassLoader()
在预加载基本类之后执行,所以每一个 APP 进程从 Zygote 中 fork 出来之后都自动携带了一个 PathClassLoader,它通常用于加载 apk 里面的 .dex 文件
1 2 3 4 5 6 7 8 9 10 public class PathClassLoader extends BaseDexClassLoader { public PathClassLoader (String dexPath, ClassLoader parent) { super (dexPath, null , null , parent); } public PathClassLoader (String dexPath, String librarySearchPath, ClassLoader parent) { super (dexPath, null , librarySearchPath, parent); } }
DexClassLoader 可以从包含classes.dex的jar或者apk中,加载类的类加载器, 可用于执行动态加载, 但必须是app私有可写目录来缓存odex文件. 能够加载系统没有安装的apk或者jar文件, 因此很多热修复和插件化方案都是采用DexClassLoader
1 2 3 4 5 6 7 public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader (String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { super (dexPath, new File (optimizedDirectory), librarySearchPath, parent); } }
与PathClassLoader相比, DexClassLoader提供了optimizedDirectory,而PathClassLoader则没有
optimizedDirectory正是用来存放odex文件的地方,所以可以利用DexClassLoader实现动态加载
BaseDexClassLoader BaseDexClassLoader是PathClassLoader和DexClassLoader共同的父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class BaseDexClassLoader extends ClassLoader { private final DexPathList pathList; public BaseDexClassLoader (String dexPath, File optimizedDirectory,String librarySearchPath, ClassLoader parent) { this (dexPath, optimizedDirectory, librarySearchPath, parent, false ); } public BaseDexClassLoader (String dexPath, File optimizedDirectory,String librarySearchPath, ClassLoader parent, boolean isTrusted) { super (parent); this .pathList = new DexPathList (this , dexPath, librarySearchPath, null , isTrusted); if (reporter != null ) { reportClassLoaderChain(); } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList <Throwable>(); 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); } throw cnfe; } return c; } }
BaseDexClassLoader继承于ClassLoader, 额外增加了pathList字段, 并且重写了类加载函数findClass
1 2 3 4 dexPath: 包含目标类或资源的apk/jar列表;当有多个路径则采用:分割; optimizedDirectory: 优化后dex文件存在的目录, 可以为null ; libraryPath: native 库所在路径列表;当有多个路径则采用:分割; ClassLoader:父类的类加载器
并且其findClass其实是委托给了pathList的.findClass函数
PathList 该类主要用来查找Dex、SO库的路径,并这些路径整体呈一个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 final class DexPathList { private Element[] dexElements; private final List<File> nativeLibraryDirectories; private final List<File> systemNativeLibraryDirectories; final class DexPathList { public DexPathList (ClassLoader definingContext, String dexPath,String libraryPath, File optimizedDirectory) { ... this .definingContext = definingContext; ArrayList<IOException> suppressedExceptions = new ArrayList <IOException>(); this .dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions); this .nativeLibraryDirectories = splitPaths(libraryPath, false ); this .systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path" ), true ); List<File> allNativeLibraryDirectories = new ArrayList <>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); this .nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null , suppressedExceptions); ... } }
DexPathList初始化过程,主要收集以下两个变量信息: (1)dexElements: 根据多路径的分隔符;
将dexPath转换成File列表,记录所有的dexFile (2)nativeLibraryPathElements: 记录所有的Native动态库, 包括app目录的native库和系统目录的native库
makePathElements:
1 2 3 4 private static Element[] makePathElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions) { return makeDexElements(files, optimizedDirectory, suppressedExceptions, null ); }
makeDexElements:
makeDexElements方法的作用是获取一个包含dex文件的元素集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 private static Element[] makeDexElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) { Element[] elements = new Element [files.size()]; int elementsPos = 0 ; for (File file : files) { if (file.isDirectory()) { elements[elementsPos++] = new Element (file); } else if (file.isFile()) { String name = file.getName(); DexFile dex = null ; if (name.endsWith(DEX_SUFFIX)) { dex = loadDexFile(file, optimizedDirectory, loader, elements); if (dex != null ) { elements[elementsPos++] = new Element (dex, null ); } } else { dex = loadDexFile(file, optimizedDirectory, loader, elements); if (dex == null ) { elements[elementsPos++] = new Element (file); } else { elements[elementsPos++] = new Element (dex, file); } } if (dex != null && isTrusted) { dex.setTrusted(); } } else { System.logW("ClassLoader referenced unknown path: " + file); } } if (elementsPos != elements.length) { elements = Arrays.copyOf(elements, elementsPos); } return elements; }
该方法的主要功能是创建Element数组
loadDexFile:
加载DexFile文件,而且会把优化后的dex文件缓存到对应目录
1 2 3 4 5 6 7 8 9 10 private static DexFile loadDexFile (File file, File optimizedDirectory, ClassLoader loader, Element[] elements) throws IOException { if (optimizedDirectory == null ) { return new DexFile (file, loader, elements); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0 , loader, elements); } }
DexFile:
用来描述Dex文件,Dex的加载以及Class的查找都是由该类调用它的native方法完成的
1 2 3 4 5 6 7 8 9 10 DexFile(File file, ClassLoader loader, DexPathList.Element[] elements) throws IOException { this (file.getPath(), loader, elements); } DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException { mCookie = openDexFile(fileName, null , 0 , loader, elements); mInternalCookie = mCookie; mFileName = fileName; }
openDexFile:
1 2 3 4 5 6 7 8 private static Object openDexFile (String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException { return openDexFileNative(new File (sourceName).getAbsolutePath(), (outputName == null ) ? null : new File (outputName).getAbsolutePath(), flags, loader, elements); }
sourceName为PathClassLoader构造函数传递的dexPath中以分隔符划分之后的文件名;
outputName为null;
flags = 0
loader为null;
elements为makeDexElements()过程生成的Element数组;
openDexFileNative:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static jobject DexFile_openDexFileNative (JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName ATTRIBUTE_UNUSED, jint flags ATTRIBUTE_UNUSED, jobject class_loader, jobjectArray dex_elements) { ScopedUtfChars sourceName (env, javaSourceName) ; if (sourceName.c_str () == nullptr ) { return 0 ; } Runtime* const runtime = Runtime::Current (); ClassLinker* linker = runtime->GetClassLinker (); std::vector<std::unique_ptr<const DexFile>> dex_files; std::vector<std::string> error_msgs; const OatFile* oat_file = nullptr ; dex_files = runtime->GetOatFileManager ().OpenDexFilesFromOat (sourceName.c_str (), class_loader, dex_elements, &oat_file, &error_msgs); if (!dex_files.empty ()) { jlongArray array = ConvertDexFilesToJavaArray (env, oat_file, dex_files); ... return array; } else { ... return nullptr ; } }
整体加载流程如图