用kotlin打印出漂亮的android日志
code in kotlin.png
Kotlin號稱是Android版本的swift,距離它1.0正式版本的推出快一年了。它像swift一樣,可以寫客戶端也可以寫服務端。由于公司項目比較繁忙,我一直沒有時間關注和更進它,只是偶爾花點時間看一下它的語法。
元旦放三天假,可以好好陪家人,也可以自己隨便寫點東西,于是便有了這篇文章。我嘗試用kotlin封裝了一個日志組件,用于android項目。
我們先來看下效果圖,看看它是如何打印出日志的
打印字符串的日志.jpeg
打印json格式的日志.jpeg
上面的日志格式是不是很酷?它是用kotlin寫出來的哦。
talk is cheap, show me the code!
import android.util.Log
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
/**
* Created by Tony Shen on 2017/1/2.
*/
object L {
enum class LogLevel {
ERROR {
override val value: Int
get() = 0
},
WARN {
override val value: Int
get() = 1
},
INFO {
override val value: Int
get() = 2
},
DEBUG {
override val value: Int
get() = 3
};
abstract val value: Int
}
private var TAG = "SAF_L"
var logLevel = LogLevel.DEBUG // 日志的等級,可以進行配置,最好在Application中進行全局的配置
@JvmStatic fun init(clazz: Class<*>) {
TAG = clazz.simpleName
}
/**
* 支持用戶自己傳tag,可擴展性更好
* @param tag
*/
@JvmStatic fun init(tag: String) {
TAG = tag
}
@JvmStatic fun e(msg: String) {
if (LogLevel.ERROR.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.e(TAG, String.format(s, msg))
}
}
}
@JvmStatic fun w(msg: String) {
if (LogLevel.WARN.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.e(TAG, String.format(s, msg))
}
}
}
@JvmStatic fun i(msg: String) {
if (LogLevel.INFO.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.i(TAG, String.format(s,msg))
}
}
}
@JvmStatic fun d(msg: String) {
if (LogLevel.DEBUG.value <= logLevel.value) {
if (msg.isNotBlank()) {
val s = getMethodNames()
Log.d(TAG, String.format(s, msg))
}
}
}
@JvmStatic fun json(json: String) {
var json = json
if (json.isBlank()) {
d("Empty/Null json content")
return
}
try {
json = json.trim { it <= ' ' }
if (json.startsWith("{")) {
val jsonObject = JSONObject(json)
var message = jsonObject.toString(LoggerPrinter.JSON_INDENT)
message = message.replace("\n".toRegex(), "\n║ ")
val s = getMethodNames()
println(String.format(s, message))
return
}
if (json.startsWith("[")) {
val jsonArray = JSONArray(json)
var message = jsonArray.toString(LoggerPrinter.JSON_INDENT)
message = message.replace("\n".toRegex(), "\n║ ")
val s = getMethodNames()
println(String.format(s, message))
return
}
e("Invalid Json")
} catch (e: JSONException) {
e("Invalid Json")
}
}
private fun getMethodNames(): String {
val sElements = Thread.currentThread().stackTrace
var stackOffset = LoggerPrinter.getStackOffset(sElements)
stackOffset++
val builder = StringBuilder()
builder.append(LoggerPrinter.TOP_BORDER).append("\r\n")
// 添加當前線程名
.append("║ " + "Thread: " + Thread.currentThread().name).append("\r\n")
.append(LoggerPrinter.MIDDLE_BORDER).append("\r\n")
// 添加類名、方法名、行數
.append("║ ")
.append(sElements[stackOffset].className)
.append(".")
.append(sElements[stackOffset].methodName)
.append(" ")
.append(" (")
.append(sElements[stackOffset].fileName)
.append(":")
.append(sElements[stackOffset].lineNumber)
.append(")")
.append("\r\n")
.append(LoggerPrinter.MIDDLE_BORDER).append("\r\n")
// 添加打印的日志信息
.append("║ ").append("%s").append("\r\n")
.append(LoggerPrinter.BOTTOM_BORDER).append("\r\n")
return builder.toString()
}
fun String.isBlank(msg:String):Boolean {
return msg==null || msg.length==0;
}
fun String.isNotBlank(msg:String):Boolean {
return !msg.isBlank();
}
}
這里,對kotlin的語法不做特別詳細的解釋,就解釋一下@JvmStatic和最后兩個方法。
kotlin中在方法名前標注@JvmStatic,就表示該方法是靜態的。
例如:
@JvmStatic fun i(msg: String)
相當于java的
public static void i(String msg)
最后兩個方法,就更加厲害了,使用了kotlin的extension function的特性。(即擴展類的函數, 可以在已有類中添加新的方法, 比繼承更加簡潔和優雅。)
fun String.isBlank(msg:String):Boolean {
return msg==null || msg.length==0;
}
fun String.isNotBlank(msg:String):Boolean {
return !msg.isBlank();
}
他們擴展了String類,在String類中增加兩個函數isBlank()、isNotBlank()。
使用方法:
msg.isNotBlank()
如果對這兩個擴展類感興趣,可以看看kotlin bytecode:
kotlin bytecode.jpeg
對了,還漏了一個LoggerPrinter類。同樣附上源碼:
/**
* Created by Tony Shen on 2017/1/2.
*/
object LoggerPrinter {
private val MIN_STACK_OFFSET = 3
/**
* Drawing toolbox
*/
private val TOP_LEFT_CORNER = '╔'
private val BOTTOM_LEFT_CORNER = '╚'
private val MIDDLE_CORNER = '╟'
private val HORIZONTAL_DOUBLE_LINE = '║'
private val DOUBLE_DIVIDER = "════════════════════════════════════════════"
private val SINGLE_DIVIDER = "────────────────────────────────────────────"
val TOP_BORDER = TOP_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER
val BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER
val MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER + SINGLE_DIVIDER
/**
* It is used for json pretty print
*/
val JSON_INDENT = 2
fun getStackOffset(trace: Array<StackTraceElement>): Int {
var i = MIN_STACK_OFFSET
while (i < trace.size) {
val e = trace[i]
val name = e.className
if (name != LoggerPrinter::class.java.name && name != L::class.java.name) {
return --i
}
i++
}
return -1
}
}
這個日志組件就這么兩個類,支持跟java混編。
舉個栗子吧:
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import cn.kotlintest.saf.log.L
import org.jetbrains.anko.find
/**
* Created by Tony Shen on 2016/12/28.
*/
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
L.init(this.javaClass)
initViews()
initData()
}
fun initViews():Unit{
val button = find<Button>(R.id.button)
button.setOnClickListener({ view ->
var intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
})
}
fun initData():Unit{
var s = "{\"firstName\":\"Brett\",\"lastName\":\"McLaughlin\",\"email\":\"aaaa\"}"
L.json(s)
}
}
在initData()中會打印一個json字符串,其運行效果已在最開始的圖中。
再舉一個跟java混編的例子吧
import android.app.Activity;
import android.os.Bundle;
import cn.kotlintest.saf.log.L;
/**
* Created by Tony Shen on 2017/1/3.
*/
public class SecondActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
L.init(this.getClass());
L.i("this is second activity");
}
}
混編的效果.jpeg
寫在最后
kotlin是開發android不錯的選擇,雖然我不會很激進地完全使用kotlin來替換原先的java代碼,但是一些常用的工具類可能會有它來寫,或者用它來逐步替換原先的工具類。
這個日志組件要是看得不過癮,可以看看我Android框架 SAF 里包含的日志組件,功能更加豐富。
來自:http://www.jianshu.com/p/87da67f09c1c