相关文章
Log.isLoggable之一正确的使用姿势
Log.isLoggable之二源码解析
简介
上一篇文章Log.isLoggable之一正确的使用姿势讲了Log.isLoggable使用,本文就来讲讲isLoggable的源码实现。
Log.isLoggable源码分析
首先,来看看Log.java中isLoggable的实现。通过源码我们可以知道isLoggable是一个JNI方法是通过CPP实现的。但是这里也有比较详细的注释。如果英文比较好的同学可以看下英文注解,这里就不细说了,直接看CPP源码。
frameworks/base/core/java/android/util/Log.java
/**
* 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;
/**
* 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);
frameworks/rs/rsCompatibilityLib.h
#define PROPERTY_KEY_MAX 32
frameworks/base/core/jni/android_util_Log.cpp
#define LOG_NAMESPACE "log.tag."
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
struct levels_t {
jint verbose;
jint debug;
jint info;
jint warn;
jint error;
jint assert;
};
static levels_t levels;
static int toLevel(const char* value)
{
//根据首字符的值设置相应的log级别,注意首字符要大写
switch (value[0]) {
case 'V': return levels.verbose;
case 'D': return levels.debug;
case 'I': return levels.info;
case 'W': return levels.warn;
case 'E': return levels.error;
case 'A': return levels.assert;
case 'S': return -1; // SUPPRESS
}
//如果都没有配备到上面的字符,就返回一个默认的info级别
return levels.info;
}
static jboolean isLoggable(const char* tag, jint level) {
String8 key;
//将log.tag.<tag>连接起来成一个字符串
key.append(LOG_NAMESPACE);
key.append(tag);
char buf[PROPERTY_VALUE_MAX];
//获取这个字符串属性的值,如果没有获取到值就给其赋空值
if (property_get(key.string(), buf, "") <= 0) {
buf[0] = '\0';
}
int logLevel = toLevel(buf);
//这个里可以看出,代码设置的级别不小于通过属性获取的级别就会返回true了。
return logLevel >= 0 && level >= logLevel;
}
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
if (tag == NULL) {
return false;
}
const char* chars = env->GetStringUTFChars(tag, NULL);
if (!chars) {
return false;
}
jboolean result = false;
//判断log.tag.<tag>的长度是否大于32,如果大于32就报异常,所以我们给tag设置字符串的时候不要过长
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
char buf2[200];
snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %zu characters\n",
chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
} else {
//真正处理逻辑的地方
result = isLoggable(chars, level);
}
env->ReleaseStringUTFChars(tag, chars);
return result;
}
int register_android_util_Log(JNIEnv* env)
{
jclass clazz = env->FindClass("android/util/Log");
if (clazz == NULL) {
ALOGE("Can't find android/util/Log");
return -1;
}
//通过反射获取Log.java上对应的值
levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));
levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));
levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));
levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));
return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
}
在上面的代码中,我已列出了重点部分的注解,相信大部分朋友都能看懂。它最重要的部分就是static jboolean isLoggable(const char* tag, jint level)这个函数了。这个函数里面会去获取与tag相关property的Log级别,并与代码设置的Log级别比较返回相应的true还是false。
property的加载源码分析
下面重点分析下property_get方法获取对应的属性值时,我们可以设置property值的方法。
一种方法是adb shell setprop,这种方法灵活方便,但是生命周期有限,机器设备重启即无效。
另一种方法就是将属性写入prop文件中,这种方法就可以永久生效。下面我们通过源码来分析下这种方法都有哪些prop文件可以写入。
bionic/libc/include/sys/_system_properties.h
#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop"
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
#define PROP_PATH_FACTORY "/factory/factory.prop"
Android系统在加载init.rc文件后会去解析各个prop文件。
system/core/init/property_service.c
void load_all_props(void)
{
//加载/system/build.prop属性文件
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
//加载/system/default.prop属性文件
load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
//加载/vendor/build.prop属性文件
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
//加载/factory/factory.prop属性文件
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
//加载/data/local.prop属性文件
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
}
static void load_override_properties() {
#ifdef ALLOW_LOCAL_PROP_OVERRIDE
char debuggable[PROP_VALUE_MAX];
int ret;
ret = property_get("ro.debuggable", debuggable);
//加载/data/local.prop属性文件有两个判断条件。1.定义了ALLOW_LOCAL_PROP_OVERRIDE,2.ro.debuggable=1
//只有满足这两个条件才会去加载/data/local.prop属性文件
if (ret && (strcmp(debuggable, "1") == 0)) {
load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
}
#endif /* ALLOW_LOCAL_PROP_OVERRIDE */
}
通过上面的源码我们知道系统会去加载各个prop文件,但通常上面加载的prop文件不一定全部都有,但是/system/build.prop一定会有。同时,如果我们去改/data/local.prop文件不一定会生效,因为系统不一定会去加载这个文件。因为它有两个判断条件需要满足,当然这只是我从5.1的系统分析,不同的系统会有差别。以实际系统为准。
参考文章
Android 5.0 如何正确启用isLoggable(一)__使用详解
Android 5.0 如何正确启用isLoggable(二)__原理分析
深入讲解Android Property机制