本文首发于我的个人博客,此处只为简单转载
背景
最近脑子一热,又想捡回来 MyLuaApp,于是开了这个新坑 AndroCode。
准备在里面实现加载插件,也就是插件化的效果。
在开发的时候,希望能测试从 apk 里加载插件的效果,但是又不想每次都手动复制粘贴插件的 apk 包到项目的 assets 目录下。
于是就想到了用 Gradle,在打插件包的时候把插件包的 apk 自动复制到主项目的 assets 目录下。
实现
整体代码很简单,但是我自己摸索了几个小时之后,才弄出来。。
1. 遍历 applicationVariants
,重命名 APK
kotlin
android {
//...
applicationVariants.all {
logger.lifecycle("Configure application variant $name")
val appProject = project(":app")
// 自定义后面复制apk 的输出路径
val outputFileDir =
"${appProject.projectDir}/src/main/assets/plugins"
// 自定义文件名
val path = project.name + "-" +buildType.name + "-" +
versionName + ".apk"
outputs
// 如果不去拿 internal 包里的类,这个 output 里面就没有 outputFileName 字段
// default type don't have outputFileName field
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
.all { output ->
output.outputFileName = path
false
}
}
}
注意在遍历 outputs 之前需要把里面的输出转成内部类,否则会找不到 outputFileName 字段。
2. 复制 APK 到 assets 目录
kotlin
android {
//...
applicationVariants.all {
// ...
// 获取 app 模块的 preBuild 任务
appProject.getTasksByName("pre${name.capitalized()}Build", true).forEach {
it.apply {
// 要求在打包 app 模块之前,先打包插件
dependsOn(this@all.assembleProvider.get())
}
}
// 配置当前变体的 assemble 任务,在完成后复制 APK 到 assets 目录下
// 因为上面的依赖关系,就可以保证在复制后才开始打包 app 模块,保证 app 模块里打出来的包有我们这个插件 apk
assembleProvider.configure {
doLast {
copy {
this@all.outputs.forEach { file ->
copy {
from(file.outputFile)
into(outputFileDir)
}
}
}
}
}
}
}
上面的代码中我们在 app 模块的 preBuild 任务中配置要求依赖了 当前变体的 assemble 任务,这样在复制 APK 到 assets 目录下的时候,就保证了插件包的 apk 已经打好了。
然后在当前变体的 assemble 任务中,在完成后复制 APK 到 assets 目录下。
其实很简单的几十行代码,但是我查询了很多资料才完成了这个需求。。。下面贴上完整源代码:
kotlin
android {
applicationVariants.all {
logger.lifecycle("Configure application variant $name")
val appProject = project(":app")
val outputFileDir =
"${appProject.projectDir}/src/main/assets/plugins"
val path = project.name + "-" +buildType.name + "-" +
versionName + ".apk"
outputs
// default type don't have outputFileName field
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
.all { output ->
output.outputFileName = path
false
}
appProject.getTasksByName("pre${name.capitalized()}Build", true).forEach {
it.apply {
dependsOn(this@all.assembleProvider.get())
}
}
assembleProvider.configure {
doLast {
copy {
this@all.outputs.forEach { file ->
copy {
from(file.outputFile)
into(outputFileDir)
}
}
}
}
}
}
}
总结
这是一个简单的需求,但是我花了几小时,查询了不少文档才实现的。。。
如果有更好的实现方式,欢迎留言。