1. 程式人生 > >android 程式設計小技巧(1)---超實用的LogUtil

android 程式設計小技巧(1)---超實用的LogUtil

LogUtil,這個單詞,相信很多做android開發的同學並不陌生。畢竟,系統的Log相對來說功能過於簡單,而且管理起來也很是麻煩,所以,一般我們都會自己封裝一個Utli用來管理Log。
那麼今天,為大家推薦一個新的LogUtli工具類,希望能喜歡。

好啦,先上效果圖片:
這裡寫圖片描述

這裡寫圖片描述

可以看到,控制檯輸出的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);
    }

進行執行,我們可以看到如下效果:

我們可以看到,陣列下標為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了。

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就更好啦~~