Android KLog源代码分析

一身转战三千里,一剑曾百万师。这篇文章主要讲述Android KLog源代码分析相关的知识,希望能为你提供帮助。
android KLog源代码分析

    • Android KLog源代码分析
        • 代码结构
        • 详细分析
          • BaseLog
          • FileLog
          • JsonLog
          • XmlLog
        • 核心文件KLogjava分析
        • 遇到的问题

一直使用这个库。但没有细致研究。今天就来研究一下。该库的地址:
KLog,在这里先感谢下作者。棒棒哒!
代码结构整个代码的结构非常easy。例如以下:
library klog BaseLog.java FileLog.java JsonLog.java XmlLog.java KLog.java KLogUtil.java

共六个文件:
  • BaseLog.java,支持主要的log打印
  • FileLog.java。支持以文件的形式保存log
  • JsonLog.java,支持打印json对象和json数组
  • XmlLog.java,支持打印Xml形式的log
详细分析BaseLog两个方法:
public class BaseLog {private static final int MAX_LENGTH = 4000; public static void printDefault(int type, String tag, String msg) {int index = 0; int length = msg.length(); int countOfSub = length / MAX_LENGTH; if (countOfSub > 0) {// 超过指定长度,粉刺打印。这样就避免了系统默认的log长度限制了 for (int i = 0; i < countOfSub; i++) { String sub = msg.substring(index, index + MAX_LENGTH); printSub(type, tag, sub); index += MAX_LENGTH; } printSub(type, tag, msg.substring(index, length)); // 打印余数部分 } else { printSub(type, tag, msg); } }private static void printSub(int type, String tag, String sub) { switch (type) { case KLog.V: Log.v(tag, sub); break; case KLog.D: Log.d(tag, sub); break; case KLog.I: Log.i(tag, sub); break; case KLog.W: Log.w(tag, sub); break; case KLog.E: Log.e(tag, sub); break; case KLog.A: Log.wtf(tag, sub); break; } }}

这里突破了android系统的log字数限制,事实上就是超过字数限制后。採用分次打印的方法来打印,其它代码不做分析,比較简单。

FileLog三个方法
public class FileLog {private static final String FILE_PREFIX = "KLog_"; private static final String FILE_FORMAT = ".log"; /** * @param taglog tag * @param targetDirectory log file save dir * @param fileNamelog file name * @param headStringlog file 文件头 * @param msglog file log内容主体 */ public static void printFile(String tag, File targetDirectory, @Nullable String fileName, String headString, String msg) {fileName = (fileName == null) ? getFileName() : fileName; if (save(targetDirectory, fileName, msg)) { Log.d(tag, headString + " save log success ! location is > > > " + targetDirectory.getAbsolutePath() + "/" + fileName); } else { Log.e(tag, headString + "save log fails !"); } }/** * @param diclog file save dir * @param fileName og file name * @param msglog file log内容主体 * @return true if save success */ private static boolean save(File dic, @NonNull String fileName, String msg) {File file = new File(dic, fileName); try { OutputStream outputStream = new FileOutputStream(file); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8"); outputStreamWriter.write(msg); outputStreamWriter.flush(); outputStream.close(); return true; } catch (FileNotFoundException e) { e.printStackTrace(); return false; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return false; } catch (IOException e) { e.printStackTrace(); return false; } catch (Exception e) { e.printStackTrace(); return false; }}/** * 当未设置文件名称时,随机生成一个文件名称 * * @return default file name */ private static String getFileName() { Random random = new Random(); return FILE_PREFIX + Long.toString(System.currentTimeMillis() + random.nextInt(10000)).substring(4) + FILE_FORMAT; }}

JsonLogJsonLog就更简单了,仅仅有一个方法(本宝宝曾经还以为Json打印会非常麻烦呢)
public class JsonLog {public static void printJson(String tag, String msg, String headString) {String message; try { if (msg.startsWith("{")) {// 处理json对象 JSONObject jsonObject = new JSONObject(msg); message = jsonObject.toString(KLog.JSON_INDENT); } else if (msg.startsWith("[")) {// 处理json数组 JSONArray jsonArray = new JSONArray(msg); message = jsonArray.toString(KLog.JSON_INDENT); } else { message = msg; } } catch (JSONException e) { message = msg; }KLogUtil.printLine(tag, true); // 调用格式化方法 message = headString + KLog.LINE_SEPARATOR + message; String[] lines = message.split(KLog.LINE_SEPARATOR); for (String line : lines) { Log.d(tag, "║ " + line); } KLogUtil.printLine(tag, false); // 调用格式化方法 } }

XmlLog两个方法:
public class XmlLog { /** * 打印xml * * @param taglog tag * @param xmlxml content * @param headString 文件头 */ public static void printXml(String tag, String xml, String headString) {if (xml != null) { xml = XmlLog.formatXML(xml); xml = headString + "\n" + xml; } else { xml = headString + KLog.NULL_TIPS; }KLogUtil.printLine(tag, true); String[] lines = xml.split(KLog.LINE_SEPARATOR); for (String line : lines) { if (!KLogUtil.isEmpty(line)) { Log.d(tag, "║ " + line); } } KLogUtil.printLine(tag, false); }/** * @param inputXML xml content * @return 格式化后的xml String */ private static String formatXML(String inputXML) { try { Source xmlInput = new StreamSource(new StringReader(inputXML)); StreamResult xmlOutput = new StreamResult(new StringWriter()); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString().replaceFirst("> ", "> \n"); } catch (Exception e) { e.printStackTrace(); return inputXML; } }}

【Android KLog源代码分析】细心的你会发现。不管是Json形式还是XML形式。调用的都是系统原生的方法来处理数据,因此又时候对熟悉熟悉java(或android)提供的方法还是非常方便的。哈哈。

上面用到的KLogUtil,例如以下:
public class KLogUtil {public static boolean isEmpty(String line) { return TextUtils.isEmpty(line) || line.equals("\n") || line.equals("\t") || TextUtils.isEmpty(line.trim()); }public static void printLine(String tag, boolean isTop) { if (isTop) { Log.d(tag, "╔═══════════════════════════════════════════════════════════════════════════════════════"); } else { Log.d(tag, "╚═══════════════════════════════════════════════════════════════════════════════════════"); } }}

核心文件KLog.java分析给方法主要提供了相似于系统log方法的不同重载,比較简单,我这里要讲的是那个获取log详细有关的类名、方法名、行号等。与之相关的就是wrapperContent这种方法了。
private static String[] wrapperContent(int stackTraceIndex, String tagStr, Object... objects) {StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); StackTraceElement targetElement = stackTrace[stackTraceIndex]; String className = targetElement.getClassName(); // 得到类名 String[] classNameInfo = className.split("\\."); // 第一种形式 if (classNameInfo.length > 0) { className = classNameInfo[classNameInfo.length - 1] + SUFFIX; }if (className.contains("$")) {//另外一种形式 className = className.split("\\$")[0] + SUFFIX; }String methodName = targetElement.getMethodName(); //得到方法名 int lineNumber = targetElement.getLineNumber(); //得到所在的行号if (lineNumber < 0) { lineNumber = 0; }String tag = (tagStr == null ? className : tagStr); if (mIsGlobalTagEmpty & & TextUtils.isEmpty(tag)) { tag = TAG_DEFAULT; } else if (!mIsGlobalTagEmpty) { tag = mGlobalTag; } // 得到消息主体 String msg = (objects == null) ? NULL_TIPS : getObjectsString(objects); String headString = "[ (" + className + ":" + lineNumber + ")#" + methodName + " ] "; return new String[]{tag, msg, headString}; }/** * 得到消息主体 * * @param objects 消息对象数组 * @return 消息主体字符串 */ private static String getObjectsString(Object... objects) {if (objects.length > 1) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("\n"); for (int i = 0; i < objects.length; i++) { Object object = objects[i]; if (object == null) { stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ").append(NULL).append("\n"); } else { stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ").append(object.toString()).append("\n"); } } return stringBuilder.toString(); } else { Object object = objects[0]; return object == null ?
NULL : object.toString(); } }

分析完成,是不是非常easy,假设你看了这个库的代码。你会认为我的分析都是多余的。
遇到的问题在使用KLog打印json形式信息时。假设网络请求时异步的,会导致KLog.json打印的格式出现错乱。即一个结果还没有全然打印出来。里外一个就開始打印了,这个应该是并发导致的问题,之后我会在KLog的基础上对这个问题进行优化的。


    推荐阅读