LOG是任何编程语言的第一个API,初学者通常使用它来打印Hello,World!。研究表明,不使用LOG或使用错误姿势的人在人际关系中会遇到困难。70%的人会在34岁时嫁给一个自己不爱的人,而剩下的30%只能把遗产留给他们的猫。毕竟爱情是需要书写的,而不是一整张白纸。LogCat是Android开发者最熟悉的日志打印工具。几乎每个Android项目都包含大量与Log相关的代码。不过,或许是因为Log太过普通,很多人在使用的时候看起来很随意,但是这些错误的使用姿势却在不经意间给我们带来了很多的坑。一些与Log相关的问题没有关闭LOG进行调试。很多同学在开发阶段调试代码时喜欢用Log输出一些当前环境数据,但是调试完成后忘记关闭这些Log,导致应用无法发布。这些日志会不断输出,不仅会造成不必要的性能损失,还会暴露一些我们不想看到的敏感数据。首先,我们需要对Log进行分类,规定“DEBUG版本输出哪些级别的LOG,阻塞哪些级别的LOG,而RELEASE版本输出其他级别的LOG,并阻塞其他级别的LOG”,这样在开发阶段可以输出我们调试需要的LOG,同时可以保证广播版可以屏蔽这些敏感的LOG。但是我们在开发阶段不应该特别关注这些细节,所以我们必须开发一个Log工具库来在框架层面解决这个需求。同时需要注意的是,用作“on/offLog”的开关必须是常量,不能是变量(如果使用常量,在编译代码时,如果常量为false),编译器会直接把调试部分的日志代码直接去掉,如果用变量作为开关,判断逻辑会继续保留。一方面会造成性能损失,另一方面这部分日志代码也可以在运行时被Hack强行打开。另外,“启用/关闭日志”的开关必须写在Log方法外,也就是说调用Log方法之前必须先判断“启用/关闭日志”的条件,因为已经造成了性能损耗当Log方法被调用时,和调用方法时,会先构造方法需要的参数(按照参数的顺序从右到左),然后调用方法,很多人喜欢这样计算调用Log方法时需要打印的内容,这里是最容易造成性能损失的地方。所以,如果你为了图方便写了一个Log工具类,在工具类内部判断是否应该开启或关闭Log,其实已经造成了很大的性能损失。正确的使用姿势应该是:publicstaticfinalbooleanDEBUG=true;if(DEBUG){Log.v(TAG,"logsomething");}在循环体内打印LOG虽然Log带来的性能损失很小,但是如果在循环体如果调用Log方法,整体损失非常可观,所以循环体内部不宜使用Log。正确的做法是将要打印的内容拼接在循环体内,跳出循环体后再打印出来。除了普通的循环,还有一个需要注意的场景就是Adapter。ListView/RecyclerView是Android开发中最常用的控件,因此在很多场景下都会用到Adapter。在滚动屏幕时,ListView/RecyclerView会频繁地通过Adapter绑定ItemView和数据,而这些都是在UI线程中执行的,所以如果在绑定过程中调用Log,可能会造成明显的卡顿。至于Log会损失多少性能,一般情况下,Log的性能损失很小。毕竟就是这么一个普通的系统API,想必也是身经百战,早就是“最佳性能”了。但是,我曾经有一个RecyclerView非常卡在MIUI上。一开始RecyclerView的布局没有优化。最后,它位于Adapter内的一个Log上。卡住的地方出现在Log的Native实现中。MIUI到底对用户输出的日志做了什么?太奇妙了。无法获取重要的LOG内容在调试代码的时候,我们经常会使用LOG来定位bug。同样,当在线版本出现问题时,我们也希望通过LOG来定位问题。但问题是我们没有办法获取用户设备上打印的LOG。唯一的办法就是借用设备,在用户设备出现问题的时候,连接到IDE上,用LogCat查看输出的LOG……显然这是不可行的。这时候,当我们打印重要的LOG(比如重要路径的触发点,或者一些异常信息)的时候,我们也可以把这些信息记录到文件中。在用户反馈系统中,将这些文件上传到我们的用户反馈服务器,以便您在处理反馈问题时,可以得到重要的参考日志。BLogBLog是AndroidSDK的LOG工具{@Linkandroid.util.Log}的增强版,方便开发时操作调试日志。特点简单易用的API;支持输出线程信息;支持设置LogLevel,方便生产环境调试关闭LOG;支持将LOG内容写入文件,通过文件LOG定位用户反馈的问题;注意,虽然Blog支持关闭Log输出,但是当你调用BLog.v(String)时,实际上已经造成了性能损失,所以请尽量用正确的姿势使用BLog,比如if(BuildConfig.DEBUG){BLog.v(TAG,"日志详细");}
