安卓to鸿蒙系列:Timber
目录
Guide
本文基于https://gitee.com/andych008/timber_ohos 分析Timber的源码,及移植到鸿蒙需要做的工作。
大神JakeWharton的Timber是我写日志的最爱,几乎在所有的项目中都用。当然一般我会通过Timber使用Logger,原因很简单,因为Timber接口简洁,Logger的输出样式好看。常规套路:
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.tag("DwGG") // (Optional) Global tag for every log. Default PRETTY_LOGGER
.build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));
Timber.plant(new Timber.DebugTree() {
@Override
protected void log(int priority, String tag, String message, Throwable t) {
Logger.log(priority, tag, message, t);
}
});
当然它的内部实现也一样完美。咱们往下看。
原理
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以空间换时间。public static abstract class Tree {
final ThreadLocal<String> explicitTag = new ThreadLocal<>(); String getTag() {
String tag = explicitTag.get();
if (tag != null) {
explicitTag.remove();
}
return tag;
}
}
public static class DebugTree extends Tree {
@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]);
}
为什么要把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,太低效,所以这里可以优化。比如如果和上次一样,就使用上次的。或者使用对象池技术。
关键代码:
public static class DebugTree extends Tree {
private final ThreadLocal<HiLogLabel> currentLabel = new ThreadLocal<>();
private final ThreadLocal<String> currentTag = new ThreadLocal<>();
@Override protected void log(int priority, String tag, @NotNull String message, Throwable t) {
HiLogLabel label = getHiLogLabel(tag);
if (message.length() < MAX_LOG_LENGTH) {
if (priority == HiLog.FATAL) {
HiLog.fatal(label,message);
} else if (priority == HiLog.INFO){
HiLog.info(label, message);
}else if (priority == HiLog.WARN){
HiLog.warn(label, message);
}else if (priority == HiLog.ERROR){
HiLog.error(label, message);
}else if (priority == HiLog.DEBUG){
HiLog.debug(label, message);
}
return;
}
// Split by line, then ensure each line can fit into Log's maximum length.
for (int i = 0, length = message.length(); i < length; i++) {
int newline = message.indexOf('\n', i);
newline = newline != -1 ? newline : length;
do {
int end = Math.min(newline, i + MAX_LOG_LENGTH);
String part = message.substring(i, end);
if (priority == HiLog.FATAL) {
HiLog.fatal(label,part);
}else if (priority == HiLog.INFO){
HiLog.info(label, part);
}else if (priority == HiLog.WARN){
HiLog.warn(label, part);
}else if (priority == HiLog.ERROR){
HiLog.error(label, part);
}else if (priority == HiLog.DEBUG){
HiLog.debug(label, part);
}
i = end;
} while (i < newline);
}
}
private HiLogLabel getHiLogLabel(String tag) {
HiLogLabel label;
if (tag.equals(currentTag.get())) {
label = currentLabel.get();
} else {
label = new HiLogLabel(HiLog.DEBUG,0,tag);
currentLabel.set(label);
currentTag.set(tag);
}
return label;
}
}
synchronized的使用,因为FOREST为单例,所以对其读写要加锁。
static volatile Tree[] forestAsArray,volatile 保证了可见性关于
plant(Tree tree)方法中的forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);public static void plant(@NotNull Tree tree) {
if (tree == null) {
throw new NullPointerException("tree == null");
}
if (tree == TREE_OF_SOULS) {
throw new IllegalArgumentException("Cannot plant Timber into itself.");
}
synchronized (FOREST) {
FOREST.add(tree);
forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);
}
}
作者:没用的喵叔
想了解更多内容,请访问51CTO和华为合作共建的鸿蒙社区:https://harmonyos.51cto.com/
安卓to鸿蒙系列:Timber的更多相关文章
- 【资源下载】安卓VS鸿蒙第三方件切换宝典 V1.0
下载<安卓VS鸿蒙第三方件切换宝典> 由于字数较多,本文仅展示部分,查看完整版请点击上方下载 众所周知,安卓应用开发经过这么多年的发展相对成熟和稳定,鸿蒙OS作为后来者兼容一个成熟的开发体 ...
- 纯C++安卓开发 (ndk)系列之 ---- 常见问题
常见问题1:run as Android Application运行时提示无法识别到模拟器 解决步骤如下: (1)首先查看安卓模拟器是否已经打开 (2)如果安卓模拟器已经打开,则操作步骤为:点击Ecl ...
- 【安卓中的缓存策略系列】安卓缓存策略之综合应用ImageLoader实现照片墙的效果
在前面的[安卓缓存策略系列]安卓缓存之内存缓存LruCache和[安卓缓存策略系列]安卓缓存策略之磁盘缓存DiskLruCache这两篇博客中已经将安卓中的缓存策略的理论知识进行过详细讲解,还没看过这 ...
- 华为鸿蒙OS发布!方舟支持混合编译,终将可替换安卓?
前言 有关于鸿蒙的消息之前也有说过,就在昨天下午,华为举行了2019开发大会,正式推出了鸿蒙os系统(Harmony).其相关负责人表示,也是基于微软内核的全场景分布式OS 鸿蒙凭借微内核的优势, ...
- 最全华为鸿蒙 HarmonyOS 开发资料汇总
开发 本示例基于 OpenHarmony 下的 JavaScript UI 框架,进行项目目录解读,JS FA.常用和自定义组件.用户交互.JS 动画的实现,通过本示例可以基本了解和学习到 JavaS ...
- 安卓开发30:AsyncTask的用法
http://blog.csdn.net/initphp/article/details/10392093 安卓开发笔记系列(43) 在开发Android应用时必须遵守单线程模型的原则: Andro ...
- 华为 鸿蒙系统(HarmonyOS)
HarmonyOS Ⅰ. 鸿蒙系统简介 鸿蒙系统(HarmonyOS),是第一款基于微内核的全场景分布式OS,是华为自主研发的操作系统.2019年8月9日,鸿蒙系统在华为开发者大会<HDC.20 ...
- [github] 关于华为鸿蒙OS
English Docs | 中文文档 | Türkçe Dökümanlar HarmonyOS Ⅰ. 鸿蒙系统简介 鸿蒙系统(HarmonyOS),是第一款基于微内核的全场景分布式OS,是华为自主 ...
- 【python基础】第02回 计算机基础2
上节内容回顾 1.绝对路径与相对路径 1.路径的概念 用来标识资源的位置 2.绝对路径 类似于全球GPS定位(给到任何人都可以顺利的找到相应的资源) eg: D:\aaa\a.txt 3.相对路径 需 ...
随机推荐
- eclipse中将项目加载到tocat报错:Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modules
第一种解决方法:只需要找到导入项目的配置文件即可,举个栗子:D:\公司\iptv_gx\iptv_gx\.settings\org.eclipse.wst.common.project.facet.c ...
- 2021-2-28:调用 System.gc() 后究竟发生了什么?
首先,根据 DisableExplicitGC 这个 JVM 启动参数的状态,确定是否会 GC,如果需要 GC,不同 GC 会有不同的处理. 1. G1 GC 的处理 如果是 System.gc() ...
- CSS:CSS基础
和 HTML 类似,CSS 也不是真正的编程语言,甚至不是标记语言.它是一门样式表语言,这也就是说人们可以用它来选择性地为 HTML 元素添加样式. CSS规则集 选择器(Selector):元素的名 ...
- 【图像处理】使用OpenCV+Python进行图像处理入门教程(二)
这篇随笔介绍使用OpenCV进行图像处理的第二章 图像的运算,让我们踏上继续回顾OpenCV进行图像处理的奇妙之旅,不断地总结.回顾,以新的视角快速融入计算机视觉的奥秘世界. 2 图像的运算 复杂的 ...
- 【Django必备01】——什么是Django框架?有什么优势?模块组成介绍。
01.什么是Django框架? Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式.使用这种架构,程序员可以方便.快捷地创建高品质.易维护.数据库驱动的应用程序. ...
- NIO三大组件之Selector选择器
什么是选择器 选择器的作用是完成IO的多路复用.一个通道代表一条连接通路,通过选择器可以同时监控多个通道的IO(输入输出)状况.选择器和通道的关系,是监控和被监控的关系. 使用 重要的成员 Selec ...
- Python之基础算法介绍
一.算法介绍 1. 算法是什么 算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制.也就是说,能够对一定规范的输入,在有限时间内获得所要求的输 ...
- restful设计风格
restful是一种软件设计风格,并不是标准,它只是提供了一组设计原则和约束条件. ① restful 提倡面向资源编程,url接口尽量要使用名词,不要使用动词 ② 在url中可以体现版本号 ③可以根 ...
- 【FreeRTOS】cpu利用率统计
目录 前言 概念 作用 必看点 实现 添加几个宏定义 源码 FreeRTOS STM32 定时器 简要说明 前言 本笔记基于 stm32+FreeRTOS. 主要参考野火.安富莱. 概念 简单概要: ...
- Android学习之启动活动的最佳写法
•开始热身 通过之前的学习,我们现在可以很容易的启动一个活动: 首先通过 Intent 构造出当前的 "意图",然后调用 startActivity() 方法将活动启动起来: ...