目錄
Android黑科技動(dòng)態(tài)加載(一)之Java中的ClassLoader
Android黑科技動(dòng)態(tài)加載(二)之Android中的ClassLoader
Android黑科技動(dòng)態(tài)加載(三)之動(dòng)態(tài)加載資源
Android黑科技動(dòng)態(tài)加載(四)之插件化開發(fā)
我們的認(rèn)識(shí)
我們都知道, 剛寫好的Java的源文件是以
.java
為擴(kuò)展名的, 需要讓JVM
去解析的時(shí)候, 必須編譯成Java字節(jié)碼
, 而Java字節(jié)碼文件就是以.class
為擴(kuò)展名的.ClassLoader
的對(duì)象就是去加載這些Java字節(jié)碼
文件
Java默認(rèn)的ClassLoader
在Java默認(rèn)環(huán)境中, 提供了三種Classloader
BootStrap ClassLoader
:啟動(dòng)類加載器
二汛,Java類加載器中最頂層的類加載器带欢,負(fù)責(zé)加載JDK中的核心類庫,如:rt.jar
、resources.jar
迹鹅、charsets.jar
等Extension ClassLoader
:擴(kuò)展類加載器
,負(fù)責(zé)加載Java的擴(kuò)展類庫倒源,默認(rèn)加載JAVA_HOME/jre/lib/ext/
目下的所有jarApp ClassLoader
:系統(tǒng)類加載器
攘蔽,負(fù)責(zé)加載應(yīng)用程序classpath
目錄下的所有jar和class文件
有興趣的朋友可以通過
Class.getClassLoader()
去驗(yàn)證一下.
ClassLoader加載原理
加載順序(雙親委托機(jī)制)
如果我們
自定義一個(gè)ClassLoader
叫做MyClassLoader
, 當(dāng)查找需要的類的時(shí)候,MyClassLoader
會(huì)把該責(zé)任委托給它的父類加載器
不是通過繼承的父類, 是通過parent屬性去持有. 整個(gè)過程從頂?shù)较? 如果一個(gè)類找不到, 那么查找順序是BootStrap ClassLoader
->Extension ClassLoader
->App ClassLoader
->MyClassLoader
->拋出異常ClassNotFoundException
兩個(gè)類是否相等
一般我們認(rèn)為兩個(gè)類是否相等, 僅僅通過
包名+類名
去區(qū)分. 事實(shí)上還有一個(gè)條件, 就是加載該類的ClassLoader是否是同一個(gè)
兩個(gè)關(guān)鍵方法
ClassLoader中認(rèn)為比較關(guān)鍵的方法有三個(gè):
public Class<?> loadClass(String name)
: 根據(jù)雙親委托機(jī)制
的方式查找classprotected Class<?> findClass(String name)
: 根據(jù)該ClassLoader的方式去查找classprotected final Class<?> defineClass()
: 該方法有多個(gè)重載, 具體參數(shù)不寫了. 主要作用是把類加載到內(nèi)存中
loadClass
為一個(gè)模板方法, 有興趣的話可以去看看源碼. 所以一般我們只需要重寫findClass
即可.
上面三個(gè)方法有關(guān)系的, loadClass->findClass->defineClass
. loadClass
使用雙親委托機(jī)制去查找要加載的類, 當(dāng)上層ClassLoader不能加載該類時(shí), 就會(huì)去使用自己的findClass
去加載類. 加載到的類(可能為空)會(huì)傳入defineClass
中, 如果為空則拋出異常.
自定義ClassLoader
我們下面自定義一個(gè)
MyClassLoader
去加載外部的RemoteClass.class
文件
RemoteClass.class
package top.august1996.demo;
public class RemoteClass {
public void catched() {
System.out.println("I am catched...");
}
}
我們把上面文件移到任意目錄(
/Users/August/Desktop
)
cd /Users/August/Desktop
javac RemoteClass.java
現(xiàn)在我們就在桌面上編譯了一個(gè)字節(jié)碼文件
MyClassLoader.java
package top.august1996.demo;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
private String mClassPath; // Class存放的的目錄
public MyClassLoader(String classPath) {
mClassPath = classPath;
}
@Override
protected Class<?> findClass(String name) {
File clsFile = new File(mClassPath, getClassName(name));
FileInputStream fis = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = null;
try {
fis = new FileInputStream(clsFile);
int data = 0;
while ((data = fis.read()) != -1) {
baos.write(data);
}
b = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 上面是基本的數(shù)據(jù)流操作, 把Class文件轉(zhuǎn)換成二進(jìn)制流
return defineClass(name, b, 0, b.length);
}
/**
* 獲取Class的完整文件名
*
* @param name
* @return
*/
private String getClassName(String name) {
String clsName = null;
if (name != null) {
int lastIndexOf = name.lastIndexOf(".");
if (lastIndexOf == -1) {
clsName = name + ".class";
} else {
clsName = name.substring(lastIndexOf + 1) + ".class";
}
}
return clsName;
}
}
TestDemo.java
package top.august1996.demo;
import java.lang.reflect.Method;
public class TestDemo {
public static void main(String[] args) throws Exception {
MyClassLoader classLoader = new MyClassLoader("/Users/August/Desktop");
Class<?> cls = classLoader.findClass("top.august1996.demo.RemoteClass");
Object object = cls.newInstance();
Method method = cls.getDeclaredMethod("catched", null);
method.invoke(object, null);
}
}
I am catched...
我們使用反射區(qū)調(diào)用自己加載的
RemoteClass
的catched
方法, 可以看到結(jié)果已經(jīng)是成功的了.