三思笔记_在使用buildconfig调试之前要三思

三思笔记

Last week I was working on a feature that syncs data between phone and backend. That sync mechanism is triggered under certain conditions and we needed a way to bypass those conditions and force the sync when enabling some preference in debug mode. Leaving aside the details, the resulting code was something like the following:

上周,我正在开发一项功能,该功能可以在手机和后端之间同步数据。 该同步机制在某些条件下触发,我们需要一种方法来绕过这些条件,并在调试模式下启用某些首选项时强制进行同步。 抛开细节,最终的代码如下:

fun syncData() {
    if (shouldForceSync() || shouldSync()) {
        // Trigger the process
    }
}


private fun shouldForceSync() = BuildConfig.DEBUG && isForceSyncPreferenceEnabled()


private fun shouldSync(): Boolean {
    // Real validation rules here
}

Pretty simple. I relied on the BuildConfig.DEBUGconstant generated by Android Studio.

很简单 我依靠Android Studio生成BuildConfig.DEBUG常量。

问题 (The problem)

Everything seemed fine to me until I received a comment in the pull request:

在我收到请求请求的评论之前,一切对我来说似乎还不错:

I would not want to ship Debug functionality in our Production APK/Bundle code.

我不想在我们的Production APK / Bundle代码中提供调试功能。

I agree that we shouldn’t ship debug functionality in the published artifact, and there are many reasons for that:

我同意我们不应该在已发布的工件中提供调试功能,这有很多原因:

  • ⛔ It’s definitely not a good practice.

    definitely绝对不是一个好习惯。
  • 🔒 It can introduce security issues: what if someone decompiles the generated artifact and found that you are doing some tricks to avoid a validation? What if someone manages to replicate that behavior? Edit: If you are using Proguard/R8 this isn’t a real problem for you, because the compiler optimizations will hide the real condition. Anyway: remember that this tools aren’t enabled by default and you need to take are about it.

    🔒它可能会引入安全性问题:如果有人对生成的工件进行反编译并发现您正在采取一些技巧来避免验证该怎么办? 如果有人设法复制该行为怎么办? 编辑:如果您使用的是Proguard / R8,这对您来说不是一个真正的问题,因为编译器优化会隐藏实际情况。 无论如何:请记住,默认情况下未启用此工具,因此您需要了解一下。

  • 🐞 And what if there is a bug in the generation of the BuildConfig class? Maybe you didn’t even consider it, but let me tell you that some years ago there was an unresolved bug that caused BuildConfig.DEBUG was always true, even in the generated .apk file.

    🐞并且,如果BuildConfig类的生成中存在错误,该怎么办? 也许您甚至都没有考虑过,但让我告诉您,几年前,有一个未解决的错误导致 BuildConfig.DEBUG 始终为true,即使在生成的.apk文件中也是如此

还有更好的选择…… (There are better alternatives…)

… but the correct one depends on what you are trying to do.

…但是正确的选择取决于您要执行的操作。

I published a tweet with a poll and thanks to the responses I learned a lot about the way developers are using this constant, and when and why we are doing things in a, probably, wrong way.

我发布了一条带有民意测验的推文,并感谢我的反馈,使我学到了很多有关开发人员使用此常量的方式以及我们何时以及为什么以错误的方式进行操作的信息。

包括调试功能 (Including debug functionality)

This is my own scenario, but I received some responses from developers facing a similar one, for example adding Interceptors to the HTTP requests in the app in order to log requests and responses. We should avoid this and my recommendation is to use the power of flavors in order to separate debugging logic from the production logic, including the same classes in the release and the debug flavor. In my case, the result is included in the following snippets:

这是我自己的情况,但是我收到了面对类似情况的开发人员的一些响应,例如,将Interceptors添加到应用程序中的HTTP请求中,以便记录请求和响应。 我们应该避免这种情况,我的建议是使用flavors ,以便将调试逻辑与生产逻辑分开,包括releasedebug风味中的相同类。 就我而言,结果包含在以下片段中:

The original syncData function should be as simple as:

原始的syncData函数应该很简单:

fun syncData() {
    if (SyncCoordinator.shouldSync()) {
        // Trigger the process
    }
}

Magic. No conditionals. Now we are delegating the condition to a function included in an object called SyncCoordinator, let’s see how to implement it:

魔术 。 没有条件。 现在,我们将条件委托给名为SyncCoordinator的对象中包含的函数,让我们看看如何实现它:

// File path: MyProject/src/release/java/com/facundomr/example/util/SyncCoordinator.kt
object SyncCoordinator {
    fun shouldSync(): Boolean {
        // Real validation rules here
    }
}




// File path: MyProject/src/debug/java/com/facundomr/example/util/SyncCoordinator.kt
object SyncCoordinator {
    fun shouldSync(): Boolean {
        // Debug rules
    }
}

Note that these objects have the same name and they are, obviously, in two different files. The file path is the key: the first one is under the src/release directory and the second one is in src/debug. Build system will include just one of these implementations based on the active build variant. Be careful: it’s not possible to have a main class and try to override it in flavors. You can find more information about it in this StackOverflow question.

