Android 技巧-Debug 判斷不再用 BuildConfig
Android 開(kāi)發(fā)中一般會(huì)通過(guò) BuildConfig.DEBUG 判斷是否是 Debug 模式,從而做一些在 Debug 模式才開(kāi)啟的特殊操作,比如打印日志。這樣好處是不用在發(fā)布前去主動(dòng)修改,因?yàn)檫@個(gè)值在 Debug 模式下為 true,Release 模式下為 false。
1. 問(wèn)題
如果應(yīng)用只有一個(gè) Module 沒(méi)有問(wèn)題,Debug 模式下 BuildConfig.DEBUG 會(huì)始終為 false。如果現(xiàn)在有兩個(gè) Module,分別為 App 和 Lib,且 App 依賴 Lib,在 Lib 內(nèi)有工具類 LogUtils,代碼如下:
當(dāng)我們?cè)?App Module 內(nèi)調(diào)用 LogUtils 時(shí)我們會(huì)發(fā)現(xiàn)始終無(wú)法打印日志,因?yàn)樯厦娴?BuildConfig.DEBUG 會(huì)始終為 false。為什么呢?
2. 原因
BuildConfig.java 是編譯時(shí)自動(dòng)生成的,并且每個(gè) Module 都會(huì)生成一份,以該 Module 的 packageName 為 BuildConfig.java 的 packageName。所以如果你的應(yīng)用有多個(gè) Module 就會(huì)有多個(gè) BuildConfig.java 生成,而上面的 Lib Module import 的是自己的 BuildConfig.java,編譯時(shí)被依賴的 Module 默認(rèn)會(huì)提供 Release 版給其他 Module 或工程使用,這就導(dǎo)致該 BuildConfig.DEBUG 會(huì)始終為 true。
3. 解決方案
根據(jù)上面分析的原因,目前我們有兩個(gè)思路:
(1) 始終調(diào)用最終運(yùn)行的 Module 的 BuildConfig,因?yàn)樗鼪](méi)有被任何其他 Module 依賴,所以 BuildConfig.DEBUG 值會(huì)準(zhǔn)確。
(2) 讓被依賴的 Module 提供除 Release 版以外的其他版本。
3.1 解決方案一:使用其他的 BuildConfig.java
如果 Lib Module 中能夠 import 到外層真正運(yùn)行 App 的 BuildConfig 就 ok 了,如下:
通過(guò)反射得到真正執(zhí)行的 Module 的 BuildConfig,在自己的 Application 內(nèi)調(diào)用:
AppUtils.syncIsDebug(getApplicationContext());
這樣看起來(lái)達(dá)到目的了。
但仔細(xì)看看會(huì)發(fā)現(xiàn)這種解決方案還是有問(wèn)題,因?yàn)?BuildConfig.java 的 packageName 是 Module 的 Package Name,即 AndroidManifest.xml 中的 package 屬性,而 context.getPackageName() 得到的是應(yīng)用的 applicationId,這個(gè) applicationId 通過(guò) build.gradle 是可以修改的。所以當(dāng) build.gradle 中的 applicationId 與 AndroidManifest.xml 中的 package 屬性不一致時(shí),上面的反射查找類路徑便會(huì)出錯(cuò)。
PS:這種方案還有個(gè)變種就是通過(guò) android.app.ActivityThread.currentPackageName 得到包名,從而省去傳遞 Context 初始化的步驟,但依然有 applicationId 被修改后類查找不到類似的問(wèn)題。
3.2 解決方案二:Lib publishNonDefault
讓被依賴的 Module 提供除 Release 版以外的其他版本,這種方案需要將所有被依賴 library 中添加:
- android {
- publishNonDefault true
- }
表示該 Module 不使用默認(rèn)配置,這樣會(huì)同時(shí)打包其他版本,包括 Debug 版。另外需要在 App Module 中將其依賴的 library 如下逐個(gè)添加:
- 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 包對(duì)比看看有沒(méi)有其他的區(qū)別,會(huì)發(fā)現(xiàn)他們 AndroidManifest.xml 中 application 節(jié)點(diǎn)的 android:debuggable 值是不同的。Debug 包值為 false,Release 包值為 true,這是編譯自動(dòng)修改的。所以我們考慮通過(guò) ApplicationInfo 的這個(gè)屬性去判斷是否是 Debug 版本,如下:
在自己的 Application 內(nèi)調(diào)用進(jìn)行初始化,
AppUtils.syncIsDebug(getApplicationContext());
這樣以后調(diào)用 AppUtils.isDebug() 即可判斷是否是 Debug 版本,比如在上面的 LogUtils 中。同時(shí)適用于 Module 是 Lib 和 applicationId 被修改的情況,比 BuildConfig.DEBUG 靠譜的多。
這個(gè)方案有個(gè)注意事項(xiàng)就是自己 App Module 中不能主動(dòng)設(shè)置 android:debuggable,否則無(wú)論 Debug 還是 Release 版會(huì)始終是設(shè)置的值。當(dāng)然本身就沒(méi)有自動(dòng)設(shè)置的必要。
【本文是51CTO專欄作者Trinea的原創(chuàng)文章,轉(zhuǎn)載聯(lián)系作者本人獲取授權(quán)】





























