android 编程小技巧(1)---超实用的LogUtil

LogUtil,这个单词,相信很多做android开发的同学并不陌生。毕竟,系统的Log相对来说功能过于简单,而且管理起来也很是麻烦,所以,一般我们都会自己封装一个Utli用来管理Log。
那么今天,为大家推荐一个新的LogUtli工具类,希望能喜欢。

好啦,先上效果图片:
这里写图片描述

android 编程小技巧(1)---超实用的LogUtil_第1张图片

可以看到,控制台输出的Log信息直接对应到了哪个类的哪个方法哪一行。

看到这样的Log信息,是不是对于我们调试代码,更加的方便呢?

那么,接下来就介绍这个类的实现吧。

1.StackTraceElement

首先,我们需要先了解一下StackTraceElement这个类,它是由java.lang提供的,主要作用是获取方法的调用栈信息。
可以通过Thread.currentThread().getStackTrace()可以获得一个StackTraceElement的数组,里面包含了线程中执行调用了哪些方法。
我们可以通过代码来检查一下这个数组的一些信息。

首先,新建一个项目,xml文件如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="quietuncle.logutli.MainActivity">
    <ScrollView
        android:layout_weight="10"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    ScrollView>
    <Button
        android:onClick="sendLog"
        android:id="@+id/sendLog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送Log信息" />
LinearLayout>

然后,在activity里执行如下方法。

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView content= (TextView) findViewById(R.id.content);
        String info="";
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        info+="stacktrace len :"+stacktrace.length;

        for (int i = 0; i < stacktrace.length; i++) {
            info+="\n\t"+"----  the " + i + " element  ----";
            info+="\n\t"+"toString: " + stacktrace[i].toString();
            info+="\n\t"+"ClassName: " + stacktrace[i].getClassName();
            info+="\n\t"+"FileName: " + stacktrace[i].getFileName();
            info+="\n\t"+"LineNumber: " + stacktrace[i].getLineNumber();
            info+="\n\t"+"MethodName: " + stacktrace[i].getMethodName();
        }
        content.setText(info);
    }

进行运行,我们可以看到如下效果:
android 编程小技巧(1)---超实用的LogUtil_第2张图片

我们可以看到,数组下标为2的对象,打印出了我们想要的信息,

  ClassName:quietuncle.logutli.MainActivity
  FileName: MainActivity.java
  LineNumber:16
  MethodName:onCreat

那么,这是否意味着我们可以开始构建我们的LogUtil了呢。

为了检验下标为2的是否是我们想要的信息,我们新建一个测试类,然后执行该方法。

public class Test {
    public static String getInfo(){
        String info="";
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        info+="stacktrace len :"+stacktrace.length;
        for (int i = 0; i < stacktrace.length; i++) {
            info+="\n\t"+"----  the " + i + " element  ----";
            info+="\n\t"+"toString: " + stacktrace[i].toString();
            info+="\n\t"+"ClassName: " + stacktrace[i].getClassName();
            info+="\n\t"+"FileName: " + stacktrace[i].getFileName();
            info+="\n\t"+"LineNumber: " + stacktrace[i].getLineNumber();
            info+="\n\t"+"MethodName: " + stacktrace[i].getMethodName();
        }
        return  getInfo();
    }
}

然后在MainActivity的onCreate方法中调用

     String info = Test.getInfo();
     content.setText(info);

然后我们再查看显示内容,会发现如下:
android 编程小技巧(1)---超实用的LogUtil_第3张图片

下标为2的StackTraceElement的信息变成了Test类下getInfo的信息
而下标为3StackTraceElement的信息变成了MainActivity下onCreat信息。

那么这是为什么呢?在经过多次测试之后,作者发现,Thread.currentThread().getStackTrace()的
StackTraceElement入栈应该是由从最里层的方法开始加入的。

而前两行,则是固定调用的VM和Thread的方法。

而 getInfo在onCreat内执行,这样就可以理解为什么,onCreate方法由原先的下标2变成了下标3了。

大致明白了Thread.currentThread().getStackTrace()的排列方法,那么我们就可以开始构造我们的LogUtil了。

2.构造LogUtli

首先我们先分析一下我们的log的层级:

 I/QT:MainActivity.sendLog(Line:32): info

可以看出

 QT:MainActivity.sendLog(Line:32) 这一部分是属于TAG部分。

这里我们可以拆分一下:

             QT  :  tag的前缀,自定义  
   MainActivity  :  log所在类名      对应方法  StackTraceElement.getClassName();
        sendLog  :  打印log的方法    对应方法: StackTraceElement.getMethodName();
        line:32  :  log所在行数     对应方法: StackTraceElement.getLineNumber();

这下,我们可以正式编写我们的LogUtil了。

这里就直接贴上代码了。

/**
 * 作者: quietUncle on 2016/2/26
 * 一个Log工具类
 * 输出格式:
 *  tagPrefix :className.methodName(Line:lineNumber),
 *  tagPrefix 为空时只输出:className.methodName(Line:lineNumber)。
 */
public class QTLog {
    public static String tagPrefix = "QT";//log前缀
    public static boolean debug = true;

    public static void d(Object o) {
        logger("d", o);
    }
    public static void e(Object o) {
        logger("e", o);
    }
    public static void i(Object o) {
        logger("i", o);
    }
    public static void w(Object o) {
        logger("w", o);
    }

    /**
     *
     * @param type logger级别
     * @param o   logger内容

     */
    private static void logger(String type, Object o) {
        if (!debug) {
            return;
        }
        String msg=o+"";
        String tag = getTag(getCallerStackTraceElement());
        switch (type){
            case  "i":
                Log.i(tag,msg);
            case  "d":
                Log.d(tag,msg);
                break;
            case  "e":
                Log.e(tag,msg);
                break;
            case  "w":
                Log.w(tag,msg);
                break;
        }
    }


    private static String getTag(StackTraceElement element) {
        String tag = "%s.%s(Line:%d)"; // 占位符
        String callerClazzName = element.getClassName(); // 获取到类名

        callerClazzName = callerClazzName.substring(callerClazzName
                .lastIndexOf(".") + 1);
        tag = String.format(tag, callerClazzName, element.getMethodName(),
                element.getLineNumber()); // 替换
        tag = TextUtils.isEmpty(tagPrefix) ? tag : tagPrefix + ":"
                + tag;
        return tag;
    }

    /**
     * 获取线程状态
     * @return
     */
    private static StackTraceElement getCallerStackTraceElement() {
        return Thread.currentThread().getStackTrace()[5];
    }
}

这里主要说一下Thread.currentThread().getStackTrace()[5]; 为什么用5

根据第一节的推论,我们可以知道0,1是vm,和thread调用的方法,从2开始才是我们调用的方法

Thread.currentThread().getStackTrace()[5]所在的getCallerStackTraceElement方法是在logger()中调用的,而logger是在public static void d/i/e/w 中调用。

我们要获取的是调用TQLog.i/w/e/d() 所在的类的信息。

那么,我们可以推论一下。

vm->thread->getCallerStackTraceElement->logger->i/e/w/d->目标层
0      1               2                  3        4      5

所以这里使用Thread.currentThread().getStackTrace()[5];

其他部分相对比较简单,大家可以自行理解一下。

demo已上传github,有意可点击下载

另:博客会每周更新,带来一些关于android的理解,如果喜欢可以关注一下,所有demo均会上传到github,有意关注一下github,赏个star就更好啦~~

你可能感兴趣的:(编程小技巧系列)