LogUtil,这个单词,相信很多做android开发的同学并不陌生。毕竟,系统的Log相对来说功能过于简单,而且管理起来也很是麻烦,所以,一般我们都会自己封装一个Utli用来管理Log。
那么今天,为大家推荐一个新的LogUtli工具类,希望能喜欢。
可以看到,控制台输出的Log信息直接对应到了哪个类的哪个方法哪一行。
看到这样的Log信息,是不是对于我们调试代码,更加的方便呢?
那么,接下来就介绍这个类的实现吧。
首先,我们需要先了解一下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);
}
我们可以看到,数组下标为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);
下标为2的StackTraceElement的信息变成了Test类下getInfo的信息
而下标为3StackTraceElement的信息变成了MainActivity下onCreat信息。
那么这是为什么呢?在经过多次测试之后,作者发现,Thread.currentThread().getStackTrace()的
StackTraceElement入栈应该是由从最里层的方法开始加入的。
而前两行,则是固定调用的VM和Thread的方法。
而 getInfo在onCreat内执行,这样就可以理解为什么,onCreate方法由原先的下标2变成了下标3了。
大致明白了Thread.currentThread().getStackTrace()的排列方法,那么我们就可以开始构造我们的LogUtil了。
首先我们先分析一下我们的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就更好啦~~