为什么BuildConfig类使用Boolean.parseBoolean()而不是字面值?

16

在查看由Android Studio和Gradle插件生成的BuildConfig类时,可以看到BuildConfig.DEBUG字段是使用Boolean.parseBoolean(String)调用进行初始化的,而不是使用布尔字面值truefalse之一。

当我使用Gradle添加自定义构建属性时,我会像这样做:

android {
    buildTypes.debug.buildConfigField 'boolean', 'SOME_SETTING', 'true'
}

但是查看生成的 BuildConfig 告诉我 Google 采取了一种不同的方法来处理 DEBUG 标志:

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");

  // more fields here

  // Fields from build type: debug
  public static final boolean SOME_SETTING = true;
}

使用Boolean.parseBoolean(String)相对于直接使用布尔字面值的好处是什么?


7
你问问题和回答问题只用了一秒钟?成就已解锁。领取你的超人徽章。 - Rajesh
http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ - david.schreiber
在提问时有一个复选框可以勾选。我只是想与社区分享我的学习经验;-) - david.schreiber
谢谢提供这个信息,我之前没有看过那篇博客文章。但我想你的问题本身并不是问题,更适合写成一篇博客文章。 - Rajesh
3个回答

29

BuildConfig类中的布尔字面常量在使用时(至少在Android Studio中)会产生IDE警告。例如,当在布尔表达式中使用它时,Android Studio会错误地建议简化布尔表达式,因为常量值始终相同(对于当前的构建变体而言)。

因缺少构建配置知识,Android Studio生成代码警告

这个警告只是因为Android Studio不知道BuildConfig.SOME_SETTING内的最终值可能对其他构建变体不同而已。

为了保持代码干净并免受警告,您可以通过添加如下IDE注释来告诉Android Studio忽略此特定警告:

添加代码注释以忽略IDE警告

但是这会使代码变得杂乱,并降低可读性。通过使用 Boolean.parseBoolean(String) 方法来初始化您的常量字段,您实际上欺骗了Android Studio,使其无法完全分析您的布尔表达式,从而不再生成警告。

使用parseBoolean(String)防止IDE警告

这种方法非常有用,因为它使得代码保持干净和可读性,并且不会关闭重要的代码分析和警告生成。

安全性和性能注意事项

如Jiří Křivánek所述,使用解析的布尔值不仅可以“欺骗”IDE执行的静态分析,还可以欺骗编译器、代码缩小器和混淆器,从而使它们难以从应用程序中删除死代码。这可能会在应用程序二进制文件中留下代码部分,否则将被剥离。


2
也许我错过了什么,但是在build.gradle中添加什么来声明使用parseBoolean的BuildConfig值呢? - Tunga
7
@Tunga 尝试在 defaultConfig 或任何风格或构建类型配置中添加 buildConfigField 'boolean', 'SOME_FLAG', 'Boolean.parseBoolean("false")' - david.schreiber
啊,我可能漏了外面的引号,谢谢。最终我使用清单替换解决了我的问题。 - Tunga
1
问题在于我周围的程序员倾向于使用BuildConfig.DEBUG进行条件编译 - 只是为了从代码中完全删除各种调试工具(和日志等)。但由于谷歌所做的这种恶心的欺骗,以及我周围大多数人对此毫无头绪,它实际上并不像条件编译那样起作用。当我们进行渗透测试时,渗透测试团队向我们报告,例如“您的代码包含有助于反向攻击的敏感信息”。而程序员则说:不可能,我们仔细审查过它是否进行了条件编译 :) - Jiří Křivánek
@J.Doe 你说得对,但问题也没有明确要求这样做。我不确定安卓团队最初采用这种模式的原因,但我猜想可能是为了内联的原因(即安卓是一个模块化系统,这些布尔值可能根据实际运行时加载的变体而不同,而Proguard或其他代码压缩工具内联时可能会破坏这个系统)。 - undefined
显示剩余5条评论

3
在我看来,“诀窍”实际上非常危险,因为您不能根据BuildConfig.DEBUG进行条件编译!
if(!BuildConfig.DEBUG) { Log.d("Here we are verifying the signature!"); }

根据我的反向工程,日志记录将保留在输出的.class文件中!这对攻击者来说是非常好的线索...

这是提到的解决方法的一个非常有效的缺点,即死分支抖动与大多数缩小器/混淆器不兼容。另一方面,我不认为大多数开发人员会一遍又一遍地编写if(constant) { Log.d("敏感内容"); }。记录敏感数据通常是一个坏主意。此外,虽然我同意攻击者不应该轻易地拆解应用程序,但即使他们设法读取您的代码,应用程序的安全性也不应被破坏。 - david.schreiber
我在答案中添加了一个关于安全性和性能考虑的部分。非常感谢您的输入! - david.schreiber
你说得对,我的工作有点特殊,我同意大多数世界并不像我们这样对代码安全性要求那么高。 - Jiří Křivánek

0
进一步说,如果您想将布尔值作为变量传递给BuildConfig,您该如何使用gradlew

  1. 将您的变量添加到gradle.properties中,并设置默认值

     MY_VAR=false
    
  2. 在您的build.gradle文件中

    android {
        defaultConfig {
            buildConfigField "boolean", "MY_VAR", "Boolean.parseBoolean(\"" + MY_VAR + "\")" 
        }
    }
    
  3. 将参数添加到您的gradlew命令中

     gradlew <task> -P MY_VAR=true
    
所有这些将在您的BuildConfig中生成以下内容。
public static final boolean MY_VAR = Boolean.parseBoolean("true");

如果你在gradlew没有传入参数,显然它将默认为false


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接