這可能是最好的 Android/Kotlin日誌輸出方法
- 2020 年 1 月 23 日
- 筆記
在編程調試和定位問題的時候,日誌是一個最常用的工具。比如輸出一些信息,確定執行軌跡。今天我們這裡簡單聊一聊打印日誌的一些分析。
通常,我們進行日誌輸出的時候都會限定在debug包下執行,對於非debug包,我們就不輸出日誌。那麼如果是非debug,不同的日誌輸出方式可能存在一定的性能問題,本文將通過幾個版本來對比着方面的差異。
原始版
這可能是最原始的版本打印日誌了,判斷是否是debug,然後決定是否輸出日誌
| 1 2 3 4 5 6 7 8 9 | fun debugLog(message: String?) { if (BuildConfig.DEBUG) { Log.d("debugLog", message) } } private fun testDebugLog() { debugLog("getProperties " + getProperties()?.joinToString()) } | 
|---|
上面的問題
- testDebugLog需要執行- getProperties(),這一步的性能不可預知
- testDebugLog內部存在字符串拼接
- 如果拼接內容複雜,比如一個龐大的Object,會造成一定的開銷
- 綜上所述,該實現如果在非Debug條件下存在一定的運行時開銷
不拼接的版本
既然拼接會導致一些問題,那麼下面的版本採用(調用處)不拼接的形式
| 1 2 3 4 5 6 7 8 9 | fun debugMessage(vararg args: Any?) { if (BuildConfig.DEBUG) { Log.d("debugMessage", args.joinToString()) } } private fun testDebugMessage() { debugMessage("getProperties", getProperties()) } | 
|---|
- 仍然需要執行 getProperties(),這一步的性能不可預知
- 上面的代碼使用了可變參數的形式處理message信息
- 而可變參數內部實際採用了數組的形式,也就是上面的代碼會在運行時生成一個數組,一個元素是getProperties,另一個元素是getProperties()的內容
- 這個版本相對第一個版本要好一些(以極端情況看),但是在非Debug條件下仍然存在一定的運行時開銷,不完美。
相對最完美的版本
這個版本是相對最好的實現,規避了非Debug環境下的字符串拼接和具體求值的操作
| 1 2 3 4 5 6 7 8 9 10 11 | inline fun smartMessage(lazyMessage: () -> Any?) { if (BuildConfig.DEBUG) { Log.d("smartMessage", lazyMessage().toString()) } } private fun testSmartMessage() { smartMessage { "getProperties " + getProperties() } } | 
|---|
- 上面使用了Lambda表達式來生成message信息
如何巧妙地規避不必要的開銷
當我們反編譯Kotlin 代碼 到 Java代碼時,一切就清晰了。
| 1 2 3 4 5 6 7 8 9 10 | private final void testSmartMessage() { int $i$f$smartMessage = false; if (BuildConfig.DEBUG) { String var3 = "smartMessage"; int var2 = false; String var4 = "getProperties " + this.getProperties(); Log.d(var3, String.valueOf(var4)); } } | 
|---|
- 之前的Lambda 由於採用了 inline 處理 會把smartMessage提取到調用處testSmartMessage
- 上面的信息,都是確保了在BuildConfig.DEBUG成立時才執行,否則不執行
- 上面的做法,利用了Kotlin的特性,就運行時可能存在的開銷一下就移除了。
注意
- smartMessage 建議只在 Kotlin 中調用,否則會生成實例,因為無法inline處理

