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
兩個方法:
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字數限制,事實上就是超過字數限制後。採用分次列印的方法來列印,其它代碼不做分析,比較簡單。
三個方法
public class FileLog {
private static final String FILE_PREFIX = "KLog_";
private static final String FILE_FORMAT = ".log";
/**
* @param tag log tag
* @param targetDirectory log file save dir
* @param fileName log file name
* @param headString log file 檔案頭
* @param msg log 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 dic log file save dir
* @param fileName og file name
* @param msg log 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;
}
}
JsonLog就更簡單了,僅僅有一個方法(本寶寶曾經還以為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);// 調用格式化方法
}
}
public class XmlLog {
/**
* 列印xml
*
* @param tag log tag
* @param xml xml 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;
}
}
}
細心的你會發現。不管是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的基礎上對這個問題進行優化的。