BuildConfig.DEBUG v.s. android:debuggable
BuildConfig是由IDE自动生成的,其值取决于 build variants。android:debuggable 也是IDE自动生成的,通常不需要人工去设置。不同之处在于,android:debuggable是在编译过程中在AndroidManifest.xml中插入的,而BuildConfig.java是编译过程中generate的。

<application
android:name=".ProxyApplication"
android:allowBackup="false"
android:debuggable="true">
...
</application>

使用方法都很简单:

if(BuildConfig.DEBUG){
Log.d(TAG,"");
}

int flags = getApplicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// development mode
} else {
// release mode
}

然而,当这两个都被设置时,哪个会生效呢?见下表:

application element Released APK In IDE
No android:debuggable FLAG_DEBUGGABLE bit is 0 and BuildConfig.DEBUG is false FLAG_DEBUGGABLE bit is 1 and BuildConfig.DEBUG is true
android:debuggable=”true” FLAG_DEBUGGABLE bit is 1 and BuildConfig.DEBUG is true FLAG_DEBUGGABLE bit is 1 and BuildConfig.DEBUG is true
android:debuggable=”false” FLAG_DEBUGGABLE bit is 0 and BuildConfig.DEBUG is false FLAG_DEBUGGABLE bit is 0 and BuildConfig.DEBUG is true

一句话总结上表:只有当android:debuggable=”false”时,BuildConfig.DEBUG才会按照预期的去指示debug,否则一切以android:debuggable为准。

不足:

在library project中,当发布aar包到maven库时,BuildConfig.DEBUG总是被设置为release,而无视了宿主 App project的IDE编译坏境

改进:

public class DebugUtils {
private static Boolean sDebug;

/**
* @return boolean
* BuildConfig.DEBUG will always return false in library projects
* see https://code.google.com/p/android/issues/detail?id=52962
*/
public static boolean isDebug() {
if (sDebug == null) {
try {
final String packageName = ApplicationContextGetter.instance().get().getPackageName();
final Class<?> buildConfig = Class.forName(packageName + ".BuildConfig");
final Field DEBUG = buildConfig.getField("DEBUG");
DEBUG.setAccessible(true);
sDebug = DEBUG.getBoolean(null);
} catch (final Throwable t) {
sDebug = false;
}
}
return sDebug;
}
}

其中ApplicationContextGetter如下:(或者传入Context)

可能的缺陷:

原来的字段是个static final 常亮,在编译时可以被inline优化,从而屏蔽了debug 的相关代码语句。现在由于需要在运行期动态的读取BuildConfig.DEBUG 的值,编译器就不会优化了。

改进方案 2 :

build.gradle

自己玩自己的,不用IDE生成的那一套

buildTypes {
debug {
buildConfigField "Boolean", "DEBUG_MODE", "true"
}
release {
buildConfigField "Boolean", "DEBUG_MODE", "false"
}
}

编译后会生成 如下字段BuildConfig.java

public final class BuildConfig {
// Fields from build type: debug
public static final Boolean DEBUG_MODE = true;
}

使用方法如下

if (BuildConfig.DEBUG_MODE) {
// do something
}

当然不一定非得是 boolean ,如果开发环境比较复杂,还可以用 String来判断。比如 “PreRelease” ,“Daily” , “Release”,这种方式比较灵活,但是 aar 在上传时会自动变为 release 的问题还是没法解决。

Log 的小改造(对性能有影响)

public class LogUtils {
private static final String TAG = "PFCommon";

private LogUtils() {
}

private static String logWithMethodAndLine(String log, StackTraceElement sElements) {
String className = sElements.getFileName();
String methodName = sElements.getMethodName();
int lineNumber = sElements.getLineNumber();
String fileName = sElements.getFileName();

StringBuilder stringBuilder = new StringBuilder(log);
stringBuilder.append(" at ")
.append(className)
.append(".")
.append(methodName)
.append(" (")
.append(fileName)
.append(":")
.append(lineNumber)
.append(") ");

return stringBuilder.toString();
}

public static void d(String tag, String msg) {
if (DebugUtils.isDebug()) {
Log.d(tag, logWithMethodAndLine(msg, new Throwable().getStackTrace()[2]));
}
}

public static void d(String msg) {
d(TAG, msg);
}

public static void e(String tag, String msg) {
Log.e(tag, logWithMethodAndLine(msg, new Throwable().getStackTrace()[2]));
}

public static void e(String tag, String msg, Throwable tr) {
Log.e(tag, logWithMethodAndLine(msg, new Throwable().getStackTrace()[2]), tr);
}

public static void e(String msg) {
e(TAG, msg);
}

public static void e(String msg, Throwable tr) {
e(TAG, msg, tr);
}

public static void logStackTrace(Throwable throwable) {
if (DebugUtils.isDebug() && throwable != null) {
throwable.printStackTrace();
}
}
}

new Throwable().getStackTrace()[2])这行语句中取的是第二层函数调用栈,这个根据实际情况可以自行调整

优化:

proguard-project.txt

# Remove debug logs
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}

上述配置会自动删掉log的相关代码
避免了上面的log代码在 release 中带来的额外开销。