用kotlin打印出漂亮的android日志(三)——基于责任链模式打印任意对象

SAF-Kotlin-log 是一个Android的日志框架,这几天我抽空重新更新了一下代码。

github地址:https://github.com/fengzhizi715/SAF-Kotlin-log

一. 打印几个Android常用的对象

1.1 Uri的打印

        Uri uri = Uri.parse("http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic");
        L.json(uri);

打印效果:


Uri.png

1.2 Bundle的打印

        User u = new User();
        u.userName = "tony";
        u.password = "123456";
        
        Bundle bundle = new Bundle();
        bundle.putString("key1","this is key1");
        bundle.putInt("key2",100);
        bundle.putBoolean("key3",true);
        bundle.putSerializable("key4",u);
        L.json(bundle);

打印效果:


Bundle.png

目前,它默认支持JSON字符串、集合、Map、Bundle、Intent、Reference、Throwable、Uri等类型的打印,分别做了特别的格式化处理。

二. 使用责任链模式,改造打印对象的方法

在使用责任链模式之前,json()方法是这样的,需要使用when表达式来判断某个类应该对应哪个方法来打印对象。

    /**
     * 将任何对象转换成json字符串进行打印
     */
    @JvmStatic
    fun json(obj: Any?) {

        if (obj == null) {
            d("object is null")
            return
        }

        when(obj) {

            is String -> string2JSONString(obj)

            is Map<*, *> -> map2JSONString(obj)

            is Collection<*> -> collection2JSONString(obj)

            is Bundle -> bundle2JSONString(obj)

            is Reference<*> -> reference2JSON(obj)

            is Intent -> intent2JSON(obj)

            is Uri -> uri2JSON(obj)

            is Throwable -> throwable2JSONString(obj)

            else -> {

                try {
                    val s = getMethodNames()

                    var msg = obj.javaClass.toString() + LoggerPrinter.BR + "║ "
                    val objStr = JSON.toJSONString(obj)
                    val jsonObject = JSONObject(objStr)
                    var message = jsonObject.toString(LoggerPrinter.JSON_INDENT)
                    message = message.replace("\n".toRegex(), "\n║ ")

                    println(String.format(s, msg+ message))
                } catch (e: JSONException) {
                    e("Invalid Json")
                }
            }
        }
    }

使用责任链模式之后,json()是这样的,一行代码代替了when表达式。

    /**
     * 将任何对象转换成json字符串进行打印
     */
    @JvmStatic
    fun json(obj: Any?) {

        if (obj == null) {
            d("object is null")
            return
        }

        firstHandler.handleObject(obj)
    }

2.1 为何需要责任链模式?

目前日志类L只能打印几种类型的对象,或者是默认地将对象打印成json。如果要对某一个对象类做一些个性化的格式化并打印出来,按照原先的做法需要修改json()方法的when表达式。

为了符合“开闭原则”,对扩展开放、对修改关闭。我考虑使用责任链模式来替代when表达式,未来有其他需求只需增加一个单独的Handler即可。

2.2 如何使用责任链模式?

首先,定义一个基类的Handler用于对象的处理,这个Handler还会被赋予一个nextHandler表示责任链中的下一个处理者。如果当前的Handler处理不了,则交给下一个Handler来处理。

/**
 * Created by tony on 2017/11/27.
 */
abstract class BaseHandler {

    // 责任链的下一个节点,即处理者
    private var nextHandler: BaseHandler? = null

    // 捕获具体请求并进行处理,或是将请求传递到责任链的下一级别
    fun handleObject(obj: Any) {

        if (obj == null) {
            return
        }

        if (!handle(obj)) {
            // 当前处理者不能胜任,则传递至责任链的下一节点
            if (this.nextHandler != null) {
                this.nextHandler!!.handleObject(obj)
            }
        }
    }

    // 设置责任链中的下一个处理者
    fun setNextHandler(nextHandler: BaseHandler) {
        this.nextHandler = nextHandler
    }

