Log.isLoggable之二源码解析

来源:互联网 发布:js根据name赋值 编辑:程序博客网 时间:2024/06/05 05:22

相关文章
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机制

原创粉丝点击