先看下一段代码:
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 + '
' + 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 将把异常直接抛给用户,在用户终端上会看到原始的异常信息。