Android开发不喜欢使用IDE提供Debug方式来调试,有着强大的Log输出机制为什么不用呢,比如我们会经常遇到一个方法被几十个地方调用,而出现问题就在于某个时刻有些地方需要调用该方法,有些地方不需要调用,如何能找到不该调用的地方却调用这个方法呢,我们平时程序崩溃的时候很容易找到出错点,根据错误栈信息往前还能找到调用该方法的地方,利用这一点我们可以打印一个错误栈
public void methodA() {
Log.e(TAG, null, new Exception("谁调用了该方法"));
//TODO 后面的逻辑
}
其实Android有一个开源项目Logger,用来输出日志,并且能显示日志输出所在的具体类具体行号,查看它的源码可以看出实现原理是这样的
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
利用这句可以输出栈的调用结构,然后筛选出主要的
StackTraceElement的API可知能获取这些信息,getClassName() getMethodName() getFileName() getLineNumber()
项目中如果不引用Logger,我们可以自己模仿着写一个这样的日志输出类,以下是我的实现:
import android.util.Log; import java.io.PrintWriter; import java.io.StringWriter; import java.net.UnknownHostException; public class LogPrint { private final static int LEVEL_VERBOSE = 0; private final static int LEVEL_DEBUG = 1; private final static int LEVEL_INFO = 2; private final static int LEVEL_WARN = 3; private final static int LEVEL_ERROR = 4; private static boolean sDebugable = true; private static String sTag = "MyTAG"; public static void setTag(String tag) { sTag = tag; } public static void setDebugable(boolean value) { sDebugable = value; } public static void v(String msg) { if (sDebugable) { log(LEVEL_VERBOSE, msg, null); } } public static void d(String msg) { if (sDebugable) { log(LEVEL_DEBUG, msg, null); } } public static void i(String msg) { if (sDebugable) { log(LEVEL_INFO, msg, null); } } public static void w(String msg) { if (sDebugable) { log(LEVEL_WARN, msg, null); } } public static void e(String msg) { if (sDebugable) { log(LEVEL_ERROR, msg, null); } } public static void e(String msg, Throwable tw) { if (sDebugable) { log(LEVEL_ERROR, msg, tw); } } private synchronized static void log(int level, String msg, Throwable tw) { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); if (trace == null) { return; } int index = getIndex(trace); printMsg(level, "╔════════════════════════════════════════════════════════════════════════════════════════"); for (int i = index; i < index + 2; i++) { if (i >= trace.length) { break; } StackTraceElement element = trace[i]; printMsg(level, "║ " + getSimpleClassName(element.getClassName()) + "." + element.getMethodName() + " (" + element.getFileName() + ":" + element.getLineNumber() + ")"); } printMsg(level, "╟────────────────────────────────────────────────────────────────────────────────────────"); if (tw != null && msg != null) { msg += " : " + getStackTraceString(tw); } if (tw != null && msg == null) { msg = getStackTraceString(tw); } if (msg == null) { msg = "No message/exception is set"; } byte[] byteData = msg.getBytes(); int lenght = byteData.length; for (int i = 0; i < lenght; i += 2000) { int count = Math.min(lenght - i, 2000); String chunk = new String(byteData, i, count); String[] lines = chunk.split(System.getProperty("line.separator")); for (String line: lines) { printMsg(level, "║ " + line); } } printMsg(level, "╚════════════════════════════════════════════════════════════════════════════════════════"); } private static int getIndex(StackTraceElement[] trace) { if (trace == null) { return 0; } int len = trace.length; for (int i = len - 1; i >= 0; i--) { StackTraceElement element = trace[i]; if (LogPrint.class.getName().equals(element.getClassName())) { return i + 1; } } return 0; } private static void printMsg(int level, String msg) { switch (level) { case LEVEL_VERBOSE: Log.v(sTag, msg); break; case LEVEL_DEBUG: Log.d(sTag, msg); break; case LEVEL_INFO: Log.i(sTag, msg); break; case LEVEL_WARN: Log.w(sTag, msg); break; case LEVEL_ERROR: Log.e(sTag, msg); break; } } private static String getSimpleClassName(String name) { int lastIndex = name.lastIndexOf("."); return name.substring(lastIndex + 1); } private static String getStackTraceString(Throwable tr) { if (tr == null) { return ""; } // This is to reduce the amount of log spew that apps do in the non-error // condition of the network being unavailable. Throwable t = tr; while (t != null) { if (t instanceof UnknownHostException) { return ""; } t = t.getCause(); } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); tr.printStackTrace(pw); pw.flush(); return sw.toString(); } }