天天看点

Android使用BuildConfig类制作Logger工具类简述:技术预研:方案设计: 方案实现:方案比较:

    套用微信的广告语:再小的知识点也体现自己的态度                     -------开场语

简述:

       在Android开发中,我们使用android.util.Log来打印日志,一般我们会设置一个全局变量,标记软件是否显示日志,打包发布之前只要改下DEBUG=false就行了,但是每次在发布之前都要手动去改这个变量,是不是很不方便?对,那么有没有可以不用手动处理而又在发布时不打印日志的方法呢?,那么今天就来研究一个这个问题。

技术预研:

    ADT(r17)发布以后,Google为我们提供了一种新的调试机制,即BuildConfig.DEBUG。即:新增了一个特性,允许开发者只在Debug模式下运行部分代码。Builds会生成一个叫做BuildConfig的类,该类包含一个名为DEBUG的常量,其常量值会依据开发者的Build类型自动设定。如此,便可以利用BuildConfig.DEBUG来实现只在Debug模式下运行的代码。     我们先来看一下BuildConfig这个类在什么位置,我以新建的一个Demo为例:现将项目的形式改为Project,然后依次app—>build—>generated—>source—>BuildConfig,如下GIF动态图所示:            

Android使用BuildConfig类制作Logger工具类简述:技术预研:方案设计: 方案实现:方案比较:

    我们找到BuildConfig类以后可以发现该类的路径为:包名.BuildConfig.java.如下:

Android使用BuildConfig类制作Logger工具类简述:技术预研:方案设计: 方案实现:方案比较:

    通过技术预研现得出两个结论:         ① Android Studio中BuildConfig.java在app—>build—>generated—>source—>BuildConfig下;         ② BuildConfig.java的包路径为:包名.BuildConfig。

方案设计:

    方案一:BuildConfig类中DEBUG属性

        根据预研结论二我们在AS中找到BuildConfig类的包路径为:包名.BuildConfig,然后直接使用DEBUG属性或者为了程序的灵活性利用反射获取DEBUG属性值。

    方案二:自定义BuildConfig类

        根据预研结论一我们打开BuildConfig类,然后自定义一个属性,利用该其作为Logger的开关的属性。

 方案实现:

     方案一实现:

       ① 我们直接使用BuildConfig的DEBUG属性:

/**
 * @author:lizhenya
 * @Time: 2016/11/6
 * @email: [email protected]
 */

public class Logger {
    private static final String TAG = "Logger";

    /**
     * The override method of Logger.
     *
     * The default level of any tag is set to LOGLEVEL 5. This means that any
     * level log will be logged. if your set the LOGLEVEL to 0 , no log will be
     * print out.
     */
    public static int LOGLEVEL = BuildConfig.DEBUG? 5 : 0;
    public static boolean VERBOSE = LOGLEVEL > 4;
    public static boolean DEBUG = LOGLEVEL > 3;
    public static boolean INFO = LOGLEVEL > 2;
    public static boolean WARN = LOGLEVEL > 1;
    public static boolean ERROR = LOGLEVEL > 0;

    public static void setDebugMode(boolean debugMode) {
        LOGLEVEL = debugMode ? 5 : 0;
        VERBOSE = LOGLEVEL > 4;
        DEBUG = LOGLEVEL > 3;
        INFO = LOGLEVEL > 2;
        WARN = LOGLEVEL > 1;
        ERROR = LOGLEVEL > 0;
    }

    public static void v(String tag, String msg) {
        if (DEBUG)
            Log.v(tag, msg == null ? "" : msg);
    }

    public static void v(String tag, String msg, Throwable tr) {
        if (DEBUG)
            Log.v(tag, msg == null ? "" : msg, tr);
    }

    public static void v(String msg) {
        if (DEBUG)
            Log.v(TAG, msg == null ? "" : msg);
    }

    public static void v(String msg, Throwable tr) {
        if (DEBUG)
            Log.v(TAG, msg == null ? "" : msg, tr);
    }

    public static void d(String tag, String msg) {
        if (DEBUG)
            Log.d(tag, msg == null ? "" : msg);
    }

    public static void d(String tag, String msg, Throwable tr) {
        if (DEBUG)
            Log.d(tag, msg == null ? "" : msg, tr);
    }

    public static void d(String msg) {
        if (DEBUG)
            Log.d(TAG, msg == null ? "" : msg);
    }

    public static void d(String msg, Throwable tr) {
        if (DEBUG)
            Log.d(TAG, msg == null ? "" : msg, tr);
    }

    public static void e(String tag, String msg) {
        if (ERROR)
            Log.e(tag, msg == null ? "" : msg);
    }

    public static void e(String tag, String msg, Throwable tr) {
        if (ERROR)
            Log.e(tag, msg == null ? "" : msg, tr);
    }

    public static void e(String msg) {
        if (ERROR)
            Log.e(TAG, msg == null ? "" : msg);
    }

    public static void e(String msg, Throwable tr) {
        if (ERROR)
            Log.e(TAG, msg == null ? "" : msg, tr);
    }

}
           

        ②使用反射获取DEBUG属性值以提高程序的灵活性:

            首先我们写一个获取DEBUG属性值的工具类

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author:lizhenya
 * @Time: 2016/11/6
 * @email: [email protected]
 */

public class BuildConfigHelper {


    public static Boolean isDebug = null;

