transform 多线程+增量编译 提升1.5倍
This commit is contained in:
@@ -73,5 +73,5 @@ todo list
|
||||
1. 自定义scope逻辑,统一配置(done)
|
||||
2. 模块内互相引用 (done)
|
||||
3. 仓库管理
|
||||
5. transform 优化
|
||||
5. transform 优化 gradle增量前7秒多 多线程+增量编译 快了1.5倍 3s
|
||||
6. kotlin 模块测试 (done)
|
||||
@@ -1,4 +1,3 @@
|
||||
apply plugin: 'com.android.application'
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.1"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,7 +9,8 @@ import com.plugin.component.extension.option.PublicationOption
|
||||
import com.plugin.component.extension.option.DebugOption
|
||||
import com.plugin.component.listener.OnModuleExtensionListener
|
||||
|
||||
import com.plugin.component.transform.ComponentTransform
|
||||
import com.plugin.component.transform.InjectCodeTransform
|
||||
import com.plugin.component.transform.ScanCodeTransform
|
||||
import com.plugin.component.utils.JarUtil
|
||||
import com.plugin.component.extension.PublicationManager
|
||||
import com.plugin.component.extension.ComponentExtension
|
||||
@@ -237,7 +238,8 @@ class ComponentPlugin implements Plugin<Project> {
|
||||
}
|
||||
}
|
||||
}
|
||||
childProject.extensions.findByType(BaseExtension.class).registerTransform(new ComponentTransform())
|
||||
childProject.extensions.findByType(BaseExtension.class).registerTransform(new ScanCodeTransform(childProject))
|
||||
childProject.extensions.findByType(BaseExtension.class).registerTransform(new InjectCodeTransform(childProject))
|
||||
} else {
|
||||
childProject.apply plugin: Constants.PLUGIN_LIBRARY
|
||||
Logger.buildOutput("project.apply plugin: com.android.library")
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
package com.plugin.component.transform
|
||||
|
||||
|
||||
import com.android.build.api.transform.DirectoryInput
|
||||
import com.android.build.api.transform.Format
|
||||
import com.android.build.api.transform.JarInput
|
||||
import com.android.build.api.transform.QualifiedContent
|
||||
import com.android.build.api.transform.Transform
|
||||
import com.android.build.api.transform.TransformException
|
||||
import com.android.build.api.transform.TransformInput
|
||||
import com.android.build.api.transform.TransformInvocation
|
||||
import com.android.build.gradle.internal.pipeline.TransformManager
|
||||
import com.android.utils.FileUtils
|
||||
import com.plugin.component.Logger
|
||||
import com.plugin.component.asm.ComponentInjectClassVisitor
|
||||
import com.plugin.component.asm.ComponentScanClassVisitor
|
||||
import com.plugin.component.utils.FileUtil
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
|
||||
import java.util.jar.JarEntry
|
||||
import java.util.jar.JarFile
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import com.plugin.component.asm.ScanRuntime
|
||||
|
||||
/**
|
||||
* 插入方法统计transform
|
||||
* created by yummylau 2019/08/12
|
||||
*/
|
||||
class ComponentTransform extends Transform {
|
||||
|
||||
|
||||
@Override
|
||||
String getName() {
|
||||
return "ComponentTransform"
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<QualifiedContent.ContentType> getInputTypes() {
|
||||
return TransformManager.CONTENT_CLASS
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<? super QualifiedContent.Scope> getScopes() {
|
||||
return TransformManager.SCOPE_FULL_PROJECT
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isIncremental() {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
transformInvocation.inputs.each { TransformInput input ->
|
||||
input.directoryInputs.each { DirectoryInput directoryInput ->
|
||||
scanClass(directoryInput.file)
|
||||
}
|
||||
input.jarInputs.each { JarInput jarInput ->
|
||||
scanClass(jarInput.file)
|
||||
}
|
||||
}
|
||||
|
||||
//log
|
||||
ScanRuntime.logScanInfo()
|
||||
ScanRuntime.buildComponentSdkInfo()
|
||||
|
||||
transformInvocation.inputs.each { TransformInput input ->
|
||||
input.directoryInputs.each { DirectoryInput directoryInput ->
|
||||
def dest = transformInvocation.outputProvider.getContentLocation(directoryInput.name,
|
||||
directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
|
||||
FileUtils.copyDirectory(directoryInput.file, dest)
|
||||
injectClass(dest)
|
||||
}
|
||||
input.jarInputs.each { JarInput jarInput ->
|
||||
def dest = transformInvocation.outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR)
|
||||
FileUtils.copyFile(jarInput.file, dest)
|
||||
injectClass(dest)
|
||||
}
|
||||
}
|
||||
|
||||
ScanRuntime.loadInjectInfo()
|
||||
ScanRuntime.clearScanInfo()
|
||||
Logger.buildOutput("transform cost : " + System.currentTimeMillis() - startTime + "ms")
|
||||
}
|
||||
|
||||
|
||||
private boolean filterClass(String fileName) {
|
||||
return !FileUtil.isValidClassFile(fileName)
|
||||
}
|
||||
|
||||
private boolean filterPackage(String fileName) {
|
||||
return false
|
||||
}
|
||||
|
||||
private void scanClass(File source) {
|
||||
if (source.isDirectory()) {
|
||||
source.eachFileRecurse { File file ->
|
||||
String filename = file.getName()
|
||||
if (filterClass(filename)) return
|
||||
ClassReader cr = new ClassReader(file.readBytes())
|
||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
|
||||
ClassVisitor cv = new ComponentScanClassVisitor(cw)
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES)
|
||||
}
|
||||
} else {
|
||||
JarFile jarFile = new JarFile(source)
|
||||
Enumeration<JarEntry> entries = jarFile.entries()
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement()
|
||||
String filename = entry.getName()
|
||||
if (filterPackage(filename)) break
|
||||
|
||||
if (filterClass(filename)) continue
|
||||
|
||||
InputStream stream = jarFile.getInputStream(entry)
|
||||
if (stream != null) {
|
||||
ClassReader cr = new ClassReader(stream.bytes)
|
||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
|
||||
ClassVisitor cv = new ComponentScanClassVisitor(cw)
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES)
|
||||
stream.close()
|
||||
}
|
||||
}
|
||||
jarFile.close()
|
||||
}
|
||||
}
|
||||
|
||||
private void injectClass(File source) {
|
||||
|
||||
if (source.isDirectory()) {
|
||||
source.eachFileRecurse { File file ->
|
||||
String filename = file.getName()
|
||||
if (filterClass(filename)) return
|
||||
ClassReader cr = new ClassReader(file.bytes)
|
||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
|
||||
ClassVisitor cv = new ComponentInjectClassVisitor(cw)
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES)
|
||||
FileOutputStream outputStream = new FileOutputStream(file)
|
||||
outputStream.write(cw.toByteArray())
|
||||
outputStream.close()
|
||||
}
|
||||
} else {
|
||||
Map<String, byte[]> tempModifiedClassByteMap = new HashMap()
|
||||
JarFile jarFile = new JarFile(source)
|
||||
Enumeration<JarEntry> entries = jarFile.entries()
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement()
|
||||
String filename = entry.getName()
|
||||
if (filterPackage(filename)) break
|
||||
|
||||
if (filterClass(filename)) continue
|
||||
|
||||
InputStream stream = jarFile.getInputStream(entry)
|
||||
if (stream != null) {
|
||||
ClassReader cr = new ClassReader(stream.bytes)
|
||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
|
||||
ClassVisitor cv = new ComponentInjectClassVisitor(cw)
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES)
|
||||
tempModifiedClassByteMap.put(filename, cw.toByteArray())
|
||||
stream.close()
|
||||
}
|
||||
}
|
||||
if (tempModifiedClassByteMap.size() != 0) {
|
||||
File tempJar = new File(source.absolutePath.replace('.jar', 'temp.jar'))
|
||||
if (tempJar.exists()) {
|
||||
tempJar.delete()
|
||||
}
|
||||
|
||||
entries = jarFile.entries()
|
||||
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(tempJar))
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry jarEntry = entries.nextElement()
|
||||
String filename = jarEntry.getName()
|
||||
ZipEntry zipEntry = new ZipEntry(filename)
|
||||
jarOutputStream.putNextEntry(zipEntry)
|
||||
if (tempModifiedClassByteMap.containsKey(filename)) {
|
||||
jarOutputStream.write(tempModifiedClassByteMap.get(filename))
|
||||
} else {
|
||||
InputStream inputStream = jarFile.getInputStream(jarEntry)
|
||||
jarOutputStream.write(inputStream.bytes)
|
||||
inputStream.close()
|
||||
}
|
||||
jarOutputStream.closeEntry()
|
||||
}
|
||||
jarOutputStream.close()
|
||||
FileOutputStream outputStream = new FileOutputStream(source)
|
||||
outputStream.write(tempJar.bytes)
|
||||
outputStream.close()
|
||||
tempJar.delete()
|
||||
}
|
||||
jarFile.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.plugin.component.asm
|
||||
package com.plugin.component.transform
|
||||
|
||||
import com.plugin.component.Logger
|
||||
|
||||
import com.plugin.component.transform.info.ComponentSdkInfo
|
||||
import com.plugin.component.transform.info.ScanRuntime
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
@@ -15,14 +17,14 @@ import org.objectweb.asm.commons.AdviceAdapter
|
||||
* <p>
|
||||
* visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )* visitEnd
|
||||
*/
|
||||
class ComponentInjectClassVisitor extends ClassVisitor {
|
||||
class InjectCodeAdapter extends ClassVisitor {
|
||||
|
||||
private static final String sCostCachePath = "com/plugin/component/CostCache"
|
||||
private static final String sComponentManagerPath = "com/plugin/component/ComponentManager"
|
||||
|
||||
private String className
|
||||
|
||||
ComponentInjectClassVisitor(ClassVisitor classVisitor) {
|
||||
InjectCodeAdapter(ClassVisitor classVisitor) {
|
||||
super(Opcodes.ASM7, classVisitor)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.plugin.component.transform
|
||||
|
||||
import com.android.build.api.transform.Context
|
||||
import com.android.build.api.transform.TransformException
|
||||
import com.android.build.api.transform.TransformInput
|
||||
import com.android.build.api.transform.TransformOutputProvider
|
||||
import com.plugin.component.Logger
|
||||
import com.plugin.component.transform.info.ScanRuntime
|
||||
import com.quinn.hunter.transform.HunterTransform
|
||||
import com.quinn.hunter.transform.RunVariant
|
||||
import org.gradle.api.Project
|
||||
|
||||
class InjectCodeTransform extends HunterTransform {
|
||||
|
||||
InjectCodeTransform(Project project) {
|
||||
super(project)
|
||||
this.bytecodeWeaver = new InjectCodeWeaver()
|
||||
}
|
||||
|
||||
@Override
|
||||
void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
|
||||
long startTime = System.currentTimeMillis()
|
||||
super.transform(context, inputs, referencedInputs, outputProvider, isIncremental)
|
||||
ScanRuntime.logInjectInfo()
|
||||
ScanRuntime.clearScanInfo()
|
||||
Logger.buildOutput("inject code cost : " + (System.currentTimeMillis() - startTime) + "ms")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RunVariant getRunVariant() {
|
||||
return super.getRunVariant()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean inDuplcatedClassSafeMode() {
|
||||
return super.inDuplcatedClassSafeMode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.plugin.component.transform
|
||||
|
||||
import com.quinn.hunter.transform.asm.BaseWeaver
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
|
||||
class InjectCodeWeaver extends BaseWeaver{
|
||||
|
||||
@Override
|
||||
protected ClassVisitor wrapClassWriter(ClassWriter classWriter) {
|
||||
return new ScanCodeAdapter(classWriter)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
package com.plugin.component.asm
|
||||
package com.plugin.component.transform
|
||||
|
||||
import com.plugin.component.anno.AutoInjectComponent
|
||||
import com.plugin.component.anno.AutoInjectImpl
|
||||
import com.plugin.component.anno.MethodCost
|
||||
import org.objectweb.asm.AnnotationVisitor
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
import com.plugin.component.transform.info.ScanComponentInfo
|
||||
import com.plugin.component.transform.info.ScanRuntime
|
||||
import com.plugin.component.transform.info.ScanSdkInfo
|
||||
import org.objectweb.asm.*
|
||||
import org.objectweb.asm.commons.AdviceAdapter
|
||||
|
||||
class ComponentScanClassVisitor extends ClassVisitor {
|
||||
class ScanCodeAdapter extends ClassVisitor {
|
||||
|
||||
private String className
|
||||
|
||||
ComponentScanClassVisitor(ClassVisitor classVisitor) {
|
||||
ScanCodeAdapter(ClassVisitor classVisitor) {
|
||||
super(Opcodes.ASM7, classVisitor)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.plugin.component.transform
|
||||
|
||||
import com.android.build.api.transform.Context
|
||||
import com.android.build.api.transform.TransformException
|
||||
import com.android.build.api.transform.TransformInput
|
||||
import com.android.build.api.transform.TransformOutputProvider
|
||||
import com.plugin.component.Logger
|
||||
import com.plugin.component.transform.info.ScanRuntime
|
||||
import com.quinn.hunter.transform.HunterTransform
|
||||
import com.quinn.hunter.transform.RunVariant
|
||||
import org.gradle.api.Project
|
||||
|
||||
class ScanCodeTransform extends HunterTransform {
|
||||
|
||||
ScanCodeTransform(Project project) {
|
||||
super(project)
|
||||
this.bytecodeWeaver = new ScanCodeWeaver()
|
||||
}
|
||||
|
||||
@Override
|
||||
void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
|
||||
long startTime = System.currentTimeMillis()
|
||||
super.transform(context, inputs, referencedInputs, outputProvider, isIncremental)
|
||||
ScanRuntime.logScanInfo()
|
||||
ScanRuntime.buildComponentSdkInfo()
|
||||
Logger.buildOutput("scan code cost : " + (System.currentTimeMillis() - startTime) + "ms")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RunVariant getRunVariant() {
|
||||
return super.getRunVariant()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean inDuplcatedClassSafeMode() {
|
||||
return super.inDuplcatedClassSafeMode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.plugin.component.transform
|
||||
|
||||
import com.quinn.hunter.transform.asm.BaseWeaver
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
|
||||
class ScanCodeWeaver extends BaseWeaver {
|
||||
|
||||
@Override
|
||||
protected ClassVisitor wrapClassWriter(ClassWriter classWriter) {
|
||||
return new InjectCodeAdapter(classWriter)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.plugin.component.asm
|
||||
package com.plugin.component.transform.info
|
||||
|
||||
class ComponentSdkInfo {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.plugin.component.asm
|
||||
package com.plugin.component.transform.info
|
||||
|
||||
class ScanComponentInfo {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.plugin.component.asm
|
||||
package com.plugin.component.transform.info
|
||||
|
||||
import com.android.annotations.NonNull
|
||||
import com.android.annotations.Nullable
|
||||
import com.android.builder.sdk.SdkInfo
|
||||
import com.plugin.component.Logger
|
||||
|
||||
class ScanRuntime {
|
||||
@@ -50,7 +49,7 @@ class ScanRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
static void loadInjectInfo() {
|
||||
static void logInjectInfo() {
|
||||
for (ComponentSdkInfo componentSdkInfo : componentSdkInfoList) {
|
||||
Logger.buildOutput(componentSdkInfo.toString())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.plugin.component.asm
|
||||
package com.plugin.component.transform.info
|
||||
|
||||
class ScanSdkInfo {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
apply plugin: 'com.android.library'
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.1"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
apply plugin: 'com.android.library'
|
||||
android {
|
||||
|
||||
compileSdkVersion 29
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user