    // 定义链中每个处理者具体的处理方式
    protected abstract fun handle(obj: Any): Boolean
}

定义完基类的Handler之后,需要定义各个具体的Handler。以BundleHandler为例,它是专门用于格式化Bundle并打印出来。

import android.os.Bundle
import com.alibaba.fastjson.JSON
import com.safframework.log.L
import com.safframework.log.LoggerPrinter
import com.safframework.log.parser.Parser
import com.safframework.log.utils.Utils
import org.json.JSONException
import org.json.JSONObject

/**
 * Created by tony on 2017/11/27.
 */
class BundleHandler():BaseHandler(), Parser<Bundle> {

    override fun handle(obj: Any): Boolean {

        if (obj is Bundle) {

            val s = L.getMethodNames()
            println(String.format(s, parseString(obj)))
            return true
        }

        return false
    }

    override fun parseString(bundle: Bundle): String {

        var msg = bundle.javaClass.toString() + LoggerPrinter.BR + "║ "

        val jsonObject = JSONObject()
        for (key in bundle.keySet()) {

            val isPrimitiveType = Utils.isPrimitiveType(bundle.get(key))

            try {

                if (isPrimitiveType) {
                    jsonObject.put(key.toString(), bundle.get(key))
                } else {
                    jsonObject.put(key.toString(), JSONObject(JSON.toJSONString(bundle.get(key))))
                }
            } catch (e: JSONException) {
                L.e("Invalid Json")
            }

        }

        var message = jsonObject.toString(LoggerPrinter.JSON_INDENT)
        message = message.replace("\n".toRegex(), "\n║ ")

        return msg + message
    }
}

定义完各个Handler之后,需要把各个Handler串联起来。在日志类L中使用Kotlin的init代码块来做这件事,init代码块相当于Java的静态代码块。

    private val handlers = ArrayList<BaseHandler>()
    private var firstHandler:BaseHandler

    init{
        handlers.add(StringHandler())
        handlers.add(CollectionHandler())
        handlers.add(MapHandler())
        handlers.add(BundleHandler())
        handlers.add(IntentHandler())
        handlers.add(UriHandler())
        handlers.add(ThrowableHandler())
        handlers.add(ReferenceHandler())
        handlers.add(ObjectHandler())

        val len = handlers.size
        for (i in 0..len - 1) {
            if (i > 0) {
                handlers[i - 1].setNextHandler(handlers[i])
            }
        }

        firstHandler = handlers[0]
    }

做完这些之后,才能通过一行代码来处理各种对象。

firstHandler.handleObject(obj)

三. 自定义对象的解析处理

目前在框架中只能处理8种对象,或者使用默认的方式将对象打印成json风格。

如果有个性化的需求,可以自定义类来实现,只需继承BaseHandler。

例如,定义一个UserHandler

import com.safframework.log.L;
import com.safframework.log.handler.BaseHandler;

import org.jetbrains.annotations.NotNull;


/**
 * Created by tony on 2017/11/27.
 */

public class UserHandler extends BaseHandler {

    @Override
    protected boolean handle(@NotNull Object obj) {

        if (obj instanceof User) {

            User u = (User)obj;

            String s = L.getMethodNames();
            System.out.println(String.format(s, u.userName+":"+u.password));
            return true;
        }

        return false;
    }
}

在使用UserHandler之前,使用默认的ObjectHandler将对象格式化后打印出来。L添加了UserHandler之后,再打印user对象就不再使用默认的ObjectHandler,而是使用UserHandler来格式化对象。

        User u = new User();
        u.userName = "tony";
        u.password = "123456";
        L.json(u);

        L.addCustomerHandler(new UserHandler());
        L.json(u);

打印效果

自定义Handler.png

四. 总结

这篇文章应该算是很久之前两篇文章的后续,现在越来越多的工具我开始使用Kotlin来开发。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267