Android 技巧 - Debug 判断不再用 BuildConfig
Android 开发中一般会通过 BuildConfig.DEBUG 判断是否是 Debug 模式,从而做一些在 Debug 模式才开启的特殊操作,比如打印日志。这样好处是不用在发布前去主动修改,因为这个值在 Debug 模式下为 true,Release 模式下为 false。
1. 问题
如果应用只有一个 Module 没有问题,Debug 模式下 BuildConfig.DEBUG 会始终为 false。如果现在有两个 Module,分别为 App 和 Lib,且 App 依赖 Lib,在 Lib 内有工具类 LogUtils,代码如下:

当我们在 App Module 内调用 LogUtils 时我们会发现始终无法打印日志,因为上面的 BuildConfig.DEBUG 会始终为 false。为什么呢?
2. 原因
BuildConfig.java 是编译时自动生成的,并且每个 Module 都会生成一份,以该 Module 的 packageName 为 BuildConfig.java 的 packageName。所以如果你的应用有多个 Module 就会有多个 BuildConfig.java 生成,而上面的 Lib Module import 的是自己的 BuildConfig.java,编译时被依赖的 Module 默认会提供 Release 版给其他 Module 或工程使用,这就导致该 BuildConfig.DEBUG 会始终为 true。
3. 解决方案
根据上面分析的原因,目前我们有两个思路:
(1) 始终调用最终运行的 Module 的 BuildConfig,因为它没有被任何其他 Module 依赖,所以 BuildConfig.DEBUG 值会准确。
(2) 让被依赖的 Module 提供除 Release 版以外的其他版本。
3.1 解决方案一:使用其他的 BuildConfig.java
如果 Lib Module 中能够 import 到外层真正运行 App 的 BuildConfig 就 ok 了,如下:

通过反射得到真正执行的 Module 的 BuildConfig,在自己的 Application 内调用:
AppUtils.syncIsDebug(getApplicationContext());
这样看起来达到目的了。
但仔细看看会发现这种解决方案还是有问题,因为 BuildConfig.java 的 packageName 是 Module 的 Package Name,即 AndroidManifest.xml 中的 package 属性,而 context.getPackageName() 得到的是应用的 applicationId,这个 applicationId 通过 build.gradle 是可以修改的。所以当 build.gradle 中的 applicationId 与 AndroidManifest.xml 中的 package 属性不一致时,上面的反射查找类路径便会出错。
PS:这种方案还有个变种就是通过 android.app.ActivityThread.currentPackageName 得到包名,从而省去传递 Context 初始化的步骤,但依然有 applicationId 被修改后类查找不到类似的问题。
3.2 解决方案二:Lib publishNonDefault
让被依赖的 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 配置都需要修改,侵入性太强。
3.3 最终解决方案:使用 ApplicationInfo.FLAG_DEBUGGABLE
既然 BuildConfig 的方式行不通,我们反编译 Debug 包和 Release 包对比看看有没有其他的区别,会发现他们 AndroidManifest.xml 中 application 节点的 android:debuggable 值是不同的。Debug 包值为 false,Release 包值为 true,这是编译自动修改的。所以我们考虑通过 ApplicationInfo 的这个属性去判断是否是 Debug 版本,如下:

在自己的 Application 内调用进行初始化,
AppUtils.syncIsDebug(getApplicationContext());
这样以后调用 AppUtils.isDebug() 即可判断是否是 Debug 版本,比如在上面的 LogUtils 中。同时适用于 Module 是 Lib 和 applicationId 被修改的情况,比 BuildConfig.DEBUG 靠谱的多。
这个方案有个注意事项就是自己 App Module 中不能主动设置 android:debuggable,否则无论 Debug 还是 Release 版会始终是设置的值。当然本身就没有自动设置的必要。
Android 技巧 - Debug 判断不再用 BuildConfig的更多相关文章
- Android 网络连接判断与处理
		
Android网络连接判断与处理 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限. <uses-permission android:name="and ...
 - Android studio Debug效率提升
		
