一、背景描述:
公司再不断推进组件化,多个Module
开发。随着Module
越来越多,当然也存在许多需要注意的问题。是无法保证多个Module
中的资源命名完全不同,就可能会出现资源引用问题,造成资源冲突。
二、思路分析
用Gradle
配置,在android
中设置 resourcePrefix
属性,前缀字符串根据实际情况自定义。
但是因为Module
太多,每添加一个模块就需要在Module
的build.gradle
里进行配置, 这样太繁琐,不够智能,那么怎么解决呢,思路如下:
1、写一个
Task
,生成一个json
文件,里面以key
->value
的形式存储模块名和其对应的前缀。
2、将
json
文件中前缀value
进行修改,每次task
执行更新不会覆盖之前修改过的value
,默认前缀名是模块名。
3、在所有模块
Gradle
都配置完之后,给每个模块设置一下修改后的前缀属性
效果展示:
编译器会这样提示,但是编译的时候不会报错
三、问题解决与实现
步骤1:写一个更新/生成命名json文件的task
这个task
我们暂时写到 app
的build.gradle
文件中,写在别的.gradle
文件中也可以
task moduleNameToJson() {
doLast {
def jsonFile = file('./ResourceNameRule.json')
def mapResult = new HashMap<String, String>()
// 每次新建的module不会覆盖之前修改过的value
if (jsonFile.exists()){
def parsedJson = new JsonSlurper().parseText(jsonFile.text)
parsedJson.each {
mapResult.put(it.key.toString(), it.value.toString())
}
mapResult = loopGetName(mapResult, rootProject)
}else{
mapResult = loopGetName(null, rootProject)
}
println mapResult.toString()
try{
def writer = new FileWriter("./app/ResourceNameRule.json")
def gson = new Gson().newBuilder().create()
gson.toJson(mapResult, writer)
writer.flush()
writer.close()
}catch (Exception e) {}
}
}
/**
* 给所有模块名处理命名
*
* @param hashmap 有json文件就传入旧map
* @param project 执行时期的功能
* @return HashMap
*/
private static HashMap<String,String> loopGetName(HashMap<String, String> hashmap, Project project){
def map
if(hashmap!=null){
map = hashmap
}else{
map = new HashMap<String, String>()
}
project.allprojects {
def childAndroid = null
try {
childAndroid = it.extensions.getByType(AppExtension.class)
} catch (Exception e) {}
try {
if (childAndroid == null) {
childAndroid = it.extensions.getByType(LibraryExtension.class)
}
} catch (Exception e) {}
if (childAndroid != null) {
// module命名 最好以module_XXX 这样,这里可以自己对模块名进行处理
def prefix = "${it.name}".replace("module_", "")
if (hashmap == null){
if (prefix != null){
map.put(it.name, prefix)
}
}else{
if (map.get(it.name) == null || map.get(it.name) == ""){
map.put(it.name, prefix)
}
}
}
}
return map
}
步骤二: 把json文件进行修改
步骤三: 点击sync时,将前缀属性设置到每个Module中
Hook点:
Gradle构建周期中的Hook点
要给每个
module
都设置属性就要考虑Gradle
执行的生命周期了,不然获取到也是空的。这里我们选择在gradle.afterProject
时期进行hook
,读取配置好的json
文件,给每个Module
设置前缀规则,这个我们也暂时写到app的build.gradle
中,代码如下:
gradle.afterProject {
// 获取资源命名规则的配置文件
def jsonFile = file('./ResourceNameRule.json')
if (jsonFile.exists()){
def parsedJson = new JsonSlurper().parseText(jsonFile.text)
rootProject.allprojects {
def childAndroid = null
try {
childAndroid = it.extensions.getByType(AppExtension.class)
} catch (Exception e) {
}
try {
if (childAndroid == null) {
childAndroid = it.extensions.getByType(LibraryExtension.class)
}
} catch (Exception e) {
}
try {
if (childAndroid != null) {
parsedJson.each { entry ->
// 这里可以对模块进行过滤
if ("libMatisse" == entry.key.toString()){
return
}
if (entry.key.toString() == it.name) {
childAndroid.resourcePrefix(entry.value.toString())
}
}
}
} catch (Exception e) {
childAndroid = null
return
}
}
}
}
四、工具的使用
1、每次添加新Module
之后,先运行task
,生成新的json
文件或者更新 新的模块到json
文件中。
2、把json文件中的前缀修改成你想要的前缀
3、点击大象,sync一下,就全局设置啦
五、结论与展望
如此就可以进行自动化设置资源名的前缀属性,前缀字符串也可以动态配置,非常的方便。随着模块越来越多,这样做的效果只会越来越明显,不按照规则起名字不会导致编译报错,但是资源名重复会导致编译报错。规范命名总是好的。
但项目也有点小缺陷,目前是将代码写到了
app
下的build.gradle
里,导致该文件代码比较多。未来可以优化,单独建一个.gradle
文件进行封装,大佬们提提思路,现在遇到的问题是Gson
不能在其他的.gradle
中使用。代码写的也比较冗余,还不够精简,进步的空间还挺大,加油,奥里给!