缘起

开发过程中经常需要获取当前正在执行的方法名,以前搜过,只是简单的知道可以调用Thread.currentThread().getStackTrace()来获取。就点到为止了。这次需要获取调用当前方法的方法的方法名(有点绕,我信你能看明白),所以详细跟了下。

探究竟

Thread.currentThread().getStackTrace()返回的是一个StackTraceElement数组,内容为调用函数堆栈,并且以调用层级关系保存。由于不记得以前用的时候使用的数组下标,想当然的认为Android和纯Java的层级应该是一样的。所以直接取下标为2(StackTraceElement[2])当成是调用当前方法的方法。后来调试了下才发现问题。调用当前方法的方法在Android上数组下标为3,纯Java的数组下标为2。

出现这个问题是因为Android的java.lang.Thread.getStackTrace方法实际上是调用了Android的VMStack.getThreadStackTrace(this):

/**
     * Returns an array of {@link StackTraceElement} representing the current thread's stack.
     */
    public StackTraceElement[] getStackTrace() {
        StackTraceElement ste[] = VMStack.getThreadStackTrace(this);
        return ste != null ? ste : EmptyArray.STACK_TRACE_ELEMENT;
    }

这里不再往深入里跟下去了(好吧,其实是我跟不下去了),贴出调试过程中打印的函数调用堆栈信息,可以扫眼函数的调用层级关系:

JAVA版本:

[0]java.lang.Thread.getStackTrace(Thread.java:1518)
[1]com.test.TestDemo.testMethod(TestDemo.java:34)
[2]com.test.TestDemo.testMethod1(TestDemo.java:29)
[3]sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[4]sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[5]sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[6]java.lang.reflect.Method.invoke(Method.java:597)
[7]org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
[8]org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
[9]org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
[10]org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
[11]org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
[12]org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
[13]org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
[14]org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
[15]org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
[16]org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
[17]org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
[18]org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
[19]org.junit.runners.ParentRunner.run(ParentRunner.java:309)
[20]org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
[21]org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
[22]org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
[23]org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
[24]org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
[25]org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Android版本:

[0]dalvik.system.VMStack.getThreadStackTrace(Native Method)
[1]java.lang.Thread.getStackTrace(Thread.java:579)
[2]com.demo.android.annotation.view.EventListener.invokeMethod(EventListener.java:60)
[3]com.demo.android.annotation.view.EventListener.onClick(EventListener.java:52)
[4]android.view.View.performClick(View.java:4475)
[5]android.view.View$PerformClick.run(View.java:18786)
[6]android.os.Handler.handleCallback(Handler.java:730)
[7]android.os.Handler.dispatchMessage(Handler.java:92)
[8]android.os.Looper.loop(Looper.java:137)
[9]android.app.ActivityThread.main(ActivityThread.java:5493)
[10]java.lang.reflect.Method.invokeNative(Native Method)
[11]java.lang.reflect.Method.invoke(Method.java:525)
[12]com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209)
[13]com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025)
[14]dalvik.system.NativeStart.main(Native Method)
[0]dalvik.system.VMStack.getThreadStackTrace(Native Method)
[1]java.lang.Thread.getStackTrace(Thread.java:579)
[2]com.demo.android.annotation.view.EventListener.invokeMethod(EventListener.java:60)
[3]com.demo.android.annotation.view.EventListener.onClick(EventListener.java:52)
[4]java.lang.reflect.Method.invokeNative(Native Method)
[5]java.lang.reflect.Method.invoke(Method.java:525)
[6]com.demo.android.annotation.view.EventListener.invokeMethod(EventListener.java:71)
[7]com.demo.android.annotation.view.EventListener.onClick(EventListener.java:52)
[8]android.view.View.performClick(View.java:4475)
[9]android.view.View$PerformClick.run(View.java:18786)
[10]android.os.Handler.handleCallback(Handler.java:730)
[11]android.os.Handler.dispatchMessage(Handler.java:92)
[12]android.os.Looper.loop(Looper.java:137)
[13]android.app.ActivityThread.main(ActivityThread.java:5493)
[14]java.lang.reflect.Method.invokeNative(Native Method)
[15]java.lang.reflect.Method.invoke(Method.java:525)
[16]com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209)
[17]com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025)
[18]dalvik.system.NativeStart.main(Native Method)

从打印Android版本的信息看到,所有调用都源自dalvik.system.NativeStart.main(Native Method),有兴趣可以深跟下。

额外赠送

写着写着想起以前看一个开源项目的源码打印log的部分时遇到过这个问题(早干嘛去了,再次鄙视自己的狗屎的记性!),还提了个issue,只是没人理。。详见https://github.com/roboguice/roboguice/issues/136和<https://github.com/laomo/android-tools/blob/master/Ln/src/roboguice/util/LnImpl.java# L222>

其实,日志打印工具类的实现都依赖于Thread.currentThread().getStackTrace()来获取相关信息,所以想自己写一个LogUtils,可以参考下。