先看下一段代碼:
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());
}
}
你們猜會打印什么?
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() 方法調用關系給打印出來了,是怎么做到的?是不是有地方把這個每步調用關系給保存下來了。
首先進 Log 看下 Log.d():
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 構造方法:
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 方法,就不往下跟了。
Exception 表示可以被程序捕獲並處理的異常錯誤,JVM 用方法調用棧來跟蹤每個線程中一系列的方法調用過程,該棧保存了每個調用方法的本地信息。
對於獨立的 Java 程序,可以一直到該程序的 main 方法,當一個新方法被調用的時候,JVM 把描述該方法的棧結構置入棧頂,位於棧頂的方法為正確執行的方法,當一個 Java 方法正常執行完畢,JVM 會從調用棧中彈處該方法的棧結構,然后繼續處理前一個方法,如果 java 方法在執行代碼的過程中拋出異常,JVM 必須找到能捕獲異常的 catch 塊代碼,它首先查看當前方法是否存在這樣的 catch 代碼塊,如果存在就執行該 catch 代碼塊,否則 JVM 回調用棧中彈處該方法的棧結構,繼續到前一個方法中查找合適的 catch 代碼塊,最后如果 JVM 向上追到了 main()方法,也就是一直把異常拋給了 main()方法,仍然沒有找到該異常處理的代碼塊,該線程就會異常終止,如果該線程是主線程,應用程序也隨之終止,此時 JVM 將把異常直接拋給用戶,在用戶終端上會看到原始的異常信息。
我的公眾號:吳小龍同學,歡迎關注交流,公號回復關鍵字「1024」有驚喜哦。