LOG 是任何一种编程语言的第一个 API,通常被初学者用来打印 Hello, World!。
有研究显示,不使用 LOG 或者使用姿势错误的人,感情路都走得很辛苦,有七成的比例会在 34 岁的时候跟自己不爱的人结婚,而其余三成的人最后只能把遗产留给自己的猫。
毕竟爱情需要书写,不能是一整张白纸。
LogCat 是 Android 开发者们最熟悉不过的日志打印工具,几乎每一个 Android 项目里面都包含着大量的 Log 相关代码。不过,或许是因为 Log 实在是太过于普通,所以许多人在使用它的时候就显得非常随意,这些错误的使用姿势却会在不经意间给我们带来不少的大坑。
Log 相关的一些问题
没有关闭调试用的 LOG
许多同学喜欢在开发阶段用 Log 输出当前的一些环境数据,用于调试代码,但是在调试完成后却忘了关闭这些 Log,导致发版出去的应用里面还会继续输出这些 LOG,这样不仅会造成不必要的性能丢失,也会暴露一些敏感的数据,这些都是我们不愿看到的。
首先,我们要给 Log 进行分级,规定 “DEBUG 版本输出哪一些级别的 LOG 并屏蔽哪一些级别的 LOG,而 RELEASE 版本又输出另一些级别的 LOG 并屏蔽另一些级别的 LOG”,这样在开发阶段能够输出我们调试需要的 LOG,而同时又能保证放送的版本能够屏蔽这些敏感的 LOG。但是在开发阶段我们不应该特意去注意这些细节,所以必须开发一个 Log 工具库,在框架层级解决这个需求。
同时,需要注意的是, 用于作为 “开启 / 关闭 Log” 的开关必须是一个常量 ,而不能是一个变量(使用常量的话,在编译代码的时候,如果常量为 false),编译器会直接把调试部分的 Log 代码直接去掉,而使用变量作为开关的话,这个判断逻辑会继续保留,一方面会造成性能丢失,另一方面在运行时也可以通过 Hack 手段强行开启这部分 Log 代码。
另外,“开启 / 关闭 Log” 的开关必须写在 Log 方法外部,也就是说必须先判断 “开启 / 关闭 Log” 条件,再调用 Log 方法,因为在调用 Log 方法的时候已经造成了性能丢失,而且调用方法的时候,会先构造好改方法需要的参数(按照参数顺序从右往左),再调用方法,而许多人喜欢在调用 Log 方法的时候计算需要打印出来的内容,这里是最容易造成性能丢失的地方。因此,如果为了图方便,写一个 Log 工具类,在工具类内部去判断是否应该开启或关闭 Log,事实上已经造成了不少的性能丢失。正确的使用姿势应该是:
1 | public static final boolean DEBUG = true; |
在循环体内部打印 LOG
尽管 Log 造成的性能损失很小,但是如果在循环体内部循环调用 Log 方法的话,那总体的丢失的非常可观了,所以不应该在循环体内部使用 Log,正确的做法是在循环体内部拼接需要打印的内容,等跳出循环体再一次打印出来。
除了常见的循环体外,还要一个需要注意的场景就是 Adapter。ListView/RecyclerView 是 Android 开发中最常用的控件,因此 Adapter 使用的情景也很多。滚动屏幕的时候,ListView/RecyclerView 会在通过 Adapter 频繁地绑定 ItemView 和数据,而且这些都是在 UI 线程里进行的,所以如果在绑定的过程中调用 Log,可能会造成明显的卡顿。
至于 Log 到底会丢失多少性能,一般情况下,Log 的性能丢失很小,毕竟是这么常见的系统 Api,肯定是身经百战,早就是 “best performance” 了。不过我曾经有个 RecyclerView 在 MIUI 上非常卡,一开始我是 RecyclerView 布局没优化好,最终定位到 Adapter 内部的一处 Log 上,卡顿的地方出现在 Log 的 Native 实现。MIUI 到底对用户输出的日志做了什么处理呢?非常神奇。
无法获取重要 LOG 内容
在调试代码的时候,我们经常通过 LOG 来定位 Bug。同理,当线上的版本出现问题的时候,我们也希望能通过 LOG 来定位问题所在。但是问题是用户的设备上的打印出来的 LOG 我们根本没有方法获取,唯一的手段就是当用户设备出现问题的时候,把设备借过来连上 IDE 用 LogCat 查看输出的 LOG…… 显然这是不可行的。
这种时候,我们可以在打印重要 LOG(比如重要路径的触发点、或者一些异常类的信息)的时候,一并把这些信息记录到文件里。在用户反馈系统里面,一并将这些文件上传到我们的用户反馈服务器,这样在处理反馈问题的时候,就能拿到重要的参考日志了。
BLog
BLog 是 Android SDK 的 LOG 工具 {@link android.util.Log} 的加强版,以方便在开发时用来 操作调试日志。
特点
- 简单易用的 API;
- 支持输出线程信息;
- 支持设置 LogLevel,方便在生产环境关闭调试用的 LOG;
- 支持将 LOG 内容写入文件,以便通过文件 LOG 定位用户反馈的问题;
注意,尽管 BLog 支持关闭 Log 的输出,但是在你调用 BLog.v (String) 的时候,其实已经造成了性能 丢失,所以请尽量使用正确的姿势来使用 BLog,比如
1 | if (BuildConfig.DEBUG) { |
Getting Started
https://github.com/kaedea/b-log
补充 2016-11-20 因为现在公司项目上也采用了 BLog 这个工具,开源公司项目相关的代码需要安装既定的流程来,所以暂时不公开源码。有兴趣的同学可以试下联系我交♂流。