写在前头
- 本文编辑于2016年11月27日,请注意技术的时效性
- 请带着批判的态度阅读
- 可以参考项目
- 这里的动态切换依赖库是指可以动态切换Module中的Build Variant
其中软件版本如下:
- Android Studio 2.2.2
- gradle 2.14.1
序
最近在写模块化,将图片库,网络库,公共库,工具库都拆分出去,依赖库一多,如果不实现动态切换依赖库就特别麻烦,需要改动到gradle代码,故有本次的验证。
多个Module
正文
1. 添加一个通用方法工具库
/**
*
* 工具类 gradle
*/
import java.util.regex.Matcher
import java.util.regex.Pattern
/**
* 获取当前的Flavor类型 查找不到返回defaultName
*
* @param libName
* @param defaultName
* @return
*/
ext.getCurrentFlavor = { String libName, String defaultName ->
println "getCurrentFlavor args:" + libName + "," + defaultName
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if(tskReqStr.contains("assemble")) {
pattern = + libName + ":assemble(\\w+)(Sources)")
}
else {
pattern = + libName + ":generate(\\w+)(Sources)")
}
Matcher matcher = pattern.matcher(tskReqStr)
if(matcher.find()) {
String result = toLowerCaseFirstOne(matcher.group(1))
println "getCurrentFlavor:" + result
saveToLocal(libName, result)
return result.toString()
} else {
String result = getFromLocal(libName, defaultName)
println "getCurrentFlavor: no match one, return local or default:" + result
return result
}
}
/**
* 首字母转小写
* @param value
* @return
*/
def toLowerCaseFirstOne(String value){
if (value == null || value.isEmpty()) {
return ""
}
if(Character.isLowerCase(value.charAt(0))) {
return value;
} else {
return (new StringBuilder()).append(Character.toLowerCase(value.charAt(0))).append(value.substring(1)).toString();
}
}
/**
* 保存到本地
*
* @param key
* @param value
* @return
*/
def saveToLocal(String key, String value) {
File localFile = project.rootProject.file('local.properties')
// 保存进local.properties,防止编译的时候找不到
Properties properties = new Properties()
properties.load(localFile.newDataInputStream())
properties.setProperty(key, value);
properties.save(localFile.newDataOutputStream(), "update-"+ key + "[" + value + "]")
}
/**
*
* 从本地读取数据
*
* @param key
* @param defaultName
* @return
*/
def getFromLocal(String key, String defaultName) {
File localFile = project.rootProject.file('local.properties')
// 保存进local.properties,防止编译的时候找不到
Properties properties = new Properties()
properties.load(localFile.newDataInputStream())
return properties.get(key, defaultName)
}
2. 为有必要的依赖库添加默认的发布Flavor
操作步骤如下:
- 添加工具库
apply from: '../your path/utils.gradle'
- 设置默认的发布config,通过动态获取Tasks任务
defaultPublishConfig(getCurrentFlavor(project.name, "your default flavor"))
以我的图片库为例子:
// 公共库
apply plugin: 'com.android.library'
// 关键步骤1: 添加工具库
apply from: '../tasks/utils.gradle'
android {
// 关键步骤2: 修改默认的发布config
defaultPublishConfig(getCurrentFlavor(project.name, "glideRelease"))
......
productFlavors {
glide {
resValue("string","lib_img","Glide")
dependencies {
glideCompile fileTree(dir: 'src/glide/libs', include: ['*.jar'])
glideCompile 'com.github.bumptech.glide:glide:3.7.0'
glideCompile 'com.android.support:support-v4:25.0.0'
}
}
uil {
resValue("string","lib_img","Uil")
dependencies {
uilCompile fileTree(dir: 'src/uil/libs', include: ['*.jar'])
}
}
}
}
......
}
3. 为调用依赖库的module添加依赖
这个就很简单了,跟调用一个通用的依赖一样,它自然会根据你设置的Build Variant来关联依赖库
// may like this
compile project(path: ":libimg")
Paste_Image.png
4 . 愉快的去切换动态依赖库吧
现在,你可以试试看,包括编译都会从local文件中动态选择。
local文件大致的原理
- 动态设置defaultPublishConfig,可以决定默认发布的Flavor(真不知道怎么翻译...)
- 其实我们通过AS切换Build Variant的时候,是会执行了多次gradle任务的,当它执行到defaultPublishConfig时,会去执行我们设置的getCurrentFlavor方法,而getCurrentFlavor方法会动态返回一个Flavor名称,我们的目的就是保持这个动态返回跟用户选择的一致。
- getCurrentFlavor的操作逻辑是先根据Tasks需要执行的任务去匹配查找,如果找不到就会从本地的local文件中查找,如果再找不到就会读取默认值
-
为什么会先根据Tasks需要执行的任务去匹配查找,是因为我们切换Build Variant时,我们可以获取到当前用户切换的Flavor,如下图,我们就是从这里正则匹配的:
当我们切换Build Variant时 -
为什么需要从local文件中读取呢,是因为刚刚说了切换Build Variant,是会执行多次gradle任务的,第一次执行时,我们是无法获取到Tasks任务的,所以一定需要有默认值。另外,当我们执行编译生成apk的时候,也无法获取到Tasks任务,如下图,都是从默认中获取:
手动编译 - 为什么需要默认值呢,因为第一次执行的时候,我们既拿不到Tasks任务,又没有local文件可以读取。
PS: 服务器Jenkins等打包的时候,可以通过动态配置local文件,实现自定义打包。