您的位置:首页 > 其它

Log.isLoggable之二源码解析

2017-06-05 20:11 363 查看
相关文章

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机制
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: