- 为什么需要使用这个?问题是什么。
- 如何解决
一、为什么需要使用这个?问题是什么。
假设我们正在开发一个电商应用,需要处理两种环境:
-
开发环境:使用测试服务器,需要详细日志和调试功能
-
生产环境:使用真实服务器,需要高性能和安全措施
java
// src/main/java/com/example/shop/ApiClient.java
public class ApiClient {
private static final boolean DEBUG = BuildConfig.DEBUG;
public String getBaseUrl() {
return DEBUG ? "https://dev.api.example.com" : "https://api.example.com";
}
public void log(String message) {
if (DEBUG) {
Log.d("API", message); // 仅调试时记录详细日志
}
}
public void enableStrictSecurity() {
if (!DEBUG) {
// 生产环境才启用严格安全措施
enableCertificatePinning();
enableObfuscation();
}
}
}
这种方法的问题:
-
代码混杂:同一个类中混杂了调试和发布逻辑
-
可读性差:条件语句遍布各处
-
安全隐患:可能忘记添加条件检查
-
维护困难:调试代码可能意外进入生产环境
-
性能影响:即使发布版本也会包含调试代码(只是不执行)
二、解决方法:使用 src/debug
和 src/release
的解决方案
在 Android 项目中,在 src
目录下创建 debug
和 release
目录是一种利用 Gradle 构建变体(Build Variants)来管理不同构建类型(Build Types)的源代码和资源的常见做法。这是 Android Gradle 插件提供的一个强大功能。
允许你为开发调试阶段和最终发布阶段编写不同的代码或配置不同的资源。
-
src/main/
:这是你的主源代码集。它包含所有构建类型和产品风味共享的代码和资源。 -
src/debug/
:这个目录下的代码和资源仅 在构建 Debug 版本时生效。它会覆盖或补充src/main/
中的内容。 -
src/release/
:这个目录下的代码和资源仅 在构建 Release 版本时生效。它会覆盖或补充src/main/
中的内容。
将特定于构建类型的代码清晰地隔离在各自的目录中,提高代码的可维护性。你不需要在主代码中使用 if (BuildConfig.DEBUG)
来包裹所有调试相关的代码(虽然这个常量仍然很有用)。
(1)创建一个接口
java
// src/main/java/com/example/shop/ApiClient.java
public interface ApiClient {
String getBaseUrl();
void log(String message);
void enableStrictSecurity();
}
(2)创建debug文件夹(注意包名路径要和 main
下一致)

java
// src/debug/java/com/example/shop/ApiClientImpl.java
public class ApiClientImpl implements ApiClient {
@Override
public String getBaseUrl() {
return "https://dev.api.example.com";
}
@Override
public void log(String message) {
Log.v("API_DEBUG", message); // 详细日志
}
@Override
public void enableStrictSecurity() {
// 调试环境不启用严格安全措施
}
}
(3)创建release文件夹中(注意包名路径要和 main
下一致)
java
// src/release/java/com/example/shop/ApiClientImpl.java
public class ApiClientImpl implements ApiClient {
@Override
public String getBaseUrl() {
return "https://api.example.com";
}
@Override
public void log(String message) {
// 生产环境不记录详细日志
if (message.contains("ERROR")) {
Log.e("API", message);
}
}
@Override
public void enableStrictSecurity() {
enableCertificatePinning();
enableObfuscation();
}
}
(4)创建工厂类(在 main 中)
java
// src/main/java/com/example/shop/ApiClientFactory.java
public class ApiClientFactory {
public static ApiClient create() {
return new ApiClientImpl();
}
}
(5)使用
java
// 在任何地方使用
ApiClient client = ApiClientFactory.create();
String url = client.getBaseUrl();
client.log("Requesting: " + url);
client.enableStrictSecurity();
因为包名相同,类名相同,所以这里会根据你是debug模式,还是release模式来使用不同类。
其实不仅仅是类,Java/Kotlin 源代码、资源文件(res)、Assets 或 Manifest 文件都可以。
在 android { }
块中配置 buildTypes
时,你就定义了构建类型(如 debug
和 release
)。Gradle 会根据这些构建类型的名称(debug
, release
)自动去寻找对应的源码集目录(src/debug/
, src/release/
)。