用kotlin打印出漂亮的android日志
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的特性。(即扩展类的函数, 可以在已有类中添加新的方法, 比继承更加简洁和优雅。)这个特性跟Objective-C的Category很类似。
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里包含的日志组件,功能更加丰富。
已经发布到github:
https://github.com/fengzhizi715/SAF-Kotlin-log