美文网首页
Timber源码分析

Timber源码分析

作者: 一缸米 | 来源:发表于2018-06-09 15:27 被阅读0次

    Timber的使用,在上一篇Timber的使用与分析中已经介绍完成.相信有与我一样的同好,对Timber的源码很感兴趣.
    源码下载地址

    Timber特性

    首先,我们来看一下Timber的特性

    Timber是一款可扩展的Logger工具
    Timber通过Timber.plant来添加tree实例
    Timber需要在使用前添加完成tree实例,最好在Application的onCreate中实现
    Timber默认实现的DebugTree将调用它的类的名称作为Tag(没有指定tag时使用)

    看到源码,Timber其实只有一个类.600+行的代码虽然不多,我们依旧带着目的看源码.

    源码分析

    1. 可扩展

    以最常用的v(@NonNls String message, Object... args)类型方法为例,我们看源码:

      /** Log a verbose message with optional format args. */
      public static void v(@NonNls String message, Object... args) {
        TREE_OF_SOULS.v(message, args);
      }
    

    继续追踪方法:

            @Override
            public void v(String message, Object... args) {
                Tree[] forest = forestAsArray;
                for (Tree tree : forest) {
                    tree.v(message, args);
                }
            }
    

    这里有个关键对象forestAsArray,我们可以查到,这是一个Tree[],并且默认赋值空数组:

        private static final Tree[] TREE_ARRAY_EMPTY = new Tree[0];
        static volatile Tree[] forestAsArray = TREE_ARRAY_EMPTY;
    

    到这里,就可以解释Timber默认是没有Tree对象的,在使用前需要我们添加Tree对象.
    追踪到plant(Tree tree)方法,可以看到,调用plant方法添加的Tree对象,都会添加到forestAsArray数组中,而调用Timber.v()方法时,会遍历已添加的Tree对象数组,挨个打印.

    这样如果开发者需要自定义log格式,或者想要打印多次不同格式日志,都可以实现,Timber的可扩展性于此可见一斑.

    2. 类名称Tag

    对于可扩展的特性,相信更多人对类名称的获取更感兴趣,我们来看下tag的生成.
    很快定位到有getTag()(抽象类Tree)方法:

        final ThreadLocal<String> explicitTag = new ThreadLocal<>();
        
            @Nullable
            String getTag() {
                String tag = explicitTag.get();
                if (tag != null) {
                    explicitTag.remove();
                }
                return tag;
            }
    

    explicitTag是tag的线程局部变量,当调用Timber.tag(String tag)方法时赋值.
    这里并没有获取类名的方法,继续看.
    我们上一篇已经说过,对Timber的初始化,使用的是DebugTree对象,再次定位到DebugTree的getTag()方法:

            //这是下面方法用到的常量
            private static final int MAX_TAG_LENGTH = 23;
            private static final int CALL_STACK_INDEX = 5;
            private static final Pattern ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$");
    
             @Override
            final String getTag() {
                String tag = super.getTag();
                if (tag != null) {
                    return tag;
                }
    
                // DO NOT switch this to Thread.getCurrentThread().getStackTrace(). The test will pass
                // because Robolectric runs them on the JVM but on Android the elements are different.
                StackTraceElement[] stackTrace = new Throwable().getStackTrace();
                if (stackTrace.length <= CALL_STACK_INDEX) {
                    throw new IllegalStateException(
                            "Synthetic stacktrace didn't have enough elements: are you using proguard?");
                }
                return createStackElementTag(stackTrace[CALL_STACK_INDEX]);
            }
            
            
             @Nullable
            protected String createStackElementTag(@NotNull StackTraceElement element) {
                String tag = element.getClassName();
                Matcher m = ANONYMOUS_CLASS.matcher(tag);
                if (m.find()) {
                    tag = m.replaceAll("");
                }
                tag = tag.substring(tag.lastIndexOf('.') + 1);
                // Tag length limit was removed in API 24.
                if (tag.length() <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    return tag;
                }
                return tag.substring(0, MAX_TAG_LENGTH);
            }
    

    源码中使用new Throwable().getStackTrace(),通过堆栈跟踪获取到堆栈跟踪数组,我打印出来数组内容:

        getTag: timber.log.Timber$DebugTree
        getTag: timber.log.Timber$Tree
        getTag: timber.log.Timber$Tree
        getTag: timber.log.Timber$1
        getTag: timber.log.Timber
        getTag: com.example.timber.ui.DemoActivity
        getTag: com.example.timber.ui.DemoActivity_ViewBinding$1
        getTag: butterknife.internal.DebouncingOnClickListener
        getTag: android.view.View
        getTag: android.view.View$PerformClick
        getTag: android.os.Handler
        getTag: android.os.Handler
        getTag: android.os.Looper
        getTag: android.app.ActivityThread
        getTag: java.lang.reflect.Method
        getTag: com.android.internal.os.ZygoteInit$MethodAndArgsCaller
        getTag: com.android.internal.os.ZygoteInit
        getTag: com.example.timber.ui.DemoActivity
    

    果然在第6个的时候,展示的是我们调用Timber的类.
    这里做好笔记,一个新的思路

    总结

    可以说,Timber的源码非常简单,但是分析Timber源码也给我带来了一些收获.
    希望以后还能够有心继续阅读源码,收获更多

    相关文章

      网友评论

          本文标题:Timber源码分析

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