Android 5.0 如何正确启用isLoggable(一)__使用详解
转自:http://blog.csdn.net/yihongyuelan/article/details/46409389
isLoggable是什么
在Android源码中,我们经常可以看到如下代码:
- //packages/apps/InCallUI/src/com/android/incallui/Log.java
- public static final String TAG = "InCall";
- public static final boolean DEBUG = android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
- public static void d(String tag, String msg) {
- if (DEBUG) {
- android.util.Log.d(TAG, delimit(tag) + msg);
- }
- }
- //packages/apps/InCallUI/src/com/android/incallui/InCallPresenter.java
- private InCallState startOrFinishUi(InCallState newState) {
- Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);
- //... ...
- }
只有在android.util.Log.isLoggable返回值为true的时候,startOrFinishUi的log才能正常输出。那什么时候isLoggable才会返回true呢?如何简单快捷的开启isLoggable并使log正常输出呢?
本文来自http://blog.csdn.NET/yihongyuelan 转载请务必注明出处
isLoggable定义
什么是isLoggable?isLoggable是android.util.Log提供的方法,用于检查指定TAG的level,是否满足输出条件,如满足则返回true反之则返回false。isLoggable在源码中的定义如下:
- /**
- * Checks to see whether or not a log for the specified tag is loggable at the specified level.
- *
- * The default level of any tag is set to INFO. This means that any level above and including
- * INFO will be logged. Before you make any calls to a logging method you should check to see
- * if your tag should be logged. You can change the default level by setting a system property:
- * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
- * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
- * turn off all logging for your tag. You can also create a local.prop file that with the
- * following in it:
- * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
- * and place that in /data/local.prop.
- *
- * @param tag The tag to check.
- * @param level The level to check.
- * @return Whether or not that this is allowed to be logged.
- * @throws IllegalArgumentException is thrown if the tag.length() > 23.
- */
- public static native boolean isLoggable(String tag, int level);
从以上定义中可以知道:
1. isLoggable默认level为android.util.Log.INFO;
2. 只有 level >= INFO才能输出,即level >= INFO时isLoggable返回true,反之则返回false;
3. 可以通过setprop log.tag.<YOUR_LOG_TAG> <LEVEL>来改变log的默认level,如adb shell setprop log.tag.InCall D。也可以将这些属性按照log.tag.InCall=D的形式,写入/data/local.prop中;
4. tag的长度如果超过23个字符则会抛出IllegalArgumentException异常;
在android.util.Log类中定义了Log的6种Level,如下:
- /**
- * Priority constant for the println method; use Log.v.
- */
- public static final int VERBOSE = 2;
- /**
- * Priority constant for the println method; use Log.d.
- */
- public static final int DEBUG = 3;
- /**
- * Priority constant for the println method; use Log.i.
- */
- public static final int INFO = 4;
- /**
- * Priority constant for the println method; use Log.w.
- */
- public static final int WARN = 5;
- /**
- * Priority constant for the println method; use Log.e.
- */
- public static final int ERROR = 6;
- /**
- * Priority constant for the println method.
- */
- public static final int ASSERT = 7;
6种level对应不同等级的log,level值的大小随着log权重的升高而增大。
isLoggable使能方法
前文提到,在自定义的Log类中通常使用isLoggable来判断是否输出log,如代码:
- //packages/apps/InCallUI/src/com/android/incallui/Log.java
- public static final String TAG = "InCall";
- public static final boolean DEBUG = android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
- public static void d(String tag, String msg) {
- if (DEBUG) {
- android.util.Log.d(TAG, delimit(tag) + msg);
- }
- }
- //packages/apps/InCallUI/src/com/android/incallui/InCallPresenter.java
- private InCallState startOrFinishUi(InCallState newState) {
- Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);
- //... ...
- }
如果想要输出startOrFinishUi()方法中的log,可以采用以下方式:
①. 将Log.d修改为android.util.Log.d;使用android.util.Log类取代InCallUI自定义的Log类,重新编译系统APP并push到手机中即可;
②. 修改com.android.incallui.Log中的DEBUG限制;将DEBUG的值直接置为true,或者注释掉if(DEBUG)。也可以将isLoggable(TAG, android.util.Log.DEBUG)中的android.util.Log.DEBUG修改为android.util.Log.INFO,即将log的level增大,从而使isLoggable()返回true并使得DEBUG的值为true;
③. 设置log.tag.InCall的属性值;通过adb shell setprop log.tag.InCall D,或者将"log.tag.InCall=D"写入/data/local.prop中(不包含引号,如local.prop不存在则需自行创建,权限设为644)。根据定义中的描述,设置属性值需要在方法被调用之前,因此需要重启InCallUI进程。通过修改属性值的方法,从而使得isLoggable()的返回值为true,从而使得DEBUG的值为true。
第一种方式直接修改,简单粗暴,但若修改点较多则比较麻烦,且需要重新编译代码。
第二种方式修改点统一,但和第一种一样,也需要重新编译。
第三种使用属性的方式使得isLoggable返回true,这种方式比较方便,同时适用于user/userdebug/eng各个版本的设备。这种方式需要在方法被调用前设置log.tag.InCall,通过代码:
- public static final boolean DEBUG = android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
可以知道,因为DEBUG是static的变量,所以当Log类被加载时,其值就已经设置好了。如果要使得isLoggable返回为true,那么setprop需要在Log类被加载前设置好,因此使用setprop之后需要重启对应的进程,如这里的com.android.incallui。但在framework中有些代码也使用isLoggable,framework属于每一个进程,如何重启framework呢?可以使用:
- adb shell stop
- adb shell start
adb shell stop会杀掉zygote进程以及所有由zygote孵化而来的子进程。adb shell start则会重启zygote进程,再由zygote进程启动其它Android核心进程。当zygote重新启动时,会重新加载framework相关资源,而此时属性已经设置。这种方法虽然简单,但当设备重启后,所有设置的log.tag.<TAG>都会失效,若想再次启用则需重新设置。
也可以通过将log.tag.InCall=D加入到/data/local.prop文件中,这种方式与setprop类似,都是设置属性值,但这种方式的好处是,设置之后重启设备该log.tag.InCall依然有效。不过因为/data目录的权限控制,只有userdebug/eng版本才可以修改/data/local.prop文件。[/system/core/init/property_service.c] load_override_properties()函数中有/data/local.prop的使能方法。
小结
isLoggable的使能方法如下图所示:
图 1 isLoggable使能方案对比
如果只是想启用某个进程的isLoggable方法,通过setprop设置属性是不错的选择,同事,想要重启设备后依然有效则需要设置/data/local.prop,不过/data目录只有在userdebug/eng版本中才可以写入。那如果想在user版中设置log.tag属性并在重启后依然有效,可以采取以下方法:
1. 获取user版系统的root权限;
2. 将log.tag.InCall=D追加到/system/build.prop文件中;
3. adb reboot重启设备;
以上方案大家可能会疑惑,既然user版获取到了root权限,为什么不直接修改/data/local.prop呢?这是因为如果是user版,则ro.debuggable=0,所以系统启动时不会读取/data/local.prop文件,并最终导致设置log.tag属性不会生效。详细原理将在下一篇文章《Android 5.0 如何正确启用isLoggable(一)__原理分析》中探讨。
Android 5.0 如何正确启用isLoggable(一)__使用详解的更多相关文章
- Android 5.0 怎样正确启用isLoggable(一)__使用具体解释
isLoggable是什么 在Android源代码中,我们常常能够看到例如以下代码: //packages/apps/InCallUI/src/com/android/incallui/Log.jav ...
- Android 5.0 怎样正确启用isLoggable(二)__原理分析
前置文章 <Android 5.0 怎样正确启用isLoggable(一)__使用具体解释> 概要 在上文<Android 5.0 怎样正确启用isLoggable(一)__使用具体 ...
- Android5.0如何正确启用isLoggable(二) 理分析
转自:http://www.it165.net/pro/html/201506/43374.html 概要 在上文<Android 5.0 如何正确启用isLoggable(一)__使用详解&g ...
- Android 4.0以后正确的获取外部sd卡存储目录
刚解决这个棘手的问题 找了很久,随笔记下. 网上搜索 android 获取外部sd卡存储目录 普遍都是: 1) Environment.getExternalStorageDirectory() 这个 ...
- Android零基础入门第80节:Intent 属性详解(下)
上一期学习了Intent的前三个属性,本期接着学习其余四个属性,以及Android系统常用内置组件的启动. 四.Data和Type属性 Data属性通常用于向Action属性提供操作的数据.Data属 ...
- Android零基础入门第19节:Button使用详解
原文:Android零基础入门第19节:Button使用详解 Button(按钮)是Android开发中使用非常频繁的组件,主要是在UI界面上生成一个按钮,该按钮可以供用户单击,当用户单击按钮时,按钮 ...
- Android四大组件之——Activity的生命周期(图文详解)
转载请在文章开头处注明本博客网址:http://www.cnblogs.com/JohnTsai 联系方式:JohnTsai.Work@gmail.com [Andro ...
- 《从0到1学习Flink》—— Flink 配置文件详解
前面文章我们已经知道 Flink 是什么东西了,安装好 Flink 后,我们再来看下安装路径下的配置文件吧. 安装目录下主要有 flink-conf.yaml 配置.日志的配置文件.zk 配置.Fli ...
- Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)
View 的绘制系列文章: Android View 的绘制流程之 Measure 过程详解 (一) Android View 绘制流程之 DecorView 与 ViewRootImpl 在上一篇 ...
随机推荐
- [POJ2586]Y2K Accounting Bug
[POJ2586]Y2K Accounting Bug 试题描述 Accounting for Computer Machinists (ACM) has sufferred from the Y2K ...
- 下载老版本的Xcode
1.苹果开发者中心,找到Xcode 2.点击下载 3,找到Support 4.找到所需的版本,点击"+"下载 5.安装Xcode,愉快的开发.
- caffe学习系列(3):数据层介绍
一个模型由多个层构成,如Data,conv,pool等.其中数据层是模型的最底层,是模型的入口. 提供数据的输入,也提供数据从Blobs转换成别的格式进行保存输出还包括数据的预处理(如减去 均值, 放 ...
- CCF 模拟C 找最大矩形+输入输出外挂
http://115.28.138.223:81/view.page?opid=3 统计出连续的最长乘以当前高度,找最大即可 #include<iostream> #include< ...
- 用消息机制解耦Activity跳转
我见过的Activity方式有三种: 1, 默认的,在一个Activity里创建一个Intent,然后startActivity/startActivityForResult: 2, 给被跳转到的Ac ...
- css选择器(E[att^=”val”]序号选择器)
一.基本选择器序号 选择器 含义1. * 通用元素选择器,匹配任何元素2. E 标签选择器,匹配所有使用E标签的元素3. .info class选择器,匹配所有class属性中包含info的元素4. ...
- peewee 字段属性help_text的支持问题
至少在__version__ = '2.6.0'的时候,给字段添加help_text的时候,在数据库的ddl语句里面是没有comment的. 看了下源码,顺藤摸瓜,最终定位到了字段(Field类)的_ ...
- zend studio安装svn插件
进入zend->Help->Install New Software 输入如下地址: http://subclipse.tigris.org/update_1.8.x 选择subclips ...
- linux skill
linux console终端乱码解决 1.console终端乱码 在/etc/profile文件的最后一行添加如下内容: export LC_ALL="zh_CN.GB18030" ...
- Unity3d UGUI序列帧动画
代码 using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine. ...