Java:如何获取调用者函数名称

时间:2010-10-31 22:36:57

标签: java unit-testing

要修复测试用例,我需要确定是否从特定的调用函数调用该函数。我无法承受添加布尔参数,因为它会破坏定义的接口。如何解决这个问题?

这就是我想要实现的目标。这里我不能改变operation()的参数,因为它是一个接口实现。

operation()
{
   if not called from performancetest() method
       do expensive bookkeeping operation
   ...       

}

8 个答案:

答案 0 :(得分:56)

你可以尝试

StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
String methodName = e.getMethodName();

答案 1 :(得分:1)

答案 2 :(得分:1)

这是我写的一个函数,用于记录调用它的函数的函数名。它会运行堆栈跟踪,直到找到名为logIt的函数,然后显示下一个名称。这是一个肮脏的黑客,所以除非你用它来调试,否则不要这样做。

private static void logIt() {
    StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
    boolean logged = false;
    boolean foundMe = false;
    for(int i=0; i<stacktrace.length; i++) {
        StackTraceElement e = stacktrace[i];
        String methodName = e.getMethodName();
        if (foundMe) {
            if (!methodName.startsWith("access$")) {
                Log.i(TAG, String.format(Locale.US, "%s.%s", e.getClassName(), methodName));
                logged = true;
                break;
            }
        } else {
            if (methodName.equals("logIt")) {
                foundMe = true;
            }
        }
    }
    if (!logged)
        Log.e(TAG, "unlogged call");
}

答案 3 :(得分:0)

另一个Android使用示例:

//package your.package.name;
import android.util.Log;
/*
 File name: MyDebugLog.java
*/
public class MyDebugLog {
    private static final int    index      = 4;     // <== Index in call stack array
    private static final String methodName = "Log"; // <== Name of method for public call
    private static String getCallerName() {
        String caller = "NONE";
        final StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        for (int i = 0; i < stacktrace.length; i++) {
            Log.e("Method ", "[" + i + "]" + stacktrace[i].getMethodName());
        }
        if (stacktrace.length >= index){
            caller = stacktrace[index].getMethodName();
        }
        return caller;
    }

    private static String getTag() {
        String tag = "NONE";
        final StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        for (int i = 0; i < stacktrace.length; i++) {
            Log.e("Method ", "[" + i + "]" + stacktrace[i].getMethodName());
            if (stacktrace[i].getMethodName().equals(methodName)) {
                tag = "("+stacktrace[i + 1].getFileName() + ":" + stacktrace[i + 1].getLineNumber()+")";
                return tag;
            }
        }
        return tag;
    }

    public static void Log(String message){
        Log.v(getTag(), getCallerName() + " " + message);
    }
}

用法:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.sample_main);
            MyDebugLog.Log("XXXXX");
    }

输出:

    V/(MainActivity.java:117): onCreate XXXXX

数组样本:

 getTag Sample of stacktace array:

    Method: [0]getThreadStackTrace
    Method: [1]getStackTrace
    Method: [2]getTag
    Method: [3]Log                 <== Method for external call
    ...
 getName Sample of stacktace array:
    Method: [0]getThreadStackTrace
    Method: [1]getStackTrace
    Method: [2]getCallerName
    Method: [3]Log
    Method: [4]onCreate            <== Our external method
    Method: [5]performCreate
    ...

答案 4 :(得分:0)

我调整了这里讨论的代码并对其进行了自定义以获取调用方法。这里的代码是迭代堆栈跟踪元素,一旦找到被调用方法的名称,它就会得到前一个方法的名称,而后者又是调用此方法的方法。

    private String method() {
    String methodName=null;
    StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
    for (int i = 0; i < stacktrace.length; i++) {
        if(stacktrace[i].getMethodName().equals("method")) {
            methodName = stacktrace[i+1].getMethodName();
            break;
        }
    }
      return methodName;

}

答案 5 :(得分:0)

有时我想向logcat输出一些信息。所以我写了一个带有一些测试方法的小类:

public class Common {

    // can be used as test-flag anywhere in the app (set to false, when release the app)
    public static boolean b_TEST_MODE = true;

    public static void echo(String message) {
        if (b_TEST_MODE) {
            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
            // subtring(25) is to cut off app-name from output
            System.out.println(">>> " + stackTraceElements[3].toString().substring(25) + ": " + message);
        }
    }
}

现在您可以从应用程序中的任何位置调用它以获取一些信息:

String sSQLQuery = "SELECT * FROM database WHERE id=23";
Common.echo(sSQLQuery);

logcat打印出来:

>>> MainActivity.onCreate(MainActivity.java:46): SELECT * FROM dateabase WHERE id=23

答案 6 :(得分:0)

我不知道为什么,但是在我的商店中,开发系统与测试和生产环境在堆栈中的位置不同。我被迫遍历堆栈以从堆栈跟踪中的下一个元素中查找并获取调用方法。有点笨拙,但到目前为止一直在不断返回所需的方法。我将其用作错误处理的一部分,以识别在何处捕获到异常。

    List<String> list = new ArrayList<String>();
    StackTraceElement[] elements = Thread.currentThread().getStackTrace();
    for (int i = 0; i < Thread.currentThread().getStackTrace().length; i++) {
        System.out.println("Stack: "
                + i
                + " Class: "
                + elements[i].getClassName()
                + " Method: "
                + elements[i].getMethodName());
        if (elements[i].getMethodName().equals("<init>")) {
            list.add(elements[i + 1].getClassName());
            list.add(elements[i + 1].getMethodName());
             break;
        } // if
    } // for

答案 7 :(得分:0)

这里的代码更现代(在 Java 9+ 中可用)并且性能更好。

private static String getCallerMethodName()
{
   return StackWalker.
      getInstance().
      walk(stream -> stream.skip(1).findFirst().get()).
      getMethodName();
}

根据需要将 skip(1) 更改为更大的数字以在堆栈中更高。

这比 Thread.currentThread().getStackTrace() 性能更好,因为它不会遍历整个堆栈并分配所有堆栈帧。它只在堆栈上走两帧。

该方法可以修改为返回 StackWalker.StackFrame,其中包含有关该方法的大量信息。