Timber源码分析

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源码也给我带来了一些收获.
希望以后还能够有心继续阅读源码,收获更多

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值