@Awille
2018-12-14T16:55:30.000000Z
字数 6750
阅读 93
android java 热修复基础
java中的类加载器有两种类型,即系统类加载器和自定义类加载器。
系统类加载器分为
1、Boostrap ClassLoader(引导类加载器)
有c/c++实现,用于加载指定的JDK核心类库。
System.out.println(System.getProperty("sun.boot.class.path"));
由以上代码可以输出Bootstrap所加载的目录:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_191\jre\classes
可以看到基本都是lib文件夹下的jar包,都为核心的类库
2、Extension ClassLoader (拓展类加载器)
用来加载以下目录的类库:
a、$JAVA_HOME/jre/lib 目录中的jar包
b、系统属性java.ext.dir所指定的目录
查看其加载的目录:
System.out.println(System.getProperty("java.ext.dirs"));
结果为:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\ext;
C:\Windows\Sun\Java\lib\ext
3、Application ClassLoader(应用程序类加载器)
实现类为AppClassLoader,也称作系统类加载器(System ClassLoader)
用来加载以下的类库
a、当前程序的Classpath目录
b、java.class.path指定的目录
4、Custom ClassLoader(自定义类加载器)
通过继承java.lang.ClassLoader来实现自己的自定义类加载器
public class Test {
public static void main(String[] args) {
ClassLoader loader = Test.class.getClassLoader();
while (loader != null) {
System.out.println(loader);
loader = loader.getParent();
}
}
}
查看输出:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
根据输出结果,是可以猜测 Test类的类加载器是系统类加载器,而系统类加载器的父类为拓展类加载器。其实bootstrapClassLoader也是应该打出来的,因为其实由c++来实现的,所以没有打印出来。
这里系统类加载器的父类与拓展类加载器,但是这并不代表着系统类加载继承了拓展类加载器。
java中实际的类继承关系为:
ClassLoader(抽象类)<-----SecureLoader(继承ClassLoader并加入权限功能,没有实现)<------URLClassLoader(继承SecureLoader,加入功能可以通过URL从jar文件和文件夹中加载类和资源)
ExtClassLoader和AppClassLoader都继承自URLClassLoader,他们都为Launcher的内部类,Launcher是java虚拟机的入口,这连两个类都在Launcher中进行初始化。
在加载一个类时,首先判断该class是否已经加载,如果没有则不是自身去查找,而是委托给父类加载器去找,这样依次递归到达最顶层,到最顶层如果Bootsrap ClassLoader找到了该class,就直接返回,如果没有找到就向下查找,知道最后让最下层的自身去查找。
classLoader中的loadClass方法 :
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);//首先从加载缓存中查看是否已经加载
if (c == null) {//如果没有
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);//递归交给父类
} else {
c = findBootstrapClassOrNull(name);
//递归失败到最顶层,BootstrapClassLoader在指定目录中加载
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {//如果BootstrapClassLoader加载失败
// 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
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
这种模式的好处:
1、避免类的重复
2、更加安全,防止出现自定义一个String类代替系统String这种情况
如果需要加载网络上或者D盘中的jar包,则需要自定义类加载器
实现步骤一般为:
(1)定义一个classloader并继承ClassLoader
(2)重写findclass方法,在findclass中调用defineclass
import java.io.*;
public class DiskClassLoader extends ClassLoader {
private String path;
DiskClassLoader(String path) {
this.path = path;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
byte[] classData = loadClassData(name);
System.out.println(classData.length);
if (classData == null) {
throw new ClassNotFoundException();
} else {
clazz = defineClass(name.substring(0, 4), classData, 0, classData.length);
}
return clazz;
}
public byte[] loadClassData(String name) { //name为文件名
File file = new File(path, name);
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
return out.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
写一个测试类编译成class文件,放在D盘根目录之下:
public class Test {
public void say() {
System.out.println("yyyyyyyy");
}
}
测试:
public class Testee {
public static void main(String[] args) {
DiskClassLoader diskClassLoader = new DiskClassLoader("D:\\");
try {
Class c = diskClassLoader.loadClass("Test.class");
if (c != null) {
try {
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("say", null);
method.invoke(obj, null);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:
384
yyyyyyyy
成功。
android中的classloader与java中的classloader还是有比较大的区别的,java中的classloader用来加载class文件(包括jar包里面的)。android中的classloader用来加载dex文件。
android中的classloader同样也分两种类型,系统类加载器与自定义类加载器。
系统类加载器:
(1)BootClassLoader
android系统会启动BootClassLoader来加载常用类
(java中boostrapClassLoader会用来加载核心类,这里类似)
(2)DexClassLoader
用来加载dex文件以及包含dex的压缩文件(apk和jar文件)
其构造函数为:
public DexClassLoader(String dexPath, String optimizedDirectory, String LibrarySearchPath, ClassLoader parent){ ... }
dexPath为dex文件路径,可以为多个,用:分隔
optimizedDirectory为解压的dex文件存储路径
LibrarySearchPath:包含C/C++库的路径集合
parent:父类加载器
(3)PathClassLoader
加载系统类和应用程序类,一般用来加载已经安装的apk文件。其构造方法为:
public PathClassLoader(String dexPath, String LibrarySearchPath, ClassLoader parent)
这个类加载器的构造方法相对于DexClassLoader少了一个参数optimizedDirectory,即dex文件解压后的路径设置没了,其默认值为/data/dalvik-cache,可见,PathClassLoader用来加载已经安装的apk文件的dex文件。(在android系统当中,已经安装的apk文件的dex文件会放在/data/dalvik-cache当中)。
查看加载一个activity的调用过程中涉及到的classloader,查看方法为在activity的oncreate当中,打印该类的加载器以及所有的父类加载器(写个while循环就可以了)。
dalvik.system.PathClassLoader[
DexPathList[
[zip file"/data/app/com.example.will.hotfixtest-2/base.apk"],
nativeLibraryDirectories=
[/data/app/com.example.will.hotfixtest-2/lib/x86, /data/app/com.example.will.hotfixtest-2/base.apk!/lib/x86,
/vendor/lib,
/system/lib]
]
]
java.lang.BootClassLoader@a52455e
可以看到涉及到PathClassLoader和BootClassLoader,PathClassLoader在上面已经提到过,这是专门用来加载已经安装的apk文件中的dex文件的,而BootClassLoader是用来加载一些常用类的,跟java的boostrapClassLoader差不多,所以这里出现并不意外。
顶层:ClassLoader(抽象类)
第二层(全部都继承ClassLoader) BootClassLoader BaseDexClassLoader SecureClassLoader
第三层:继承BaseDexClassLoader(DexClassLoader, PathClassLoader, IMemoryDexClassLoader); 继承自SecureClassLoader(URLClassLoader)
这里的SecureClassLoader与URLClassLoader跟java的classLoader是一样的。
BootClassLoader不能说是属于第二层,他是ClassLoader的继承类。
BaseDexClassLoader:ClassLoader的实现类。
IMemoryDexClassLoader:用于加载内存中的dex文件
PathClassLoader与DexClassLoader前面已经提过了。
android中的dex文件的加载同样遵循双亲委托模式。
Zygote进程的Zygote入口中创建
在SystemServer中采用工厂模式创建。
后面这三个小点涉及到了android的源码,目前感觉以自己的水平还是不适合去研究源码,所以跳过了。希望这些对后面热修复相关知识的学习有一些帮助。