Android 技巧 —— Debug 判斷不再用 BuildConfig

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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末憾筏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子花鹅,更是在濱河造成了極大的恐慌氧腰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刨肃,死亡現(xiàn)場離奇詭異古拴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)真友,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門黄痪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盔然,你說我怎么就攤上這事桅打。” “怎么了愈案?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵挺尾,是天一觀的道長。 經(jīng)常有香客問我站绪,道長遭铺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮魂挂,結(jié)果婚禮上甫题,老公的妹妹穿的比我還像新娘。我一直安慰自己涂召,他們只是感情好坠非,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芹扭,像睡著了一般麻顶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舱卡,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天辅肾,我揣著相機(jī)與錄音,去河邊找鬼轮锥。 笑死矫钓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舍杜。 我是一名探鬼主播新娜,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼既绩!你這毒婦竟也來了概龄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤饲握,失蹤者是張志新(化名)和其女友劉穎私杜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體救欧,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衰粹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了笆怠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铝耻。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹬刷,靈堂內(nèi)的尸體忽然破棺而出瓢捉,到底是詐尸還是另有隱情,我是刑警寧澤办成,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布泊柬,位于F島的核電站,受9級特大地震影響诈火,放射性物質(zhì)發(fā)生泄漏兽赁。R本人自食惡果不足惜状答,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刀崖。 院中可真熱鬧惊科,春花似錦、人聲如沸亮钦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜂莉。三九已至蜡娶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間映穗,已是汗流浹背窖张。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚁滋,地道東北人宿接。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像辕录,于是被迫代替她去往敵國和親睦霎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,285評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理走诞,服務(wù)發(fā)現(xiàn)副女,斷路器,智...
    卡卡羅2017閱讀 134,701評論 18 139
  • 1.介紹 如果你正在查閱build.gradle文件的所有可選項(xiàng)蚣旱,請點(diǎn)擊這里進(jìn)行查閱:DSL參考 1.1新構(gòu)建系統(tǒng)...
    Chuckiefan閱讀 12,142評論 8 72
  • Gradle是什么碑幅? Gradle 是以Groovy為基礎(chǔ),面向java應(yīng)用姻锁,基于DSL語法的自動化構(gòu)建工具枕赵。是g...
    Jinwong閱讀 8,155評論 1 65
  • 調(diào)試有歧義的布局 1. 使用hasAmbiguousLayout來測試約束是否充分 如果約束充分猜欺,則返回NO位隶,如果...
    兔迪哥閱讀 298評論 0 0