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) { } else { }
|
然而,当这两个都被设置时,哪个会生效呢?见下表:
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;
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 { public static final Boolean DEBUG_MODE = true; }
|
使用方法如下
if (BuildConfig.DEBUG_MODE) { }
|
当然不一定非得是 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 中带来的额外开销。