目錄
Guide
本文基於https://gitee.com/andych008/timber_ohos 分析Timber的源碼,及移植到鴻蒙需要做的工作。
大神JakeWharton的Timber是我寫日志的最愛,幾乎在所有的項目中都用。當然一般我會通過Timber使用Logger,原因很簡單,因為Timber接口簡潔,Logger的輸出樣式好看。常規套路:
當然它的內部實現也一樣完美。咱們往下看。
原理
Timber英文翻譯為**“木材”**。靜態方法Timber.plant(Tree tree)
即種樹。每種一棵樹,就擁有一種日志能力。
比如樹A表示輸出日志到控制台,樹B表示輸出日志到文件,樹C輸出到網絡。
代碼實現上,Timber使用了外觀(facade)模式。
Tree類是外觀類,通過plant方法Timber持有Tree類的實例,Timber中的asTree、tag方法將它暴露出去,而對於調用者來說依賴的是抽象類Tree,而不是具體的Tree的實現,如果要更換或者添加Tree類實例,只需要調用plant等相關方法即可,所有調用者使用Tree對象的地方不需要做任何修改,這是符合面向對象依賴倒置原則的一個很好的體現。
另外也使用了委托(delegate)模式。Tree TREE_OF_SOULS
把所有的操作都委托給forestAsArray
。
更詳細的分析請移步
知識點
-
臨時tag的實現方法
很簡單,
Timber.tag("臨時tag").d(xxx);
設置臨時tag。使用一次就刪除。為了性能,使用
ThreadLocal
以空間換時間。
-
-
為什么要把List<Tree>轉成
Tree[]
數組?解釋這個問題可以參考 深度解析CopyOnWriteArrayList,線程安全的ArrayList!,從使用場景上看,Timber對於
List<Tree> FOREST
讀多寫少,所以只對寫操作加鎖,讀操作(遍歷時)不需要加鎖。其本質上也是讀寫分離的思想,和CopyOnWriteArrayList
類似,也是為了性能。 -
為什么要用
List.toArray(T[] a)
,而不是List.toArray()
?不推薦使用 toArray() 無參方法,此方法返回值只能是Object[]類,若強轉將出現ClassCastException錯誤。
-
移植到鴻蒙
如果Timber沒有默認提供DebugTree
,直接拿來就能在鴻蒙上使用。DebugTree
這棵樹的能力是在Logcat中輸出日志。所以移植要做的就是把android.util.Log
換成ohos.hiviewdfx.HiLog
。
HiLog
在tag的基礎上擴展了HiLogLabel
的概念。
label = new HiLogLabel(HiLog.DEBUG,0,tag);
如果每次都new一個label,太低效,所以這里可以優化。比如如果和上次一樣,就使用上次的。或者使用對象池技術。
關鍵代碼:
public static class DebugTree extends Tree { private final ThreadLocal<HiLogLabel> currentLabel = new ThreadLocal<>(); private final ThreadLocal<String> currentTag = new ThreadLocal<>(); @Override protected void log(int priority, String tag, @NotNull String message, Throwable t) { HiLogLabel label = getHiLogLabel(tag); if (message.length() < MAX_LOG_LENGTH) { if (priority == HiLog.FATAL) { HiLog.fatal(label,message); } else if (priority == HiLog.INFO){ HiLog.info(label, message); }else if (priority == HiLog.WARN){ HiLog.warn(label, message); }else if (priority == HiLog.ERROR){ HiLog.error(label, message); }else if (priority == HiLog.DEBUG){ HiLog.debug(label, message); } return; } // Split by line, then ensure each line can fit into Log's maximum length. for (int i = 0, length = message.length(); i < length; i++) { int newline = message.indexOf('\n', i); newline = newline != -1 ? newline : length; do { int end = Math.min(newline, i + MAX_LOG_LENGTH); String part = message.substring(i, end); if (priority == HiLog.FATAL) { HiLog.fatal(label,part); }else if (priority == HiLog.INFO){ HiLog.info(label, part); }else if (priority == HiLog.WARN){ HiLog.warn