
我们把上面的流程图做个模板如下代码所示:
abstract class BaseCustomTransform(private val debug: Boolean) : Transform() {
abstract fun provideFunction(): ((InputStream, OutputStream) -> Unit)?
open fun classFilter(className: String) = className.endsWith(".class")
override fun isIncremental() = true
override fun transform(transformInvocation: TransformInvocation) {
super.transform(transformInvocation)
log("Transform start, isIncremental = ${transformInvocation.isIncremental}.")
val inputProvider = transformInvocation.inputs
val referenceProvider = transformInvocation.referencedInputs
val outputProvider = transformInvocation.outputProvider
// 1. Transform logic implemented by subclasses.
val function = provideFunction()
// 2. Delete all transform tmp files when not in incremental build.
if (!transformInvocation.isIncremental) {
log("All File deleted.")
outputProvider.deleteAll()
}
for (input in inputProvider) {
// 3. Transform jar input.
log("Transform jarInputs start.")
for (jarInput in input.jarInputs) {
val inputJar = jarInput.file
val outputJar = outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR)
if (transformInvocation.isIncremental) {
// 3.1 Transform jar input in incremental build.
when (jarInput.status ?: Status.NOTCHANGED) {
Status.NOTCHANGED -> {
// Do nothing.
}
Status.ADDED, Status.CHANGED -> {
// Do transform.
transformJar(inputJar, outputJar, function)
}
Status.REMOVED -> {
// Delete.
delete(outputJar)
}
}
} else {
// 3.2 Transform jar input in full build.
transformJar(inputJar, outputJar, function)
}
}
// 4. Transform dir input.
log("Transform dirInput start.")
for (dirInput in input.directoryInputs) {
val inputDir = dirInput.file
val outputDir = outputProvider.getContentLocation(dirInput.name, dirInput.contentTypes, dirInput.scopes, Format.DIRECTORY)
if (transformInvocation.isIncremental) {
// 4.1 Transform dir input in incremental build.
for ((inputFile, status) in dirInput.changedFiles) {
val outputFile = concatOutputFilePath(outputDir, inputFile)
when (status ?: Status.NOTCHANGED) {
Status.NOTCHANGED -> {
// Do nothing.
}
Status.ADDED, Status.CHANGED -> {
// Do transform.
doTransformFile(inputFile, outputFile, function)
}
Status.REMOVED -> {
// Delete
delete(outputFile)
}
}
}
} else {
// 4.2 Transform dir input in full build.
// Traversal fileTree (depthFirstPreOrder).
for (inputFile in FileUtils.getAllFiles(inputDir)) {
val outputFile = concatOutputFilePath(outputDir, inputFile)
if (classFilter(inputFile.name)) {
doTransformFile(inputFile, outputFile, function)
} else {
// Copy.
Files.createParentDirs(outputFile)
FileUtils.copyFile(inputFile, outputFile)
}
}
}
}
}
log("Transform end.")
}
/**
* Do transform Jar.
*/
private fun transformJar(inputJar: File, outputJar: File, function: ((InputStream, OutputStream) -> Unit)?) {
// Create parent directories to hold outputJar file.
Files.createParentDirs(outputJar)
// Unzip.
FileInputStream(inputJar).use { fis ->
ZipInputStream(fis).use { zis ->
// Zip.
FileOutputStream(outputJar).use { fos ->
ZipOutputStream(fos).use { zos ->
var entry = zis.nextEntry
while (entry != null && isValidZipEntryName(entry)) {
if (!entry.isDirectory) {
zos.putNextEntry(ZipEntry(entry.name))
if (classFilter(entry.name)) {
// Apply transform function.
applyFunction(zis, zos, function)
} else {
// Copy.
zis.copyTo(zos)
}
}
entry = zis.nextEntry
}
}
}
}
}
}
/**
* Do transform file.
*/
private fun doTransformFile(inputFile: File, outputFile: File, function: ((InputStream, OutputStream) -> Unit)?) {
// Create parent directories to hold outputFile file.
Files.createParentDirs(outputFile)
FileInputStream(inputFile).use { fis ->
FileOutputStream(outputFile).use { fos ->
// Apply transform function.
applyFunction(fis, fos, function)
}
}
}
private fun concatOutputFilePath(outputDir: File, inputFile: File) = File(outputDir, inputFile.name)
private fun applyFunction(input: InputStream, output: OutputStream, function: ((InputStream, OutputStream) -> Unit)?) {
try {
if (null != function) {
function.invoke(input, output)
} else {
// Copy
input.copyTo(output)
}
} catch (e: UncheckedIOException) {
throw e.cause!!
}
}
private fun log(logStr: String) {
if (debug) {
println("$name - $logStr")
}
}
}
按照自定义插件步骤,一步一步配置好 plugin、build.gradle,并且在相应的调用 module 中依赖插件
自定义 transform 实现类,继承上述模板,注意:groovy 与 kotlin不互通的,所以Kotlin得转成 java 或者 groovy后才能继续使用
class MyTransform2 extends BaseCustomTransform2{
def project
MyTransform2(Project project) {
this.project=project
}
@Override
Function2<InputStream, OutputStream, Unit> provideFunction() {
return new Function2<InputStream, OutputStream, Unit>() {
@Override
Unit invoke(InputStream inputStream, OutputStream outputStream) {
def classPool = ClassPool.getDefault()
try {
classPool.appendClassPath(((BaseExtension)project.extensions.getByName("android")).bootClasspath[0].toString())
classPool.importPackage("android.os.Bundle")
def clazz = classPool.makeClass(inputStream)
def onCreate = clazz.getDeclaredMethod("onCreate")
println("onCreate found in ${clazz.simpleName}")
def attribute = (AnnotationsAttribute)it.methodInfo.getAttribute(AnnotationsAttribute.invisibleTag)
if (null != attribute?.getAnnotation("com.monk.timer.ui.MyTransformAnno")) {
println("Insert toast in ${clazz.simpleName}")
onCreate.insertAfter(
"""android.widget.Toast.makeText(this,"Hello Transform!",android.widget.Toast.LENGTH_SHORT).show();
"""
)
}
outputStream.write(clazz.toBytecode())
clazz.detach()
} catch (Exception e) {
e.printStackTrace()
}
return null
}
}
}
@Override
String getName() {
return "MyTransform"
}
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
return TransformManager.CONTENT_CLASS
}
@Override
Set<? super QualifiedContent.Scope> getScopes() {
return TransformManager.SCOPE_FULL_PROJECT
}
@Override
boolean isIncremental() {
return true
}
@Override
boolean classFilter( String className) {
return className.endsWith("Activity.class")
}
@Override
boolean applyToVariant(VariantInfo variant) {
return "debug" == variant.buildTypeName
}
}
在 plugin 中注册上述 transform
class CustomPlugins implements Plugin<Project> {
@Override
void apply(Project project) {
println "Hello Plugins --buildSrc: ${project.name}"
// 创建用于设置版本信息的扩展属性
project.extensions.create("releaseInfo", ReleaseInfoExt.class)
project.tasks.create("releaseInfoTask", ReleaseInfoTask.class)
// 注册 transform
def baseExt = project.extensions.getByType(BaseExtension.class)
baseExt.registerTransform(new MyTransform2(project))
// baseExt.registerTransform(new MyTransform2Copy(project))
}
}
在我们自己的 app module 中定义一个 MyTransformAnno 注解,在 onCreate 方法中测试运行
// MyTransformAnno.kt
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.BINARY)
annotation class MyTransformAnno
class TestAct : AppCompatActivity() {
@MyTransformAnno
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
测试运行后会发现,app 提示了 Hello Transform! 这样的吐司,说明 transform 成功执行