请注意,这些对象具有相同的名称,并且显然它们位于两个不同的文件中。 文件路径是关键:第一个位于src/release目录下,第二个位于src/debug 。 构建系统将仅包括基于活动构建变体的这些实现之一。 请注意:不可能有一个主类并尝试在口味上覆盖它。 您可以在此StackOverflow问题中找到有关它的更多信息。

You will note also that this approach allow us to write separate unit test classes for each flavor, and maintain debug tests isolated from the release tests, which is so much cleaner. These classes should be included in a path like the following: MyProject/src/testDebug/java/com/facundomr/example/util/SyncCoordinatorTest.kt and MyProject/src/testRelease/java/com/facundomr/example/util/SyncCoordinatorTest.kt .

您还将注意到,这种方法使我们可以为每种口味编写单独的单元测试类,并保持与release测试隔离的debug测试,这非常干净。 这些类应包含在以下路径中: MyProject/src/ testDebug /java/com/facundomr/example/util/SyncCoordinatorTest.ktMyProject/src/ testRelease /java/com/facundomr/example/util/SyncCoordinatorTest.kt

Now, let’s move forward and evaluate other situations:

现在,让我们继续前进并评估其他情况:

根据口味更改值/常数 (Changing a value/constant depending on the flavor)

If you just want to get a different value in a constant, then you don’t need to write any extra code. Defining the same buildConfigField in every flavor should be enough. Follow the official documentation for more information about this capability.

如果只想在常量中获得其他值,则无需编写任何额外的代码。 在每种buildConfigField中定义相同的buildConfigField应该足够了。 请遵循官方文档以获取有关此功能的更多信息。

包括仅在调试模式下才需要的库 (Including libraries needed only when in debug mode)

If you are doing this, then you are not just including debug functionality: you are probably increasing significantly the size and method count of the released artifact. In order to avoid this you should read this article about No-op versions for dev tools.

如果执行此操作,则不仅包括调试功能,还可能会大大增加已发布工件的大小和方法数量 。 为了避免这种情况,您应该阅读有关开发工具的No-op版本的本文。

避免在生产中执行代码 (Avoiding code execution in production)

The difference can be subtle: it’s not the same to include debug features as to avoid some code execution in production: because maybe you should’t be scared of security issues if someone discovers that you are, for example, disabling logs based on a condition as simple as relying on BuildConfig.DEBUG. But there are better and more elegant way to do this: use Proguard to remove that lines when exporting your artifact. I recommend this article from Craig Russell about Stripping log statements using Proguard.

差异可能是微妙的:包含调试功能与避免在生产中执行某些代码并不相同:因为如果有人发现您例如基于条件禁用日志,也许您不应该担心安全问题就像依赖BuildConfig.DEBUG一样简单。 但是,有一种更好,更优雅的方法来执行此操作:在导出工件时,使用Proguard删除这些行。 我推荐Craig Russell撰写的有关使用Proguard剥离日志语句的文章

警告:在应用中编写模块时要非常小心 (Warning: very careful when you write modules in your app)

If you are writing an app and the code is structured in modules (for example: app and ui, and you are depending on the constant in the uimodule to do some debug logic, then be careful: BuildConfig.DEBUG in app is not the same as BuildConfig.DEBUG in the ui module: you can be executing debug code even in the released artifact. There is a safer way to check globally if a build is in debug mode or not: it’s called ApplicationInfo.FLAG_DEBUGGABLE. You can read this article to learn about it.

如果您正在编写应用程序,并且代码是按模块进行结构化的(例如: appui ,并且您要依靠ui模块中的常量来执行一些调试逻辑),请注意: app中的BuildConfig.DEBUG不是与ui模块中的BuildConfig.DEBUG相同: 即使在已发布的工件中,您也可以执行调试代码 。有一种更安全的方法可以全局检查构建是否处于调试模式:称为ApplicationInfo.FLAG_DEBUGGABLE您可以阅读此内容文章以了解它

结论:我们应该始终避免使用BuildConfig.DEBUG吗? (Conclusion: Should we always avoid using BuildConfig.DEBUG?)

Definitely not. I’m not trying to say that. There can be situations in which you shouldn’t be concerned about it and it’s perfectly fine to rely on the constant, for example disabling Crashlytics on debug. When you use an if statement for this:

当然不。 我不是想这样说。 在某些情况下,您不必担心它,并且完全依赖常量就可以了,例如在调试时禁用Crashlytics。 当您对此使用if语句时:

  • You still need to include the library in production.

    您仍然需要在生产中包含该库。
  • You are not shipping a debug feature.

    您没有提供调试功能。
  • It’s not dangerous if someone discovers that you are disabling crash reports when debugging the app.

    如果有人在调试应用程序时发现您正在禁用崩溃报告,这并不危险。

My only conclusion is: we should consider all these options before writing code that depends on the BuildConfig.DEBUG constant. Maybe there is a better, safer and more elegant way to achieve the same goal, and it’s always nice to learn new things.

我唯一的结论是: 在编写依赖 BuildConfig.DEBUG 常量的 代码之前,我们应该考虑所有这些选项 也许有一种更好,更安全,更优雅的方法来实现相同的目标,学习新事物总是很高兴的。

Happy coding! 😊

编码愉快! 😊

翻译自: https://proandroiddev.com/think-before-using-buildconfig-debug-f2e279da7bad

三思笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值