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源码分析】到这里,就可以解释Timber默认是没有Tree对象的,在使用前需要我们添加Tree对象.
追踪到plant(Tree tree)方法,可以看到,调用plant方法添加的Tree对象,都会添加到forestAsArray数组中,而调用Timber.v()方法时,会遍历已添加的Tree对象数组,挨个打印.
这样如果开发者需要自定义log格式,或者想要打印多次不同格式日志,都可以实现,Timber的可扩展性于此可见一斑.
2. 类名称Tag
对于可扩展的特性,相信更多人对类名称的获取更感兴趣,我们来看下tag的生成.
很快定位到有getTag()(抽象类Tree)方法:
final ThreadLocal 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源码也给我带来了一些收获.
希望以后还能够有心继续阅读源码,收获更多

    推荐阅读