開發階段利用 Log 日誌方便代碼調試是再常見不過的事情。出於安全考慮,這種作法僅限於 Debug 模式,Release 模式下打包發佈時必定要關掉。因此在咱們的項目中,必定會有一個工具類或者方法來控制 Log 日誌的使用,好比:java
public class LogUtils {
public static final Boolean DEBUG_MODE = true;
public static void d(String message) {
if (DEBUG_MODE) {
Log.d("TAG", message);
}
}
}複製代碼
常見的作法即是像上面這樣,自定義一個布爾類型的常量做爲開關來控制是否打印日誌。可是這種作法有一個弊端,那就是每次發佈 Release 包時都須要手動修改這個常量的值爲 false,而後下一次開發階段再手動修改成 true。android
雖然是很簡單的手動修改操做,可是也很容易忘記。那麼有沒有一種辦法實現自動化管理呢?答案固然是有的,使用 BuildConfig 類。程序員
相似 R 資源文件,BuildConfig 也是在編譯階段,Gradle 插件自動生成的一個 class 文件。該文件包含一些幫助開發人員辨別當前 build 類型的常量信息。固然你也能夠經過 Gradle 提供的定製功能向該文件裏面添加其餘輔助內容。這裏咱們看一下默認狀況下,BuildConfig 文件都包含有哪些內容:安全
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.yifeng.sample";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
}複製代碼
可以看出,都是一些你們很熟悉的信息。其中包括一個 DEBUG 常量,其值即可用於判斷當前 build 類型。debug 模式下爲 true,release 模式下爲 false。因此,使用 BuildConfig.DEBUG 能夠替代前面咱們自定義的常量,實現自動管理 Log 日誌的打印:bash
public static void d(String message) {
if (BuildConfig.DEBUG) {
Log.d("TAG", message);
}
}複製代碼
看上去貌似已經很完美了,但其實仍是有瑕疵的。BuildConfig 類文件的生成依據於 Module,也就是說每個 Module 編譯時都會產生本身的這個文件。若是你的主 app module 使用其餘依賴 module 中 BuildConfig 文件裏面的 DEBUG 值,就須要多加註意。微信
默認狀況下,Library 的構建永遠是以 Release 模式執行的,因此其 BuildConfig.DEBUG 值必定是 false!即便主 Module 使用 Debug 模式構建,也是如此。app
那麼,有沒有辦法修改 Library Module 的默認構建方式呢?答案也是確定的。打開對應 Library 的 build.gradle 文件,添加這樣一行配置代碼:工具
android {
// 這裏省略其餘內容
publishNonDefault true
}複製代碼
即表示不使用默認構建方式,編譯時也會自動生成其餘 build 類型的 BuildConfig 類文件。你能夠在相應 Library 路徑下查看配置該命令先後 BuildConfig 文件的生成狀況,目錄地址爲:gradle
libraryName/build/generated/source/buildConfig/ + debug/release複製代碼
而後在咱們的主 Module 依賴的時候同時引入 debug 和 release 兩種配置,這裏以 extras/PullToRefresh 做爲 Library 爲例,看下依賴代碼:優化
dependencies {
releaseCompile project(path: ':extras:PullToRefresh', configuration: 'release')
debugCompile project(path: ':extras:PullToRefresh', configuration: 'debug')
}複製代碼
如此這般,即可以解決前面提到的依賴 Module 問題。固然,若是你的項目比較簡單,只是單一 Module,也就不存在這個問題。
可是若是項目中的依賴 Module 比較多的話,這種處理方式仍是略顯麻煩。你須要在用到的地方針對每一個 Module 逐一處理。其實還有一種更好的解決方案,那就是使用 Manifest 清單文件中 application 標籤裏的 debuggable 屬性。
application 標籤裏有個 android:debuggable 屬性,表示當前應用是否能夠被調試(通常不建議手動設置這個屬性)。這個屬性也會隨着 build 類型自動改變。因此,利用這個特性也能斷定應用是否處於 Debug 模式,好比:
public static boolean isDebug(Context context) {
return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}複製代碼
控制 Log 日誌打印的開關,除了上面講到的這些方式,其實還有別的方式。好比利用 Gradle 的靈活性在 build.gradle 文件中自定義一個 Boolean 變量,根據 build 類型動態賦值,也能達到咱們的目的。
前面全部這些作法都只是使 release 包不去顯示 Log 日誌,從而提升安全性。可是,有沒有想過,若是 apk 被反編譯的話,這些 Log 相關的代碼仍是可以別識別出來,別人只須要稍做修改,從新打包,依舊可以使 Log 重現。
固然,使用常量做爲 LogUtils 中的判斷條件的話,根據 proguard 的優化規則,在 Release 包中是不包含條件體中的 Log.d 等操做代碼的。關於這一點,能夠本身反編譯 apk 嘗試看下。
然而,在其餘調用 LogUtils 工具類的地方依舊暴露了咱們的意圖。因此,定義一個 LogUtils 類雖然提升了使用 Log 的效率,依舊解決不了 Log 安全的問題。相比而言,咱們作了這麼多努力只是稍微提升了一些安全的門檻而已。
因此,最好的辦法就是,Release 包中不包含任何用於調試的 Log 代碼(若是使用 LogUtils 的話,也包括 該類的調用)。也就是說,不使用 LogUtils 工具類封裝,在任何須要的地方,不嫌麻煩的逐一添加判斷條件:(可使用 Live Template 提升效率)
if (BuildConfig.DEBUG) {
Log.d("TAG", message);
}複製代碼
這樣,打包時,開啓 proguard 後,Release 包會自動刪除上面的代碼,完全根絕 Log 引起的安全問題。關於這一部分的細節操做,能夠參考這兩篇文章:
(END)
關於我:亦楓,博客地址:yifeng.studio/,新浪微博:IT亦楓
微信掃描二維碼,歡迎關注個人我的公衆號:安卓筆記俠
不只分享個人原創技術文章,還有程序員的職場遐想
![]()