了解Android ClassLoader
Java中通过ClassLoader将.jar或class类加载进JVM虚拟机。在Android中也有ClassLoader,因为安卓ART虚拟机执行的是.dex字节码,所以安卓中的ClassLoader和Java中的有所不同。来了解一下Android的类加载器。
1、ClassLoader
ClassLoader有两种类型:系统加载器和自定义加载器。系统加载器有三种:BootClassLoader、DexClassLoader、PathClassLoader。
1.1、BootClassLoader
Android系统启动是使用BootClassLoader加载器来加载常用的类。是Java实现,并非JVM中的BootClassLoader用C++实现。
源码路径:libcore/ojluni/src/main/java/java/lang/ClassLoader.java
class BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
@FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
}
return instance;
}
public BootClassLoader() {
super(null);
}
...
}BootClassLoader是单例模式,而且该类定义在ClassLoader中,访问修饰符默认是包访问权限,普通应用程序无法调用。
1.2、DexClassLoader
DexClassLoader加载器用来加载.dex文件、包含dex的文件(Apk、Jar包)等。
源码路径:/libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
/**
* @param dexPath dex相关文件路径集合,多个路径用文件分隔符分割,默认文件分隔符为“:”
* @param optimizedDirectory 解压dex文件存储路径,这个路径必须是一个内部存储路径,
* 一般情况下是当前应用程序的私有路径:/data/data/<PackageName>/...
* 自API级别26起,此参数已弃用,并且无效.
* @param librarySearchPath C/C++库的路径集合,多个文件用文件分隔符分割,可为null
* @param parent 父加载器
*/
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}1.3、PathClassLoader
Android用PathClassLoader加载系统类和应用程序的类。
源码路径:/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
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);
}
/**
* @hide
*/
@libcore.api.CorePlatformApi
public PathClassLoader(
String dexPath, String librarySearchPath, ClassLoader parent,
ClassLoader[] sharedLibraryLoaders) {
super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
}
}2、ClassLoader关系
粗略的更具源码继承关系捋了一下各个ClassLoader之间的继承关系。
3、ClassLoader初始化
了解一下Android系统各个ClassLoader加载器的初始化过程。
3.1、BootClassLoader加载
在《Zyoget进程的启动》这篇中讲到Zygote进程的启动,在ZygoteInit的main()方法中有一行代码执行预加载:
@UnsupportedAppUsage
public static void main(String argv[]) {
...
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
...
preload(bootTimingsTraceLog);
...
} else {
Zygote.resetNicePriority();
}
...
}preload(TimingsTraceLog bootTimingsTraceLog)中又调用了preloadClasses()方法:
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
...
preloadClasses();
...
sPreloadComplete = true;
}preloadClasses()方法加载系统常用的类
/**
* 执行Zygote进程初始化。 加载并初始化常用的类。
* 大多数类仅会分配几百个字节,但有些会分配十几个KB(在一种情况下为500 + K)。
*/
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);//1
}
...
try {
BufferedReader br =
new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
...
try {
// Load and explicitly initialize the given class. Use
// Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
// (to derive the caller's class-loader). Use true to force initialization, and
// null for the boot classpath class-loader (could as well cache the
// class-loader of this class in a variable).
Class.forName(line, true, null);
count++;
}
...
}PRELOADED_CLASSES定义的是预加载类文件的目录。
/**
* The path of a file that contains classes to preload.
*/
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";在源码中的路径为/frameworks/base/config/preloaded-classes,数千行类名称:
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Preloaded-classes filter file for phones.
# This file has been derived for mainline phone (and tablet) usage.
#
android.R$styleable
android.accessibilityservice.AccessibilityServiceInfo$1
...
android.animation.TimeInterpolator
...
java.sql.Date
...
libcore.io.Os
...
org.json.JSON
...
sun.util.logging.PlatformLogger每读取一行类名,就通过Class.forName()加载该类。源码在/libcore/ojluni/src/main/java/java/lang/目录下
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
loader = BootClassLoader.getInstance();//获取BootClassLoader实例
}
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;
}可以看到是通过BootClassLoader实例加载系统的类,不继续跟下去了,后面就是Native层。
3.2、PathClassLoader加载
和BootClassLoader启动会不同,PathClassLoader在ZygoteInit的forkSystemServer()方法中开始初始化,在《SystemServer系统服务启动》有提到过该方法,API 29源码和之前不大一样。
/**
* 准备系统服务器进程的参数和派生。
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
...
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
return null;
}fork自身创建新的进程,pid≠0,最后执行handleSystemServerProcess()方法
/**
* 完成fork系统进程之后的剩余各种工作
*/
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
// 将umask设置为0077,以便新文件和目录将默认为仅所有者权限。
Os.umask(S_IRWXG | S_IRWXO);
...
if (parsedArgs.mInvokeWith != null) {
...
} else {
createSystemServerClassLoader(); //1
ClassLoader cl = sCachedSystemServerClassLoader; //2
if (cl != null) {
Thread.currentThread().setContextClassLoader(cl); //3
}
/*
* 将其余参数传递给SystemServer。
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, cl);
}
/* should never reach here */
}其中在createSystemServerClassLoader()方法初始化sCachedSystemServerClassLoader实例:
private static void createSystemServerClassLoader() {
if (sCachedSystemServerClassLoader != null) {
return;
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
// TODO: Should we run optimization here?
if (systemServerClasspath != null) {
sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
}
}createPathClassLoader()方法创建PathClassLoader,在该方法的最后是通过ClassLoaderFactory工厂模式来创建PathClassLoader加载器。
static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
// 使用BootClassLoader加载器,正是运行时AOT所期望的。
ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */);
}回到handleSystemServerProcess()方法,将ClassLoader加载器实例通过Thread的setContextClassLoader(cl)方法设置给当前进程:
/**
* Sets the context ClassLoader for this Thread. The context
* ClassLoader can be set when a thread is created, and allows
* the creator of the thread to provide the appropriate class loader,
* through {@code getContextClassLoader}, to code running in the thread
* when loading classes and resources.
*
* @param cl 此线程的上下文ClassLoader,或者为null,指示系统类加载器 (or, failing that, the bootstrap class loader)
*
* @throws SecurityException 如果当前线程无法设置上下文ClassLoader
*
* @since 1.2
*/
public void setContextClassLoader(ClassLoader cl) {
// Android-removed: SecurityManager stubbed out on Android
// SecurityManager sm = System.getSecurityManager();
// if (sm != null) {
// sm.checkPermission(new RuntimePermission("setContextClassLoader"));
// }
contextClassLoader = cl;
}4、ClassLoader加载过程
Java的ClassLoader加载使用的是双亲委派模式,从ClassLoader的loadClass()方法开始
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}调用protected服务权限的loadClass(String name, boolean resolve)方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 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);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}该方法中先调用findLoadedClass(String name)方法判断要查找的类是否存在
protected final Class<?> findLoadedClass(String name) {
ClassLoader loader;
if (this == BootClassLoader.getInstance())
loader = null;
else
loader = this;
return VMClassLoader.findLoadedClass(loader, name);
}最后还是通过Native的findLoadedClass()方法来查找。
@FastNative
native static Class findLoadedClass(ClassLoader cl, String name);回到loadClass()方法中,标准的类加载模式:
①先判断类是否已经被加载,如果已经加载就直接返回
②先判断父类加载器是否存在,调用父类加载器loadClass()方法
③否则,调用findBootstrapClassOrNull(name)方法,但是该方法直接返回null
* Returns a class loaded by the bootstrap class loader;
* or return null if not found.
*/
private Class<?> findBootstrapClassOrNull(String name)
{
return null;
}④整个委托流程执行完毕,最后判断该类是否为null,在调用findClass()方法查找
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}ClassLoader中的该方法直接抛出ClassNotFoundException异常,它的实现在BaseDexClassLoader类加载器中
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// First, check whether the class is present in our shared libraries.
if (sharedLibraryLoaders != null) {
for (ClassLoader loader : sharedLibraryLoaders) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}
}
}
// Check whether the class in question is present in the dexPath that
// this classloader operates on.
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;
}通过DexPathList的findClass()方法
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}又调用Element的findClass()方法,Element是DexPathList的静态内部类
/**
* Element of the dex/resource path. Note: should be called DexElement, but apps reflect on
* this.
*/
static class Element {
...
}Element中的findClass()方法定如下:
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}Element内部封装了DexFile,当其不为空时,通过loadClassBinaryName()方法加载dex
/**
* See {@link #loadClass(String, ClassLoader)}.
*
* This takes a "binary" class name to better match ClassLoader semantics.
*
* @hide
*/
@UnsupportedAppUsage
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}defineClass()的定义如下:
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List<Throwable> suppressed) {
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);
}
}
return result;
}发现最终调用Native方法defineClassNative()加载.dex文件。
参考资料:
Android进阶解密
AOSP


