安卓to鸿蒙系列:Timber

系统
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

[[395653]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

目录

  • Guide
  • 原理
  • 知识点
  • 移植到鸿蒙

Guide

本文基于https://gitee.com/andych008/timber_ohos 分析Timber的源码,及移植到鸿蒙需要做的工作。

大神JakeWharton的Timber是我写日志的最爱,几乎在所有的项目中都用。当然一般我会通过Timber使用Logger,原因很简单,因为Timber接口简洁,Logger的输出样式好看。常规套路:

  1. FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder() 
  2.         .tag("DwGG")   // (Optional) Global tag for every log. Default PRETTY_LOGGER 
  3.         .build(); 
  4.  
  5. Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy)); 
  6. Timber.plant(new Timber.DebugTree() { 
  7.     @Override 
  8.     protected void log(int priority, String tag, String message, Throwable t) { 
  9.         Logger.log(priority, tag, message, t); 
  10.     } 
  11. }); 

当然它的内部实现也一样完美。咱们往下看。

原理

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。

更详细的分析请移步

  1. Timber 源码解析
  2. Timber源码解析及涉及知识点总结

知识点

1.临时tag的实现方法

  • 很简单,Timber.tag("临时tag").d(xxx);设置临时tag。使用一次就删除。
  • 为了性能,使用ThreadLocal 以空间换时间。
  1. public static abstract class Tree { 
  2. final ThreadLocal<String> explicitTag = new ThreadLocal<>(); 
  3.  
  4. String getTag() { 
  5.   String tag = explicitTag.get(); 
  6.   if (tag != null) { 
  7.     explicitTag.remove(); 
  8.   } 
  9.   return tag; 
  1. public static class DebugTree extends Tree { 
  2.  
  3. @Override final String getTag() { 
  4.   String tag = super.getTag(); 
  5.   if (tag != null) { 
  6.     return tag; 
  7.   } 
  8.  
  9.   // DO NOT switch this to Thread.getCurrentThread().getStackTrace(). The test will pass 
  10.   // because Robolectric runs them on the JVM but on Android the elements are different. 
  11.   StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 
  12.   if (stackTrace.length <= CALL_STACK_INDEX) { 
  13.     throw new IllegalStateException( 
  14.         "Synthetic stacktrace didn't have enough elements: are you using proguard?"); 
  15.   } 
  16.   return createStackElementTag(stackTrace[CALL_STACK_INDEX]); 

2.synchronized的使用,因为FOREST为单例,所以对其读写要加锁。

3.static volatile Tree[] forestAsArray ,volatile 保证了可见性

4.关于plant(Tree tree)方法中的forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);

  1. public static void plant(@NotNull Tree tree) { 
  2.   if (tree == null) { 
  3.     throw new NullPointerException("tree == null"); 
  4.   } 
  5.   if (tree == TREE_OF_SOULS) { 
  6.     throw new IllegalArgumentException("Cannot plant Timber into itself."); 
  7.   } 
  8.   synchronized (FOREST) { 
  9.     FOREST.add(tree); 
  10.     forestAsArray = FOREST.toArray(new Tree[FOREST.size()]); 
  11.   } 
  • 为什么要把List 转成Tree[]数组?

解释这个问题可以参考 深度解析CopyOnWriteArrayList,线程安全的ArrayList!,从使用场景上看,Timber对于List 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,太低效,所以这里可以优化。比如如果和上次一样,就使用上次的。或者使用对象池技术。

关键代码:

  1. public static class DebugTree extends Tree { 
  2.   private final ThreadLocal<HiLogLabel> currentLabel = new ThreadLocal<>(); 
  3.   private final ThreadLocal<String> currentTag = new ThreadLocal<>(); 
  4.  
  5.  
  6.   @Override protected void log(int priority, String tag, @NotNull String message, Throwable t) { 
  7.     HiLogLabel label = getHiLogLabel(tag); 
  8.  
  9.     if (message.length() < MAX_LOG_LENGTH) { 
  10.       if (priority == HiLog.FATAL) { 
  11.         HiLog.fatal(label,message); 
  12.       } else  if (priority == HiLog.INFO){ 
  13.         HiLog.info(label, message); 
  14.       }else if (priority == HiLog.WARN){ 
  15.         HiLog.warn(label, message); 
  16.       }else if (priority == HiLog.ERROR){ 
  17.         HiLog.error(label, message); 
  18.       }else if (priority == HiLog.DEBUG){ 
  19.         HiLog.debug(label, message); 
  20.       } 
  21.       return
  22.     } 
  23.  
  24.     // Split by line, then ensure each line can fit into Log's maximum length. 
  25.     for (int i = 0, length = message.length(); i < length; i++) { 
  26.       int newline = message.indexOf('\n', i); 
  27.       newline = newline != -1 ? newline : length; 
  28.       do { 
  29.         int end = Math.min(newline, i + MAX_LOG_LENGTH); 
  30.         String part = message.substring(i, end); 
  31.         if (priority == HiLog.FATAL) { 
  32.           HiLog.fatal(label,part); 
  33.         }else  if (priority == HiLog.INFO){ 
  34.           HiLog.info(label, part); 
  35.         }else if (priority == HiLog.WARN){ 
  36.           HiLog.warn(label, part); 
  37.         }else if (priority == HiLog.ERROR){ 
  38.           HiLog.error(label, part); 
  39.         }else if (priority == HiLog.DEBUG){ 
  40.           HiLog.debug(label, part); 
  41.         } 
  42.         i = end
  43.       } while (i < newline); 
  44.     } 
  45.   } 
  46.  
  47.   private HiLogLabel getHiLogLabel(String tag) { 
  48.     HiLogLabel label; 
  49.     if (tag.equals(currentTag.get())) { 
  50.       label = currentLabel.get(); 
  51.     } else { 
  52.       label = new HiLogLabel(HiLog.DEBUG,0,tag); 
  53.       currentLabel.set(label); 
  54.       currentTag.set(tag); 
  55.     } 
  56.     return label; 
  57.   } 

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-04-27 09:22:28

鸿蒙HarmonyOS应用

2021-05-11 14:43:16

鸿蒙HarmonyOS应用

2021-07-26 10:09:43

鸿蒙HarmonyOS应用

2019-06-20 16:07:12

鸿蒙安卓操作系统

2020-09-10 09:30:03

鸿蒙安卓操作系统

2013-11-04 14:49:34

安卓

2018-02-09 08:59:47

安卓FuchsiaiOS

2021-05-18 15:44:13

IOS安卓鸿蒙

2021-06-04 05:13:22

鸿蒙

2019-07-12 16:00:25

华为禁令开发

2013-04-24 11:33:50

安卓

2013-10-17 10:17:41

安卓

2014-12-09 11:15:06

邮箱安卓移动端

2021-02-25 10:40:00

数据

2020-09-29 13:03:45

安卓应用开发工具开发

2011-10-18 13:33:02

思亚诺CMMBDTV

2020-10-14 07:27:08

MacBook

2021-10-21 18:57:55

Windows 11操作系统微软

2013-12-12 16:51:43

安卓进化AndroidGoogle

2019-06-11 13:41:45

华为鸿蒙操作系统
点赞
收藏

51CTO技术栈公众号