先看下一段代码: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