编译阶段修改Manifest文件属性
概述
起因:在开发业务过程中,遇到了需要根据不同的平台,修改Manifest属性的问题。为了只维护一套代码,打算在编译阶段修改这个属性。
在网上找资料,感觉比较少,自己后面成功之后记下来,方便回溯和分享。
编写插件作为入口
创建插件
/**
* 插件入口
*/
class ManifestProject implements Plugin<Project> {
String variantName
@Override
void apply(Project project) {
}
}
导入项目
在项目app的build.gradle导入插件
apply plugin: ManifestProject
获取变体名称
这一步是需要找到,弄清楚自己项目的编译任务的变体,方便后面通过这些变体来查找具体的任务。
查看当前项目的所有Task
./gradlew tasks --all
在这个输出的结果中查找:(正则表达式---process.*?Manifest)
这个对应编译任务的这个:
那么,通过上面的操作,可以了解到:
- 要查找的任务简单描述为"process变体名称Manifest"(根据自己项目输出的内容灵活调整)
- 以上面为例,"Base"就是我的变体名称。
获取变体名称
通过代码获取当前的变体名称
/**
* 插件入口
*/
class ManifestProject implements Plugin<Project> {
protected static final String TAG = "ManifestPlugin"
String variantName
@Override
void apply(Project project) {
//创建ManifestExtension
createManifestExtension(project)
//获取变体
getVariantNameInBuild(project)
System.out.println(String.format("Welcome %s ManifestProject", variantName)
//如果不是一个有效的variant,则直接返回
if (!isValidVariantName()) {
return
}
}
/**
* 配置扩展属性
* @param project
*/
void createManifestExtension(Project project) {
project.getExtensions().create(TAG, ManifestExtension)
}
/**
* 获取变体名称
* @param project
*/
void getVariantNameInBuild(Project project) {
// 根据自己项目调整, 可能你的项目有定制化
// 以上面为例, 需要去掉多余的前缀后缀之类的, 转换为"Base"
// 可以打印出来看下, 如果没有定制, 就不用处理了
variantName = project.gradle.startParameter.taskNames[0]
}
boolean isValidVariantName() {
variantName != null && variantName.length() > 0 && !variantName.contains("clean")
}
}
通过上面的代码,就可以在variantName中拿到当前的变体名称了。
创建修改Manifest的Task
下面的例子,通过修改Manifest,修改属性
android:directBootAware="true"
的值,例子中将true改为false。 实现如下:
/**
* 具体修改manifest的Task
*
*/
class ChangeDirectBootForPackageManifestTask extends DefaultTask {
protected static String TAG = "ChangeDirectForPackageManifestFromManifestProject"
private FileCollection manifestCollection
private File mainManifestFile
/**
* 设置所有的 需要合并的Manifest文件
* @param collection
*/
void setManifestsFileCollection(FileCollection collection) {
manifestCollection = collection
}
/**
*
* @param file
*/
void setMainManifestFile(File file) {
mainManifestFile = file
}
@TaskAction
void doTaskAction() {
//处理所有包下的AndroidManifest文件
manifestCollection.each {
handlerVariantManifestFile(it)
}
// 自己app下的
handlerVariantManifestFile(mainManifestFile)
}
/**
* 处理单个变体的Manifest文件
*/
void handlerVariantManifestFile(File manifestFile) {
if (!manifestFile.exists()) {
return
}
def node = readManifestFromPackageManifest(manifestFile)
writeManifestForPackageManifest(manifestFile, node)
}
/**
* 读manifest内容
* @param manifestFile
* @return
*/
Node readManifestFromPackageManifest(File manifestFile) {
try {
// xml 解析器
XmlParser xmlParser = new XmlParser()
def node = xmlParser.parse(manifestFile)
boolean needChange = false
Object key1 = null
// 找见application下的属性
Node application = node.application[0]
if (application != null) {
// 找到directBootAware属性, 如果不知道自己的属性名称, 可以加日志打印, 再针对修改
application.attributes().find {
if ("{http://schemas.android.com/apk/res/android}directBootAware".equals(it.key.toString())) {
key1 = it.key
needChange = true
}
}
application.attributes().each { key, value ->
System.out.println("my key : " + key + " value: " + value)
}
System.out.println("needChange: " + needChange)
if (needChange) { // 修改属性为false
application.attributes().put(key1, false)
}
}
return node
} catch (ParserConfigurationException e) {
e.printStackTrace()
} catch (SAXException e) {
e.printStackTrace()
} catch (IOException e) {
e.printStackTrace()
}
}
/**
* 第四步:保存到原AndroidManifest文件中
* @param manifestFile
* @param node
*/
void writeManifestForPackageManifest(File manifestFile, Node node) {
// 修改写回到maniFest
String result = XmlUtil.serialize(node)
manifestFile.write(result, "utf-8")
}
}
将修改Manifest的任务添加到编译任务中
...
@Override
void apply(Project project) {
....
// 添加修改Task
addTaskForVariantAfterEvaluate(project)
}
/**
* 在项目配置完成之后添加task
* @param project
*/
void addTaskForVariantAfterEvaluate(Project project) {
//初始化 AddExportForPackageManifestTask
ChangeDirectBootForPackageManifestTask addExportTask = project.getTasks().create(ChangeDirectBootForPackageManifestTask.TAG,
ChangeDirectBootForPackageManifestTask)
//在项目配置完成后,添加自定义Task
project.afterEvaluate {
//为当前变体的task都加入到这个任务队列中。
//所以通过project.getTasks().each {}去匹配每个task的startsWith&&endsWith的逻辑是一致的
//并且这种性能会更高
//直接通过task的名字找到ProcessApplicationManifest这个task
addExportTaskForPackageManifest(project, addExportTask)
}
}
void addExportTaskForPackageManifest(Project project, ChangeDirectBootForPackageManifestTask beforeAddTask) {
// 利用之前获取到的变体名称, 这里查找Task
ProcessApplicationManifest processManifestTask = project.getTasks().getByName(String.format("process%sManifest", variantName))
beforeAddTask.setManifestsFileCollection(processManifestTask.getManifests())
beforeAddTask.setMainManifestFile(processManifestTask.getMainManifest().get())
processManifestTask.dependsOn(beforeAddTask)
}
....