美文网首页Kotlin精选程序员Kotlin编程
用kotlin打印出漂亮的android日志(三)——基于责任链

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

作者: fengzhizi715 | 来源:发表于2017-11-28 14:20 被阅读413次

    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来开发。

    相关文章

      网友评论

        本文标题:用kotlin打印出漂亮的android日志(三)——基于责任链

        本文链接:https://www.haomeiwen.com/subject/stndbxtx.html