這可能是最好的 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處理

相關閱讀