若是你看過ZygoteInit.java
的main
方法可能會對這個類不陌生,在Android8.1以前,其main方法都是相似如下這樣:java
如下代碼基於Android8.0android
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
try {
...
// 建立server端的socket,name爲"zygote"
zygoteServer.registerServerSocket(socketName);
...
if (startSystemServer) {
// 啓動SystemServer進程
startSystemServer(abiList, socketName, zygoteServer);
}
Log.i(TAG, "Accepting command socket connections");
// 等待AMS請求
zygoteServer.runSelectLoop(abiList);
zygoteServer.closeServerSocket();
} catch (Zygote.MethodAndArgsCaller caller) {
// 運行MethodAndArgsCaller的run方法
caller.run();
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
zygoteServer.closeServerSocket();
throw ex;
}
}
複製代碼
其中比較讓人疑惑的地方是caller.run();
這句,爲什麼一個Exception
須要運行?socket
咱們先看下MethodAndArgsCaller
這個類的源碼:oop
/**
* Helper exception class which holds a method and arguments and
* can call them. This is used as part of a trampoline to get rid of
* the initial process setup stack frames.
*/
public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
複製代碼
這個類的功能比較單一,能夠看出這個類是協助反射調用的,調用了其run方法將經過反射調用傳入的方法。ui
這個類繼承了Exception類,咱們看拋出這個異常的地方(RuntimeInit類中):this
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws Zygote.MethodAndArgsCaller {
Class<?> cl;
try {
// 根據類名查找類
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
// 找到該類的main方法
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new Zygote.MethodAndArgsCaller(m, argv);
}
複製代碼
到這個方法就能夠看出,最終找到某個類的main方法和方法須要的參數,將其傳入MethodAndArgsCaller
這個Exception中,並在catch了這個Exception的地方調用。spa
那麼爲何要使用這種奇技淫巧調用,而不直接調用某個類呢?code
其實這個註釋已經解釋了:server
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new Zygote.MethodAndArgsCaller(m, argv);
複製代碼
經過拋異常而後調用Exception的run方法的方式,能夠清除調用過程的堆棧信息。對象
解釋一下,就是這樣作以後,調用的堆棧信息會是相似這樣:
...
at com.android.server.SystemServer.main(SystemServer.java:175)
at java.lang.reflect.Method.invoke!(Native method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
複製代碼
咱們看到上面異常信息中只有SystemServer.main
、MethodAndArgsCaller.run
、ZygoteInit.main
,而沒有中間的調用過程。這樣使得每一個被ZygoteInit啓動的類看起來都像是直接被啓動了,而看不到啓動前的設置過程,看起來比較清爽。
我下載的源碼是Android9.0,發現MethodAndArgsCaller
方法已經再也不繼承Exception類了,而是僅實現了Runnable接口,同時ZygoteInit類的main方法也再也不經過catch Exception的方法運行。
我就很奇怪,難道再也不須要清除堆棧信息了嗎?
我按照Android9.0的代碼實現了一遍上述的調用過程,代碼以下:
Main2.java
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Main2 {
public static void main(String[] args) {
new Main2().b().run();
}
private Runnable b(){
return a();
}
private Runnable a() {
return findStaticMain("method_invoke.ClassTwo", new String[]{"111111"}, this.getClass().getClassLoader());
}
/**
* Invokes a static "main(argv[]) method on class "className".
* Converts various failing exceptions into RuntimeExceptions, with
* the assumption that they will then cause the VM instance to exit.
*
* @param className Fully-qualified class name
* @param argv Argument vector for main()
* @param classLoader the classLoader to load {@className} with
*/
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[]{String[].class});
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
return new MethodAndArgsCaller(m, argv);
}
/**
* Helper class which holds a method and arguments and can call them. This is used as part of
* a trampoline to get rid of the initial process setup stack frames.
*/
static class MethodAndArgsCaller implements Runnable {
/**
* method to call
*/
private final Method mMethod;
/**
* argument array
*/
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[]{mArgs});
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
}
複製代碼
ClassTwo.java
public class ClassTwo {
public static void main(String[] args) {
System.out.println(args[0]);
try {
// 製造除0異常
System.out.println(1/0);
} catch (InterruptedException e) {
// 輸出堆棧信息
e.printStackTrace();
}
}
}
複製代碼
發現其調用鏈信息一樣是被清除了的:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at method_invoke.ClassTwo.main(ClassTwo.java:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at method_invoke.Main2$MethodAndArgsCaller.run(Main2.java:93)
at method_invoke.Main2.main(Main2.java:10)
複製代碼
這沒有用什麼奇技淫巧,也沒有額外的堆棧信息,Android哪一個catch Exception的操做在搞什麼?
我這時覺得是Runnable接口有什麼魔力,而後本身寫了個接口,讓MethodAndArgsCaller
繼承,結果沒有什麼兩樣。
也就是說,將所須要的結果封裝成一個對象,最終返回到main方法,main方法中調用就能夠了--並不會有中間設置對象的堆棧信息被保留。