安卓to鸿蒙系列:Timber 原创 精华
目录
Guide
本文基于https://gitee.com/andych008/timber_ohos 分析Timber的源码,及移植到鸿蒙需要做的工作。
大神JakeWharton的Timber是我写日志的最爱,几乎在所有的项目中都用。当然一般我会通过Timber使用Logger,原因很简单,因为Timber接口简洁,Logger的输出样式好看。常规套路:
当然它的内部实现也一样完美。咱们往下看。
原理
Timber英文翻译为**“木材”**。静态方法Timber.plant(Tree tree)
即种树。每种一棵树,就拥有一种日志能力。
比如树A表示输出日志到控制台,树B表示输出日志到文件,树C输出到网络。
代码实现上,Timber使用了外观(facade)模式。
Tree类是外观类,通过plant方法Timber持有Tree类的实例,Timber中的asTree、tag方法将它暴露出去,而对于调用者来说依赖的是抽象类Tree,而不是具体的Tree的实现,如果要更换或者添加Tree类实例,只需要调用plant等相关方法即可,所有调用者使用Tree对象的地方不需要做任何修改,这是符合面向对象依赖倒置原则的一个很好的体现。
另外也使用了委托(delegate)模式。Tree TREE_OF_SOULS
把所有的操作都委托给forestAsArray
。
更详细的分析请移步
知识点
-
临时tag的实现方法
很简单,
Timber.tag("临时tag").d(xxx);
设置临时tag。使用一次就删除。为了性能,使用
ThreadLocal
以空间换时间。 -
synchronized的使用,因为FOREST为单例,所以对其读写要加锁。
-
static volatile Tree[] forestAsArray
,volatile 保证了可见性 -
关于
plant(Tree tree)
方法中的forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);
-
为什么要把List<Tree>转成
Tree[]
数组?解释这个问题可以参考 深度解析CopyOnWriteArrayList,线程安全的ArrayList!,从使用场景上看,Timber对于
List<Tree> FOREST
读多写少,所以只对写操作加锁,读操作(遍历时)不需要加锁。其本质上也是读写分离的思想,和CopyOnWriteArrayList
类似,也是为了性能。 -
为什么要用
List.toArray(T[] a)
,而不是List.toArray()
?不推荐使用 toArray() 无参方法,此方法返回值只能是Object[]类,若强转将出现ClassCastException错误。
-
移植到鸿蒙
如果Timber没有默认提供DebugTree
,直接拿来就能在鸿蒙上使用。DebugTree
这棵树的能力是在Logcat中输出日志。所以移植要做的就是把android.util.Log
换成ohos.hiviewdfx.HiLog
。
HiLog
在tag的基础上扩展了HiLogLabel
的概念。
label = new HiLogLabel(HiLog.DEBUG,0,tag);
如果每次都new一个label,太低效,所以这里可以优化。比如如果和上次一样,就使用上次的。或者使用对象池技术。
关键代码:
2021-04-25更新
上文遗留一个问题:
如果每次都new一个label,太低效,所以这里可以优化。比如如果和上次一样,就使用上次的。或者使用对象池技术。
我在master分支中,默认实现只缓存一个label。后来,在mydev_lru分支中使用LinkedHashMap
实例的lru算法,缓存了最近使用的8个label。
不过,最后觉得这样做没有太大的必要。也许引入LinkedHashMap
比构建几个HiLogLabel
的代价要更大,还提高了复杂性。作为一个分支提交上去,希望和大家一起思考这个问题。如果大家有更好的想法,可以一起讨论。
感谢分享。
前排学习,希望能够分享更多安卓移植到鸿蒙相关的内容