先看下一段代碼:java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test1();
}
private void test1() {
test2();
}
private void test2() {
test3();
}
private void test3() {
test4();
}
private void test4() {
Log.d("wxl", "test", new Exception());
}
}
複製代碼
大家猜會打印什麼?android
12-29 12:16:05.479 8140-8140/com.wuxiaolong.myapplication D/wxl: test
java.lang.Exception
at com.wuxiaolong.myapplication.MainActivity.test4(MainActivity.java:29)
at com.wuxiaolong.myapplication.MainActivity.test3(MainActivity.java:25)
at com.wuxiaolong.myapplication.MainActivity.test2(MainActivity.java:21)
at com.wuxiaolong.myapplication.MainActivity.test1(MainActivity.java:17)
at com.wuxiaolong.myapplication.MainActivity.onCreate(MainActivity.java:13)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
複製代碼
是否是有點神奇,竟然把 test4() 方法調用關係給打印出來了,是怎麼作到的?是否是有地方把這個每步調用關係給保存下來了。app
首先進 Log 看下 Log.d():ide
public static int d(String tag, String msg, Throwable tr) {
return println(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
}
public static String getStackTraceString(Throwable tr) {
if (tr == null) {
return "";
}
// This is to reduce the amount of log spew that apps do in the non-error
// condition of the network being unavailable.
Throwable t = tr;
while (t != null) {
if (t instanceof UnknownHostException) {
return "";
}
t = t.getCause();
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
tr.printStackTrace(pw);
pw.flush();
return sw.toString();
}
複製代碼
這裏第三個參數 Exception 是 Throwable 子類,初始化看 Throwable 構造方法:oop
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
/** * Fills in the execution stack trace. This method records within this * {@code Throwable} object information about the current state of * the stack frames for the current thread. * * <p>If the stack trace of this {@code Throwable} {@linkplain * Throwable#Throwable(String, Throwable, boolean, boolean) is not * writable}, calling this method has no effect. * * @return a reference to this {@code Throwable} instance. * @see java.lang.Throwable#printStackTrace() */
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
backtrace = nativeFillInStackTrace();
stackTrace = libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
}
return this;
}
複製代碼
如本身所想,這裏 fillInStackTrace() ,正是一步一步地追蹤方法的調用,直到追蹤到線程的終端。fillInStackTrace 是 native 方法,就不往下跟了。this
Exception 表示能夠被程序捕獲並處理的異常錯誤,JVM 用方法調用棧來跟蹤每一個線程中一系列的方法調用過程,該棧保存了每一個調用方法的本地信息。spa
對於獨立的 Java 程序,能夠一直到該程序的 main 方法,當一個新方法被調用的時候,JVM 把描述該方法的棧結構置入棧頂,位於棧頂的方法爲正確執行的方法,當一個 Java 方法正常執行完畢,JVM 會從調用棧中彈處該方法的棧結構,而後繼續處理前一個方法,若是 java 方法在執行代碼的過程當中拋出異常,JVM 必須找到能捕獲異常的 catch 塊代碼,它首先查看當前方法是否存在這樣的 catch 代碼塊,若是存在就執行該 catch 代碼塊,不然 JVM 回調用棧中彈處該方法的棧結構,繼續到前一個方法中查找合適的 catch 代碼塊,最後若是 JVM 向上追到了 main()方法,也就是一直把異常拋給了 main()方法,仍然沒有找到該異常處理的代碼塊,該線程就會異常終止,若是該線程是主線程,應用程序也隨之終止,此時 JVM 將把異常直接拋給用戶,在用戶終端上會看到原始的異常信息。線程
個人公衆號:吳小龍同窗,歡迎關注交流,公號回覆關鍵字「1024」有驚喜哦。code