Android 開發(fā)中一般會通過 BuildConfig.DEBUG 判斷是否是 Debug 模式养叛,從而做一些在 Debug 模式才開啟的特殊操作借宵,比如打印日志洲押。這樣好處是不用在發(fā)布前去主動修改喇闸,因?yàn)檫@個值在 Debug 模式下為 true修赞,Release 模式下為 false。
1. 問題
如果應(yīng)用只有一個 Module 沒有問題稚配,Debug 模式下 BuildConfig.DEBUG 會始終為 true畅涂。如果現(xiàn)在有兩個 Module,分別為 App 和 Lib道川,且 App 依賴 Lib午衰,在 Lib 內(nèi)有工具類 LogUtils,代碼如下:
package cn.trinea.android.lib.util;
import android.util.Log;
import cn.trinea.android.lib.util.BuildConfig;
public class LogUtils {
public static void d(String log) {
if (BuildConfig.DEBUG) {
Log.d("trinea-debug", log);
}
}
……
}
當(dāng)我們在 App Module 內(nèi)調(diào)用 LogUtils 時我們會發(fā)現(xiàn)始終無法打印日志冒萄,因?yàn)樯厦娴?BuildConfig.DEBUG 會始終為 false臊岸。為什么呢?
2. 原因
BuildConfig.java 是編譯時自動生成的尊流,并且每個 Module 都會生成一份帅戒,以該 Module 的 packageName 為 BuildConfig.java 的 packageName。所以如果你的應(yīng)用有多個 Module 就會有多個 BuildConfig.java 生成崖技,而上面的 Lib Module import 的是自己的 BuildConfig.java逻住,編譯時被依賴的 Module 默認(rèn)會提供 Release 版給其他 Module 或工程使用,這就導(dǎo)致該 BuildConfig.DEBUG 會始終為 false迎献。
3. 解決方案
根據(jù)上面分析的原因瞎访,目前我們有兩個思路:
(1) 始終調(diào)用最終運(yùn)行的 Module 的 BuildConfig,因?yàn)樗鼪]有被任何其他 Module 依賴吁恍,所以 BuildConfig.DEBUG 值會準(zhǔn)確扒秸。
(2) 讓被依賴的 Module 提供除 Release 版以外的其他版本。
3.1 解決方案一:使用其他的 BuildConfig.java
如果 Lib Module 中能夠 import 到外層真正運(yùn)行 App 的 BuildConfig 就 ok 了冀瓦,如下:
package cn.trinea.android.lib.util;
/**
* Utils for App
* <ul>
* <li>{@link #syncIsDebug(Context)} Should be called in module Application</li>
* </ul>
* Created by Trinea on 2017/3/9.
*/
public class AppUtils {
private static Boolean isDebug = null;
public static boolean isDebug() {
return isDebug == null ? false : isDebug.booleanValue();
}
/**
* Sync lib debug with app's debug value. Should be called in module Application
*
* @param context
*/
public static void syncIsDebug(Context context) {
if (isDebug == null) {
try {
String packageName = context.getPackageName();
Class buildConfig = Class.forName(packageName + ".BuildConfig");
Field DEBUG = buildConfig.getField("DEBUG");
DEBUG.setAccessible(true);
isDebug = DEBUG.getBoolean(null);
} catch (Throwable t) {
// Do nothing
}
}
}
}
通過反射得到真正執(zhí)行的 Module 的 BuildConfig伴奥,在自己的 Application 內(nèi)調(diào)用:
AppUtils.syncIsDebug(getApplicationContext());
這樣看起來達(dá)到目的了。
但仔細(xì)想想會發(fā)現(xiàn)這種解決方案還是有問題翼闽,因?yàn)?BuildConfig.java 的 packageName 是 Module 的 Package Name渔伯,即 AndroidManifest.xml 中的 package 屬性,而 context.getPackageName() 得到的是應(yīng)用的 applicationId肄程,這個 applicationId 通過 build.gradle 是可以修改的锣吼。所以當(dāng) build.gradle 中的 applicationId 與 AndroidManifest.xml 中的 package 屬性不一致時,上面的反射查找類路徑便會出錯蓝厌。
PS:這種方案還有個變種就是通過 android.app.ActivityThread.currentPackageName 得到包名玄叠,從而省去傳遞 Context 初始化的步驟,但依然有 applicationId 被修改后類查找不到類似的問題拓提。
3.2 解決方案二:被依賴的 Module 提供其他版本
讓被依賴的 Module 提供除 Release 版以外的其他版本读恃,這種方案需要將所有被依賴 library 中添加:
android {
publishNonDefault true
}
表示該 Module 打包時會同時打包其他版本,包括 Debug 版。并且需要在 App Module 中將其依賴的 library 如下逐個添加:
dependencies {
releaseCompile project(path: ':library', configuration: 'release')
debugCompile project(path: ':library', configuration: 'debug')
}
表示依賴不同版本的依賴 Module寺惫。
然而這種方式所有 Module 配置都需要修改疹吃,侵入性太強(qiáng)。
3.3 最終解決方案:使用 ApplicationInfo.FLAG_DEBUGGABLE
既然 BuildConfig 的方式行不通西雀,我們反編譯 Debug 包和 Release 包對比看看有沒有其他的區(qū)別萨驶,會發(fā)現(xiàn)他們 AndroidManifest.xml 中 application 節(jié)點(diǎn)的 android:debuggable 值是不同的。Debug 包值為 true艇肴,Release 包值為 false腔呜,這是編譯自動修改的。所以我們考慮通過 ApplicationInfo 的這個屬性去判斷是否是 Debug 版本再悼,如下:
package cn.trinea.android.lib.util;
/**
* Utils for App
* <ul>
* <li>{@link #syncIsDebug(Context)} Should be called in module Application</li>
* </ul>
* Created by Trinea on 2017/3/9.
*/
public class AppUtils {
private static Boolean isDebug = null;
public static boolean isDebug() {
return isDebug == null ? false : isDebug.booleanValue();
}
/**
* Sync lib debug with app's debug value. Should be called in module Application
*
* @param context
*/
public static void syncIsDebug(Context context) {
if (isDebug == null) {
isDebug = context.getApplicationInfo() != null &&
(context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
}
}
在自己的 Application 內(nèi)調(diào)用進(jìn)行初始化核畴,
AppUtils.syncIsDebug(getApplicationContext());
這樣以后調(diào)用 AppUtils.isDebug() 即可判斷是否是 Debug 版本,比如在上面的 LogUtils 中冲九。同時適用于 Module 是 Lib 和 applicationId 被修改的情況谤草,比 BuildConfig.DEBUG 靠譜的多。
這個方案有個注意事項(xiàng)就是自己 App Module 中不能主動設(shè)置 android:debuggable莺奸,否則無論 Debug 還是 Release 版會始終是設(shè)置的值丑孩。當(dāng)然本身就沒有自動設(shè)置的必要。
原文地址:http://www.trinea.cn/android/android-whether-debug-mode-why-buildconfig-debug-always-false/