Java中关于StackTraceElement的使用

实际项目中有时候我们需要知道在执行某个方法时,它之前的调用链咋样的,尤其是涉及到多线程时,我们需要知道某个方法到底有没有被调用,除了常用的打印日志外,还可以使用StackTraceElement 类。

一、什么是StackTrace?

StackTrace(堆栈轨迹)可以认为是一系列方法调用过程的集合。

异常处理中常用的printStackTrace()即为打印异常调用的堆栈信息

二、StackTraceElement介绍

StackTraceElement表示StackTrace(堆栈轨迹)中的一个元素,属性包括方法调用者的类名、方法名、文件名以及调用的行数。

public final class StackTraceElement implements java.io.Serializable {  
    // Normally initialized by VM (public constructor added in 1.5)  
    private String declaringClass;  
    private String methodName;  
    private String fileName;  
    private int    lineNumber;  

}

StackTraceElement被定义为final,可见其作为一个java的基础类不允许被继承。

获取StackTraceElement的方法有两种,均返回StackTraceElement数组,也就是这个栈的信息。

  • Thread.currentThread().getStackTrace()

  • new Throwable().getStackTrace()

StackTraceElement数组包含了StackTrace(堆栈轨迹)的内容,通过遍历它可以得到方法间的调用过程,即可以得到当前方法以及其调用者的方法名、调用行数等信息。

public class TestClass {  
    public static void main(String[] args)  
    {
        new TestClass().methodA();
    }

    private void methodA(){
        System.out.println("------进入methodA----------");
        methodB();
    }

    private void methodB(){
        System.out.println("------进入methodB----------");
        StackTraceElement elements[] = Thread.currentThread().getStackTrace();
        for (int i = 0; i < elements.length; i++) {
            StackTraceElement stackTraceElement=elements[i];
            String className=stackTraceElement.getClassName();
            String methodName=stackTraceElement.getMethodName();
            String fileName=stackTraceElement.getFileName();
            int lineNumber=stackTraceElement.getLineNumber();
            System.out.println("StackTraceElement数组下标 i="+i+",fileName="
                    +fileName+",className="+className+",methodName="+methodName+",lineNumber="+lineNumber);
        }
    }
}

三、用途

  • 我们可以封装一个日志库,在打印目标日志的时候,也可以通过这个调用栈打印出这个日志所在的行数,这样就可以迅速的定位到日志输出行,再也不要全局搜索去查找了。
public static void d(String tag, String msg, Object... params) {
    StackTraceElement targetStackTraceElement = getTargetStackTraceElement();
    Log.d(tag, "(" + targetStackTraceElement.getFileName() + ":"
            + targetStackTraceElement.getLineNumber() + ")");
    Log.d(tag, String.format(msg, params));
}
  • 如果我们写了一个SDK,希望某个方法在固定的位置被调用,我们也可以在这个方法被调用的时候,进行检查,看这个方法的调用位置是否正确。
    例如,必须在Activity.onResume中执行,PVSdk.onResume,所以我们在调用PVSdk.onResume方法的时候,在PVSdk.onResume方法里面来通过获取调用栈的信息检测这个方法是否在Activity的onResume方法中调用的。
public class PVSdk {

    public static void onResume() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        boolean result = false;
        for (StackTraceElement stackTraceElement : stackTrace) {
            String methodName = stackTraceElement.getMethodName();
            String className = stackTraceElement.getClassName();
            try {
                boolean assignableFromClass = Class.forName(className).isAssignableFrom(Activity.class);
                if (assignableFromClass && "onResume".equals(methodName)) {
                    result = true;
                    break;
                }
            } catch (ClassNotFoundException e) {

            }
        }
        if (!result)
            throw new RuntimeException("PVSdk.onResume must in Activity.onResume");
    }
}

  • 我们在进行源码分析的时候,如果想分析整个代码的执行流程,我们可以进行通过打印栈的信息来获取,这个在源码分析的时候还是挺有用的。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值