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