原因
由于经常发版本,每个版本下的模块代码,都有不同。
有些时候,都不确定当前的代码版本是啥,需要通过APK版本号找到模块版本号,然后反查git的代码位置。
如果将使用的Git的最后代码提交时的commit直接写入apk中,将能大大提升代码的定位速度。
实现过程
目标
利用Gradle构建脚本,在构建时自动查询当前代码的commit信息,自动将commit信息写入java代码。
获取代码当前位置的命令
git rev-parse --short HEAD
执行效果:
命令
将commit信息写入java代码
Gradle基础
这里要先了解Gradle自动生成Java代码的知识点。每个Android项目都会自动生成一个版本信息的class,位于:
BuildConfig位置
BuildConfig类的基本字段:
BuildConfig Class
可以看到自动生成的BuildConfig包含了VersionCode与VersionName,那这两个final成员,受谁的控制呢?
gradle
在gradle文件中的android节点下的defaultConfig节点下的versionCode与versionName,分别控制BuildConfig内的成员变量,在程序运行时,可以读取这两个变量。
Gradle扩展
了解了BuildConfig的由来,便可以开始合理拓展与注入任意构建信息到java代码里了。
基本语法是:
buildConfigField("String", "key", '"value"')
现在添加一个GIT_COMMIT的成员变量:
gradle
添加一个key-value后的效果为:
BuildConfig
关键脚本
buildConfigField("String", "GIT_COMMIT", '"' + 'git rev-parse --short HEAD'.execute().text.trim() + '"')
项目被拆分为多AAR后,使用BuildConfig要注意的事项
以上讲解了如何向java代码中添加一条自定义构建信息,但是生成的java代码为static final类型的,这意味着编译器会进行编译优化。
比如:
// 外部AAR中的代码
public static final String VERSION_NAME = "1.0";
// 你自己模块内的原始代码
String str = "version:" + VERSION_NAME;
// 编译器编译后的字节码的中的代码:
String str = "version:1.0";
如果外部AAR升级,而你的AAR依赖没有及时更新,则编译不会出问题,但是运行时,就会发现没有获取到最新的外部AAR中的BuildConfig代码。
每次其他AAR升级,都要更新到最新依赖,这很明显是不可能实现的。
唯一的办法是避免编译器的优化,不能直接引用static final类型的变量。
基础的解决方案是使用反射,如:
String gitCommit = "";
String versionName = "";
try {
Class jniBuildConfigClazz = com.youku.flash.downloader.jni.BuildConfig.class;
Field[] fields = jniBuildConfigClazz.getDeclaredFields();
for (Field field : fields) {
if ("GIT_COMMIT".equals(field.getName())) {
gitCommit = (String) field.get(jniBuildConfigClazz);
}
if ("VERSION_NAME".equals(field.getName())) {
versionName = (String) field.get(jniBuildConfigClazz);
}
}
} catch (Throwable ignore) {
}
如果有其他更好的避免编译器优化的方案,欢迎告知。
效果
result
总结
合理使用gradle构建脚本。