Android studio Debug效率提升,可以在控制台打印log的同时而不暂停程序的运行,尤其是当遇到复杂交互的时候,比如滑动,拖动,这时候程序暂停执行是特别恶心的.其实你可以更新打印信息而不 ...
 - Android中如何判断是否联网
		
首先在AndroidManifest.xml中添加与连接网络相关的权限: [xhtml] view plain copy <uses-permission android:name=&qu ...
 - Android device debug (adb) by Charge Only mode
		
Android device debug by Charge Only mode Method 1 Connect devices to computer and execute lsusb Find ...
 - 安卓 运行、调试 配置  android  Run/debug configurations
		
android 运行.调试 配置 android Run/debug configurations 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq. ...
 - 强制开启android webview debug模式使用Chrome inspect
		
强制开启android webview debug模式使用Chrome inspect https://blog.csdn.net/zhulin2609/article/details/5143782 ...
 - Android技巧分享——如何用电脑下载在Google play中应用的apk文件
		
[Android技巧分享系列] 1.Android技巧分享——让官方模拟器和genymotion虚拟机飞起来 2.Android技巧分享——如何用电脑下载在Google play中应用的apk文件 G ...
 - iOS开发小结 - 关于swift中#if DEBUG判断不执行的解决方案
		
在以前的OC项目中我们经常用#if DEBUG来判断是否是在DEBUG模式下调试代码,然后最近我用swift写的项目中发现#if DEBUG判断居然不管用了,多方查证为果的情况下终于找到了解决方案,希 ...
 - iOS remote debug & Android remote debug & Chrome & APP
		
iOS remote debug & Android remote debug & Chrome & APP iOS remote debugging 如何在 iOS 真机上调 ...
 
随机推荐
- OPENCV(5) —— 图像直方图
			
新版本对直方图不再使用之前的histogram的形式,而是用统一的Mat或者MatND的格式来存储直方图,可见新版本Mat数据结构的优势. C++: void calcHist(const Mat* ...
 - file---探测给定文件的类型
			
file命令用来探测给定文件的类型.file命令对文件的检查分为文件系统.魔法幻数检查和语言检查3个过程. 语法 file(选项)(参数) 选项 -b:列出辨识结果时,不显示文件名称: -c:详细显示 ...
 - 紫书 习题 10-21 UVa 1649 (组合数)
			
C(n, k) = m, 固定k,枚举k 这里用到了组合数的一个性质 当k固定的时候,C(2 * k, k) 最小 C(m, k)最大(对于这道题而言是这样,因为大于m 就最终答案不可能为m了) 所以 ...
 - 【Henu ACM Round#15 A】 A and B and Chess
			
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 统计大写和小写的个数. 比较答案.输出即可. [代码] #include <bits/stdc++.h> using n ...
 - CSUOJ 1542 Flipping Parentheses
			
ACM International Collegiate Programming Contest Asia Regional Contest, Tokyo, 2014–10–19 Problem G ...
 - androidclient和站点数据交互的实现(基于Http协议获取数据方法)
			
androidclient一般不直接訪问站点数据库,而是像浏览器一样发送get或者post请求.然后站点返回client能理解的数据格式,client解析这些数据.显示在界面上.经常使用的数据格式是x ...
 - android中9-patch图片的使用
			
看了非常多文章的介绍,9.png图片有两种区域:可扩展区和内容显示区. 弄了半天才明确什么叫做可扩展区,什么叫做内容显示区. 分享一下自己的理解. 下图是某博客的截图: 图片来自:http://blo ...
 - c++值传递,指针传递,引用传递以及指针与引用的区别
			
值传递: 形参是实参的拷贝,改变形参的值并不会影响外部实参的值.从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入, 不能传出.当函数内部需要修改参数,并且不希望这个改变影 ...
 - 网页中插入javascript的几种方法
			
网页中插入javascript的方法常见的有两种: 一.直接使用html标记 JavaScript 可以出现在 html的任意地方.使用标记<script>…</script> ...
 - 4.cocos场景和层的调用
			
调用关系: AppDeligate.cpp bool AppDelegate::applicationDidFinishLaunching() { // initialize director aut ...