    public static boolean isDebugBuild() {
        if (isDebug == null) {
            try {
                final Class<?> activityThread = Class.forName("android.app.ActivityThread");
                final Method currentPackage = activityThread.getMethod("currentPackageName");
                final String packageName = (String) currentPackage.invoke(null, (Object[]) null);
                final Class<?> buildConfig = Class.forName(packageName + ".BuildConfig");
                final Field DEBUG = buildConfig.getField("DEBUG");
                DEBUG.setAccessible(true);
                isDebug = DEBUG.getBoolean(null);
            } catch (final Throwable t) {
                final String message = t.getMessage();
                if (message != null && message.contains("BuildConfig")) {
                    isDebug = false;
                } else {
                    isDebug = BuildConfig.DEBUG;
                }
            }
        }
        return isDebug;
    }
}
           

         其次再在修改Logger类中修改获取BuildConfig的DEBUG属性的方式:

public static int LOGLEVEL = BuildConfigHelper.isDebug ? 5 : 0;
           

    方案二实现:

        我们首先在BuildConfig类中定义一个Logger开关的属性值:LOG_TOGGLE,完整的BuildConfig类如下:

/**
 * Automatically generated file. DO NOT MODIFY
 */
package com.lzy.buildconfigdemo;

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.lzy.buildconfigdemo";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
  // Fields from build type: debug()我们自定义的一个控制Log开关的属性值
  public static final boolean LOG_TOGGLE = true;
}
           

   我们知道BuildConfig类是Gradle编译过程中自动生成的,所以我们得把刚才自定义的属性配置到build.gradle中,如下:

buildTypes {
        release {
            buildConfigField "boolean", "LOG_TOGGLE", "false"
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug{
            buildConfigField "boolean", "LOG_TOGGLE", "true"
        }
    }
           

      在上面我们定义了一个:

buildConfigField "boolean", "LOG_TOGGLE", "false"
           

      这个语法规则表示:我们定义了一个boolean变量,变量名为LOG_TOGGLE,并给变量赋值为false。

    我们使用如下:

findViewById(R.id.btn_buildConfig).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (BuildConfig.LOG_TOGGLE) {
                    Toast.makeText(MainActivity.this, "Debug", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "Release", Toast.LENGTH_SHORT).show();
                }
            }
        });
           

    OK,方案二流程已经熟悉了,那么我们现在在封装Logger类如下:

public class Logger {
    private static final String TAG = "Logger";

    /**
     * The override method of Logger.
     * <p>
     * The default level of any tag is set to LOGLEVEL 5. This means that any
     * level log will be logged. if your set the LOGLEVEL to 0 , no log will be
     * print out.
     */
    public static int LOGLEVEL = BuildConfig.LOG_TOGGLE ? 5 : 0;
    public static boolean VERBOSE = LOGLEVEL > 4;
    public static boolean DEBUG = LOGLEVEL > 3;
    public static boolean INFO = LOGLEVEL > 2;
    public static boolean WARN = LOGLEVEL > 1;
    public static boolean ERROR = LOGLEVEL > 0;

    public static void setDebugMode(boolean debugMode) {
        LOGLEVEL = debugMode ? 5 : 0;
        VERBOSE = LOGLEVEL > 4;
        DEBUG = LOGLEVEL > 3;
        INFO = LOGLEVEL > 2;
        WARN = LOGLEVEL > 1;
        ERROR = LOGLEVEL > 0;
    }

    public static void v(String tag, String msg) {
        if (DEBUG)
            Log.v(tag, msg == null ? "" : msg);
    }

    public static void v(String tag, String msg, Throwable tr) {
        if (DEBUG)
            Log.v(tag, msg == null ? "" : msg, tr);
    }

    public static void v(String msg) {
        if (DEBUG)
            Log.v(TAG, msg == null ? "" : msg);
    }

    public static void v(String msg, Throwable tr) {
        if (DEBUG)
            Log.v(TAG, msg == null ? "" : msg, tr);
    }

    public static void d(String tag, String msg) {
        if (DEBUG)
            Log.d(tag, msg == null ? "" : msg);
    }

    public static void d(String tag, String msg, Throwable tr) {
        if (DEBUG)
            Log.d(tag, msg == null ? "" : msg, tr);
    }

    public static void d(String msg) {
        if (DEBUG)
            Log.d(TAG, msg == null ? "" : msg);
    }

    public static void d(String msg, Throwable tr) {
        if (DEBUG)
            Log.d(TAG, msg == null ? "" : msg, tr);
    }

    public static void e(String tag, String msg) {
        if (ERROR)
            Log.e(tag, msg == null ? "" : msg);
    }

    public static void e(String tag, String msg, Throwable tr) {
        if (ERROR)
            Log.e(tag, msg == null ? "" : msg, tr);
    }

    public static void e(String msg) {
        if (ERROR)
            Log.e(TAG, msg == null ? "" : msg);
    }

    public static void e(String msg, Throwable tr) {
        if (ERROR)
            Log.e(TAG, msg == null ? "" : msg, tr);
    }

}
           

方案比较:

    两种方案比较,第一种方案直接利用BuildConfig类的DEBUG属性,这种情况适用于API开发环境较为简单的情况,比如就一个测试环境和一个正式环境。第二种方案我们自定义的一个专门的日志开关属性,这样可以适用于多种api开发环境。