最近一直在学习Android 加壳和脱壳,在进行Android加壳和脱壳的学习中,第一步便是深入理解类加载器和动态加载二者之间的关系,本文详细的介绍了类加载器和动态加载之间的关系和原理,之所以详细的讲解两者之间的关系,一是学习脱壳和加壳的需要,二是为后面Android插件化漏洞挖掘的讲解做铺垫。
Android中的类加载器机制与JVM一样遵循双亲委派模式
我们了解Android基本类预加载,首先我们回顾上文的Dalvik虚拟机启动相关:
我们执行app_process程序,进入main函数里面,然后进行AndroidRuntime::start
:
然后进入Java层:
Android的类加载机制和JVM一样遵循双亲委派模式,在dalvik/art启动时将所有Java基本类和Android系统框架的基本类加载进来,预加载的类记录在/frameworks/base/config/preloaded-classes
中
这些类只需要在Zygote进程启动时加载一遍就可以了,后续没一个APP或Android运行时环境的进程,都是从Zygote中fork出来,天然保留了加载过的类缓存
ZygoteInit.preload()
无论是系统类加载器(PathClassLoader)还是自定义的类加载器(DexClassLoader),最顶层的祖先加载器默认是 BootClassLoader,与 JVM 一样,保证了基本类的类型安全
Class文件加载:
类加载时机:
主要用于系统和app的类加载器,其中optimizedDirectory为null, 采用默认目录/data/dalvik-cache/
可以从包含classes.dex的jar或者apk中,加载类的类加载器, 可用于执行动态加载, 但必须是app私有可写目录来缓存odex文件. 能够加载系统没有安装的apk或者jar文件, 因此很多热修复和插件化方案都是采用DexClassLoader
BaseDexClassLoader会初始化dexPathList,收集dex文件和Native文件动态库
初始化:
DexPathList:
该类主要用来查找Dex、SO库的路径,并这些路径整体呈一个数组
makePathElements:
makeDexElements:
makeDexElements方法的作用是获取一个包含dex文件的元素集合
该方法的主要功能是创建Element数组
loadDexFile:
加载DexFile文件,而且会把优化后的dex文件缓存到对应目录
DexFile:
用来描述Dex文件,Dex的加载以及Class的查找都是由该类调用它的native方法完成的
openDexFile:
openDexFileNative:
这样就完成了dex的加载过程,而BaseDexClassLoader
派生出两个子类加载器:PathClassLoader
和DexClassLoader
Android中如果parent类加载器加载不到类,最终还是会调用ClassLoader对象自己的findClass()方法
loadClass()加载:
findLoadedClass:
[-> ClassLoader.java]
findClass:
[-> BaseDexClassLoader.java]
DexPathList.findClass:
DexFile.loadClassBinaryName:
defineClassNative()这是native方法
defineClassNative:
[-> dalvik_system_DexFile.cc]
在native层创建目标类的对象并添加到虚拟机列表
我们继续分析Native层可以发现:
ClassLinker核心原理:
ClassLinker 在加载类的时候遇到该类依赖的类,进行穿插加载依赖类:
我们总结BaseDexClassLoader初始化和加载原理:
Android类加载详细流程:
我们验证App中的MainActivity类加载器和系统类String类的类加载器:
我们可以明显发现PathClassLoader
加载已安装的APK
类加载器,而BootClassLoader
加载系统预安装的类
我们使用PathClassLoader
去加载 String.class类,还是可以加载成功,因为双亲委派的机制
这里我借用网上寒冰大佬动态加载的案例,来进一步讲述使用DexClassLoader类实现简单的动态加载插件dex,并验证ClassLoader的继承关系
我们先编写一个测试类文件,然后生成dex文件
我们先将dex文件放到模拟器的sdcard/下
我们新建一个程序,然后编写主程序的代码,并授权sd读取权限
然后我们编写类加载器代码
我们通过getClassNameList来获取类列表
花了一段时间,断断续续总算把这篇类加载器和动态加载的帖子写完了,从中学习到了很多,这里如果有什么错误,就请各位大佬指正了。
(
1
)加载.
class
文件时,以递归的形式逐级向上委托给父加载器ParentClassLoader加载,如果加载过了,就不用再加载一遍
(
2
)如果父加载器没有加载过,继续委托给父加载器去加载,一直到这条链路的顶级,顶级ClassLoader如果没有加载过,则尝试加载,加载失败,则逐级向下交还调用者加载
(
1
)加载.
class
文件时,以递归的形式逐级向上委托给父加载器ParentClassLoader加载,如果加载过了,就不用再加载一遍
(
2
)如果父加载器没有加载过,继续委托给父加载器去加载,一直到这条链路的顶级,顶级ClassLoader如果没有加载过,则尝试加载,加载失败,则逐级向下交还调用者加载
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
/
/
1.
先检查是否已经加载过
-
-
findLoaded
Class<?> c
=
findLoadedClass(name);
if
(c
=
=
null) {
try
{
/
/
2.
如果自己没加载过,存在父类,则委托父类
if
(parent !
=
null) {
c
=
parent.loadClass(name, false);
}
else
{
c
=
findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if
(c
=
=
null) {
/
/
3.
如果父类也没加载过,则尝试本级classLoader加载
c
=
findClass(name);
}
}
return
c;
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
/
/
1.
先检查是否已经加载过
-
-
findLoaded
Class<?> c
=
findLoadedClass(name);
if
(c
=
=
null) {
try
{
/
/
2.
如果自己没加载过,存在父类,则委托父类
if
(parent !
=
null) {
c
=
parent.loadClass(name, false);
}
else
{
c
=
findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if
(c
=
=
null) {
/
/
3.
如果父类也没加载过,则尝试本级classLoader加载
c
=
findClass(name);
}
}
return
c;
}
代码解释:
(
1
)先检查自己是否已经加载过
class
文件,用findLoadedClass方法,如果已经加载了直接返回
(
2
)如果自己没有加载过,存在父类,则委派父类去加载,用parent.loadClass(name,false)方法,此时会向上传递,然后去父加载器中循环第
1
步,一直到顶级ClassLoader
(
3
) 如果父类没有加载,则尝试本级classLoader加载,如果加载失败了就会向下传递,交给调用方式实现.
class
文件的加载
代码解释:
(
1
)先检查自己是否已经加载过
class
文件,用findLoadedClass方法,如果已经加载了直接返回
(
2
)如果自己没有加载过,存在父类,则委派父类去加载,用parent.loadClass(name,false)方法,此时会向上传递,然后去父加载器中循环第
1
步,一直到顶级ClassLoader
(
3
) 如果父类没有加载,则尝试本级classLoader加载,如果加载失败了就会向下传递,交给调用方式实现.
class
文件的加载
我们要加载一个
class
文件,我们定义了一个CustomerClassLoader类加载器:
(
1
)首先会判断自己的CustomerClassLoader否加载过,如果加载过直接返回,
(
2
)如果没有加载过则会调用父类PathClassLoader去加载,该父类同样会判断自己是否加载过,如果没有加载过则委托给父类BootClassLoader去加载,
(
3
)这个BootClassLoader是顶级classLoader,同样会去判断自己有没有加载过,如果也没有加载过则会调用自己的findClass(name)去加载,
(
4
)如果顶级BootClassLoader加载失败了,则会把加载这个动作向下交还给PathClassLoader,
(
5
)这个PathClassLoader也会尝试去调用findClass(name);去加载,如果加载失败了,则会继续向下交还给CustomClassLoader来完成加载,这整个过程感觉是一个递归的过程,逐渐往上然后有逐渐往下,直到加载成功
其实这个String.
class
在系统启动的时候已经被加载了,我们自己定义一个CustomerClassLoader去加载,其实也是父类加载的
我们要加载一个
class
文件,我们定义了一个CustomerClassLoader类加载器:
(
1
)首先会判断自己的CustomerClassLoader否加载过,如果加载过直接返回,
(
2
)如果没有加载过则会调用父类PathClassLoader去加载,该父类同样会判断自己是否加载过,如果没有加载过则委托给父类BootClassLoader去加载,
(
3
)这个BootClassLoader是顶级classLoader,同样会去判断自己有没有加载过,如果也没有加载过则会调用自己的findClass(name)去加载,
(
4
)如果顶级BootClassLoader加载失败了,则会把加载这个动作向下交还给PathClassLoader,
(
5
)这个PathClassLoader也会尝试去调用findClass(name);去加载,如果加载失败了,则会继续向下交还给CustomClassLoader来完成加载,这整个过程感觉是一个递归的过程,逐渐往上然后有逐渐往下,直到加载成功
其实这个String.
class
在系统启动的时候已经被加载了,我们自己定义一个CustomerClassLoader去加载,其实也是父类加载的
(
1
) 防止同一个.
class
文件重复加载
(
2
) 对于任意一个类确保在虚拟机中的唯一性.由加载它的类加载器和这个类的全类名一同确立其在Java虚拟机中的唯一性
(
3
) 保证.
class
文件不被篡改,通过委派方式可以保证系统类的加载逻辑不被篡改
(
1
) 防止同一个.
class
文件重复加载
(
2
) 对于任意一个类确保在虚拟机中的唯一性.由加载它的类加载器和这个类的全类名一同确立其在Java虚拟机中的唯一性
(
3
) 保证.
class
文件不被篡改,通过委派方式可以保证系统类的加载逻辑不被篡改
Zygote native 进程主要工作:
(
1
)创建虚拟机–startVM
(
2
)注册JNI函数–startReg
(
3
)通过JNI知道Java层的com.android.internal.os.ZygoteInit 类,调用main 函数,进入java 世界
Zygote native 进程主要工作:
(
1
)创建虚拟机–startVM
(
2
)注册JNI函数–startReg
(
3
)通过JNI知道Java层的com.android.internal.os.ZygoteInit 类,调用main 函数,进入java 世界
Zygote总结:
(
1
)解析init.zygote.rc中的参数,创建AppRuntime并调用AppRuntime.start()方法
(
2
)调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数
(
3
)通过JNI方式调用ZygoteInit.main(),第一次进入Java世界
(
4
)registerZygoteSocket()建立socket通道,zygote作为通信的服务端,用于响应客户端请求
(
5
)preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率
(
6
)通过startSystemServer(),fork得力帮手system_server进程,也是Java Framework的运行载体(下面讲到system server再详细讲解)
(
7
)调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作
Zygote总结:
(
1
)解析init.zygote.rc中的参数,创建AppRuntime并调用AppRuntime.start()方法
(
2
)调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数
(
3
)通过JNI方式调用ZygoteInit.main(),第一次进入Java世界
(
4
)registerZygoteSocket()建立socket通道,zygote作为通信的服务端,用于响应客户端请求
(
5
)preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率
(
6
)通过startSystemServer(),fork得力帮手system_server进程,也是Java Framework的运行载体(下面讲到system server再详细讲解)
(
7
)调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作
android.R$styleable
android.accessibilityservice.AccessibilityServiceInfo$
1
android.accessibilityservice.AccessibilityServiceInfo
android.accessibilityservice.IAccessibilityServiceClient$Stub$Proxy
android.accessibilityservice.IAccessibilityServiceClient$Stub
android.accessibilityservice.IAccessibilityServiceClient
android.accounts.AbstractAccountAuthenticator$Transport
android.accounts.AbstractAccountAuthenticator
android.accounts.Account$
1
android.accounts.Account
...
java.lang.Short
java.lang.StackOverflowError
java.lang.StackTraceElement
java.lang.StrictMath
java.lang.String$
1
java.lang.String$CaseInsensitiveComparator
java.lang.String
java.lang.StringBuffer
java.lang.StringBuilder
java.lang.StringFactory
java.lang.StringIndexOutOfBoundsException
java.lang.System$PropertiesWithNonOverrideableDefaults
java.lang.System
java.lang.Thread$
1
...
android.R$styleable
android.accessibilityservice.AccessibilityServiceInfo$
1
android.accessibilityservice.AccessibilityServiceInfo
android.accessibilityservice.IAccessibilityServiceClient$Stub$Proxy
android.accessibilityservice.IAccessibilityServiceClient$Stub
android.accessibilityservice.IAccessibilityServiceClient
android.accounts.AbstractAccountAuthenticator$Transport
android.accounts.AbstractAccountAuthenticator
android.accounts.Account$
1
android.accounts.Account
...
java.lang.Short
java.lang.StackOverflowError
java.lang.StackTraceElement
java.lang.StrictMath
java.lang.String$
1
java.lang.String$CaseInsensitiveComparator
java.lang.String
java.lang.StringBuffer
java.lang.StringBuilder
java.lang.StringFactory
java.lang.StringIndexOutOfBoundsException
java.lang.System$PropertiesWithNonOverrideableDefaults
java.lang.System
java.lang.Thread$
1
...
static void preload(TimingsTraceLog bootTimingsTraceLog) {
/
/
...省略
preloadClasses();
/
/
...省略
}
private static void preloadClasses() {
final VMRuntime runtime
=
VMRuntime.getRuntime();
/
/
读取 preloaded_classes 文件
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) {
/
/
Skip comments
and
blank lines.
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
{
/
/
...省略
}
}
static void preload(TimingsTraceLog bootTimingsTraceLog) {
/
/
...省略
preloadClasses();
/
/
...省略
}
private static void preloadClasses() {
final VMRuntime runtime
=
VMRuntime.getRuntime();
/
/
读取 preloaded_classes 文件
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) {
/
/
Skip comments
and
blank lines.
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
{
/
/
...省略
}
}
Android中的ClassLoader类型分为系统ClassLoader和自定义ClassLoader。其中系统ClassLoader包括
3
种是BootClassLoader、DexClassLoader、PathClassLoader
(
1
)BootClassLoader:Android平台上所有Android系统启动时会使用BootClassLoader来预加载常用的类
(
2
)BaseDexClassLoader:实际应用层类文件的加载,而真正的加载委托给pathList来完成
(
3
)DexClassLoader:可以加载dex文件以及包含dex的压缩文件(apk,dex,jar,
zip
),可以安装一个未安装的apk文件,一般为自定义类加载器
(
4
)PathClassLoader:可以加载系统类和应用程序的类,通常用来加载已安装的apk的dex文件
补充:
Android 提供的原生加载器叫做基础类加载器,包括:BootClassLoader,PathClassLoader,DexClassLoader,InMemoryDexClassLoader(Android
8.0
引入),DelegateLastClassLoader(Android
8.1
引入)
Android中的ClassLoader类型分为系统ClassLoader和自定义ClassLoader。其中系统ClassLoader包括
3
种是BootClassLoader、DexClassLoader、PathClassLoader
(
1
)BootClassLoader:Android平台上所有Android系统启动时会使用BootClassLoader来预加载常用的类
(
2
)BaseDexClassLoader:实际应用层类文件的加载,而真正的加载委托给pathList来完成
(
3
)DexClassLoader:可以加载dex文件以及包含dex的压缩文件(apk,dex,jar,
zip
),可以安装一个未安装的apk文件,一般为自定义类加载器
(
4
)PathClassLoader:可以加载系统类和应用程序的类,通常用来加载已安装的apk的dex文件
补充:
Android 提供的原生加载器叫做基础类加载器,包括:BootClassLoader,PathClassLoader,DexClassLoader,InMemoryDexClassLoader(Android
8.0
引入),DelegateLastClassLoader(Android
8.1
引入)
启动类加载器,用于加载 Zygote 进程已经预加载的基本类,可以推测它只需从缓存中加载。这是基类 ClassLoader 的一个内部类,是包访问权限,所以应用程序无权直接访问
启动类加载器,用于加载 Zygote 进程已经预加载的基本类,可以推测它只需从缓存中加载。这是基类 ClassLoader 的一个内部类,是包访问权限,所以应用程序无权直接访问
public abstract
class
ClassLoader {
/
/
...省略
class
BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
public static synchronized BootClassLoader getInstance() {
if
(instance
=
=
null) {
instance
=
new BootClassLoader();
}
return
instance;
}
public BootClassLoader() {
super
(null);
}
@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;
}
/
/
...省略
}
}
public abstract
class
ClassLoader {
/
/
...省略
class
BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
public static synchronized BootClassLoader getInstance() {
if
(instance
=
=
null) {
instance
=
new BootClassLoader();
}
return
instance;
}
public BootClassLoader() {
super
(null);
}
@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()方法
findClass()方法调用Class.classForName()方法,而ZygoteInit.preloadClasses()中,加载基本类是Class.forName()
源码分析:
我们可以看见,BootClassLoader没有父加载器,在缓存取不到类是直接调用自己的findClass()方法
findClass()方法调用Class.classForName()方法,而ZygoteInit.preloadClasses()中,加载基本类是Class.forName()
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;
/
/
...省略
}
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;
/
/
...省略
}
我们可以发现,预加载时,ZygoteInit.preloadClasses()中调用Class.forName(),实际是指定BootClassLoader为类加载器,且只需要在预加载的时候进行类初始化,只需要一次
总之,通过 Class.forName() 或者 Class.classForName() 可以且仅可以直接加载基本类,一旦基本类预加载后,对于应用程序而言,我们虽然不能直接访问BootClassLoader,但可以通过Class.forName
/
Class.classForName加载
我们可以发现,预加载时,ZygoteInit.preloadClasses()中调用Class.forName(),实际是指定BootClassLoader为类加载器,且只需要在预加载的时候进行类初始化,只需要一次
总之,通过 Class.forName() 或者 Class.classForName() 可以且仅可以直接加载基本类,一旦基本类预加载后,对于应用程序而言,我们虽然不能直接访问BootClassLoader,但可以通过Class.forName
/
Class.classForName加载
1.
通过Class.forName()方法动态加载
2.
通过ClassLoader.loadClass()方法动态加载
类的加载分为
3
个步骤:
1.
装载(Load),
2.
链接(Link),
3.
初始化(Intialize)
1.
通过Class.forName()方法动态加载
2.
通过ClassLoader.loadClass()方法动态加载
类的加载分为
3
个步骤:
1.
装载(Load),
2.
链接(Link),
3.
初始化(Intialize)
1.
隐式加载:
(
1
)创建类的实例,也就是new一个对象
(
2
)访问某个类或接口的静态变量,或者对该静态变量赋值
(
3
)调用类的静态方法
(
4
)反射Class.forName(
"android.app.ActivityThread"
)
(
5
)初始化一个类的子类(会首先初始化子类的父类)
2.
显示加载:
(
1
)使用LoadClass()加载
(
2
)使用forName()加载
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这种方式,不但会加载一个类,还会触发类的初始化阶段,也能够为这个类的静态变量,静态代码块进行初始化操作
Class.forName 和 ClassLoader.loadClass加载有何不同:
(
1
)ClassLoader.loadClass也能加载一个类,但是不会触发类的初始化(也就是说不会对类的静态变量,静态代码块进行初始化操作)
(
2
)Class.forName这种方式,不但会加载一个类,还会触发类的初始化阶段,也能够为这个类的静态变量,静态代码块进行初始化操作
PathClassLoader 是作为应用程序的系统类加载器,也是在 Zygote 进程启动的时候初始化的(基本流程为:ZygoteInit.main()
-
> ZygoteInit.forkSystemServer()
-
> ZygoteInit.handleSystemServerProcess()
-
> ZygoteInit.createPathClassLoader()。在预加载基本类之后执行),所以每一个 APP 进程从 Zygote 中 fork 出来之后都自动携带了一个 PathClassLoader,它通常用于加载 apk 里面的 .dex 文件
PathClassLoader 是作为应用程序的系统类加载器,也是在 Zygote 进程启动的时候初始化的(基本流程为:ZygoteInit.main()
-
> ZygoteInit.forkSystemServer()
-
> ZygoteInit.handleSystemServerProcess()
-
> ZygoteInit.createPathClassLoader()。在预加载基本类之后执行),所以每一个 APP 进程从 Zygote 中 fork 出来之后都自动携带了一个 PathClassLoader,它通常用于加载 apk 里面的 .dex 文件
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);
}
}
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);
}
}
public
class
DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super
(dexPath, new
File
(optimizedDirectory), librarySearchPath, parent);
}
}
public
class
DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super
(dexPath, new
File
(optimizedDirectory), librarySearchPath, parent);
}
}
总结:
我们可以发现DexClassLoader与PathClassLoader都继承于BaseDexClassLoader,这两个类只是提供了自己的构造函数,没有额外的实现
区别:
DexClassLoader提供了optimizedDirectory,而PathClassLoader则没有,optimizedDirectory正是用来存放odex文件的地方,所以可以利用DexClassLoader实现动态加载
总结:
我们可以发现DexClassLoader与PathClassLoader都继承于BaseDexClassLoader,这两个类只是提供了自己的构造函数,没有额外的实现
区别:
DexClassLoader提供了optimizedDirectory,而PathClassLoader则没有,optimizedDirectory正是用来存放odex文件的地方,所以可以利用DexClassLoader实现动态加载
public
class
BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
/
/
记录dex文件路径信息
public BaseDexClassLoader(String dexPath,
File
optimizedDirectory, String libraryPath, ClassLoader parent) {
super
(parent);
this.pathList
=
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
}
public
class
BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
/
/
记录dex文件路径信息
public BaseDexClassLoader(String dexPath,
File
optimizedDirectory, String libraryPath, ClassLoader parent) {
super
(parent);
this.pathList
=
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
}
dexPath: 包含目标类或资源的apk
/
jar列表;当有多个路径则采用:分割;
optimizedDirectory: 优化后dex文件存在的目录, 可以为null;
libraryPath: native库所在路径列表;当有多个路径则采用:分割;
ClassLoader:父类的类加载器
dexPath: 包含目标类或资源的apk
/
jar列表;当有多个路径则采用:分割;
optimizedDirectory: 优化后dex文件存在的目录, 可以为null;
libraryPath: native库所在路径列表;当有多个路径则采用:分割;
ClassLoader:父类的类加载器
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>();
/
/
记录所有的dexFile文件
this.dexElements
=
makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
/
/
app目录的native库
this.nativeLibraryDirectories
=
splitPaths(libraryPath, false);
/
/
系统目录的native库
this.systemNativeLibraryDirectories
=
splitPaths(System.getProperty(
"java.library.path"
), true);
List
<
File
> allNativeLibraryDirectories
=
new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
/
/
记录所有的Native动态库
this.nativeLibraryPathElements
=
makePathElements(allNativeLibraryDirectories, null, suppressedExceptions);
...
}
}
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>();
/
/
记录所有的dexFile文件
this.dexElements
=
makePathElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
/
/
app目录的native库
this.nativeLibraryDirectories
=
splitPaths(libraryPath, false);
/
/
系统目录的native库
this.systemNativeLibraryDirectories
=
splitPaths(System.getProperty(
"java.library.path"
), true);
List
<
File
> allNativeLibraryDirectories
=
new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
/
/
记录所有的Native动态库
this.nativeLibraryPathElements
=
makePathElements(allNativeLibraryDirectories, null, suppressedExceptions);
...
}
}
DexPathList初始化过程,主要收集以下两个变量信息:
(
1
)dexElements: 根据多路径的分隔符“;”将dexPath转换成
File
列表,记录所有的dexFile
(
2
)nativeLibraryPathElements: 记录所有的Native动态库, 包括app目录的native库和系统目录的native库
DexPathList初始化过程,主要收集以下两个变量信息:
(
1
)dexElements: 根据多路径的分隔符“;”将dexPath转换成
File
列表,记录所有的dexFile
(
2
)nativeLibraryPathElements: 记录所有的Native动态库, 包括app目录的native库和系统目录的native库
private static Element[] makePathElements(
List
<
File
> files,
File
optimizedDirectory,
List
<IOException> suppressedExceptions) {
return
makeDexElements(files, optimizedDirectory, suppressedExceptions, null);
}
private static Element[] makePathElements(
List
<
File
> files,
File
optimizedDirectory,
List
<IOException> suppressedExceptions) {
return
makeDexElements(files, optimizedDirectory, suppressedExceptions, null);
}
private static Element[] makeDexElements(
List
<
File
> files,
File
optimizedDirectory,
List
<IOException> suppressedExceptions, ClassLoader loader) {
return
makeDexElements(files, optimizedDirectory, suppressedExceptions, loader, false);
}
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;
/
/
匹配以.dex为后缀的文件
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;
}
private static Element[] makeDexElements(
List
<
File
> files,
File
optimizedDirectory,
List
<IOException> suppressedExceptions, ClassLoader loader) {
return
makeDexElements(files, optimizedDirectory, suppressedExceptions, loader, false);
}
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;
/
/
匹配以.dex为后缀的文件
if
(name.endsWith(DEX_SUFFIX)) {
dex
=
loadDexFile(
file
, optimizedDirectory, loader, elements);
if
(dex !
=
null) {
elements[elementsPos
+
+
]
=
new Element(dex, null);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-2-18 19:51
被随风而行aa编辑
,原因: