Skip to content

Latest commit

 

History

History
360 lines (270 loc) · 146 KB

APT & Plugin.md

File metadata and controls

360 lines (270 loc) · 146 KB

1、APT KAPT auto_service javapoet

2、Transform scop jar directory ASM javassists

一、APT

Annotation Processing Tool 注解处理工具

Step1、定义注解

@Retention(RetentionPolicy.CLASS) // 注解的生命周期
@Target(ElementType.TYPE)
public @interface ModuleProvider {
  Class<?> interfaceClass();
  int type() default 0; // 默认值
}

Step2、实现自己的注解处理器

继承AbstractProcessor

public class ModuleInitProcessor extends AbstractProcessor {

 @Override
 public Set<String> getSupportedAnnotationTypes() {
  Set<String> annotationTypes = new LinkedHashSet<>();
  // 1、返回需要处理的注解
  annotationTypes.add(ModuleProvider.class.getCanonicalName());
  return annotationTypes;
 }
 
 @Override
 public SourceVersion getSupportedSourceVersion() {
  // 2、固定写法
  return SourceVersion.latestSupported();
 }

 @Override
 public synchronized void init(ProcessingEnvironment processingEnv) {
  super.init(processingEnv);
  mFiler = processingEnv.getFiler(); // 3、后续用于生成class文件
  mMessager = processingEnv.getMessager(); // 4、日志辅助类
 }
 
 @Override
 public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
  if (env.processingOver()) { // 5、判断是否是最后一轮处理
    generateInit();
  } else {
    processAnnotations(env);
  }
  return true;
 }
 
 private void processAnnotations(RoundEnvironment env) {
  // 6、通过注解类型获取元素集合
  Set<? extends Element> set = env.getElementsAnnotatedWith(ModuleProvider.class);
  。。。
 }
}

Step3、注册注解处理器

Way 1、三方库:AutoService

1)、build.gradle

implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

2)、MyProcessor

@AutoService(Processor.class) // 添加AutoService注解
public class ModuleInitProcessor extends AbstractProcessor {
}

Way 2、配置文件注册

resources/MEAT-INF/

gradle/

incremental.annotation.processors // Gradle (>= 4.7) 增量编译支持

services/

javax.annotation.processing.Processor

图片

增量处理器限制:

  1. 只能使用Filer API生成新的文件
  2. 不能依赖特定编译器的API
  3. 如果使用了Filer.creataResource(), Location参数只能是 CLASS_OUTPUT、SOURCE_OUTPUT、NATIVE_HEADER_OUTPUT Gradle 支持两种注解处理器的增量编译:isolatingaggregating

isolating:

相对aggregating、dynamic这是最快的一种增量注解处理器,也是最容易理解的一个.

限制:

  1. 要去每一个注解处理器只能使用一个相关的编译时注解去生成新的文件。简单来说就是不能使用两个以上的注解去生成一个新的文件。
  2. 对于每个用filer生成的文件,必须要在构造时传入一个originating element与其对应.
// 注意看第二个参数传入了一个element,并且只能传入一个
JavaFileObject file = filer.createSourceFile("com.xxx.classname", element);

重新编译源文件时,Gradle 将重新编译由源文件生成的所有文件。 删除源文件后,从其生成的文件也会被删除。 ??

  • aggregating: isolating不能使用多个注解生成一个文件,而aggregating是允许的,但是增量成功率和效率比不上isolating。

限制:

  1. 注解的Rentention必须是:CLASS OR RUNTIME
  2. 如果用户传递-parameters编译器参数,则它们只能读取参数名称。(不理解,估计也不常用) Gradle 将始终重新处理(但不会重新编译)APT 已处理的所有带注解的源文件。Gradle 将始终重新编译 APT 生成的任何文件。 ??
  • dynamic: 需要用户自己决定是否开启增量注解,那么dynamic就很适合.
kapt {
  useBuildCache = false
  
  arguments {
  	//将这个选项传递到注解处理器中
    arg("enableIncremental", "true")
  }
}

4、Other

4.1、Javapoet

辅助生成java文件的工具类

4.2、apt、annotationProcessor、kapt的区别?

APT:

APT(Annotation Processing Tool),即注解处理工具,在代码编译期对源代码进行扫描,找出代码中的注解,根据开发者定义的解析规则生成新的Java文件,新生成的Java文件最终也会生成class文件。APT处理流程如下:

annotationProcessor:

annotationProcessor在Android Gradle 2.2之后由Google引入的,是Gradle中内置的APT工具,同时支持 javac 和 jack 编译方式。使用方式(即Java语言下的使用方式)如下:

dependencies {
    annotationProcessor "com.xxx"
}

kapt:

Kotlin中不使用annotationProcessor,而是使用kapt,其使用方式为:

dependencies {
    kapt "com.xxx"
}

此外在Java和Kotlin两种语言下APT的配置命令也不一样:

Java:

android {
  defaultConfig {
    javaCompileOptions {
      annotationProcessorOptions {
        arguments = ["ARGU_NAME": "xxx"]
      }
    }
  }
}

Kotlin:

kapt {
    arguments {
        arg("ARGU_NAME": "xxx")
    }
}

二、Plugin

Step1、实现Plugin,注册自定义Transform

public class ModuleInitPlugin implements Plugin<Project> {
  @Override
  public void apply(Project project) {
    project.getExtensions().findByType(BaseExtension.class)
        .registerTransform(new ModuleInitTransform());
  }
}

Step2、自定义Transform

public class ModuleInitTransform extends Transform {
  
  @Override
  public String getName() {
    return "ModuleInitPlugin"; // 可以在编译Task中看到
  }

  @Override
  public Set<QualifiedContent.ContentType> getInputTypes() {
    return TransformManager.CONTENT_CLASS;
  }

  @Override
  public Set<? super QualifiedContent.Scope> getScopes() {
    return TransformManager.SCOPE_FULL_PROJECT;
  }
  
  @Override
  public boolean isIncremental() { // 是否支持增量编译
    return false;
  }
  
  @Override
  public void transform(TransformInvocation invocation) {
    for (TransformInput input : invocation.getInputs()) {
      // 遍历jar
      input.getJarInputs().parallelStream().forEach(jarInput -> {
      }
      // 遍历文件
      input.getDirectoryInputs().parallelStream().forEach(directoryInput -> {
      }
    }
  }
}

2.1、getInputTypes()

指明需要操作的内容。

ContentType:

  • DefaultContentType.CLASSES java代码
  • DefaultContentType.RESOURCES java资源
  • ExtendedContentType.DEX
  • ExtendedContentType.NATIVE_LIBS
  • ExtendedContentType.CLASS_ENHANCED
  • ExtendedContentType.DATA_BINDING
  • ExtendedContentType.DEX_ARCHIVE

2.2、getScopes()

指明需要搜索的目标范围

Scope:

  • PROJECT 当前项目内容
  • SUB_PROJECTS 子项目内容
  • EXTERNAL_LIBRARIES 外部依赖库
  • TESTED_CODE 测试代码
  • PROVIDED_ONLY provider 方式的本地或者远程依赖
  • PROJECT_LOCAL_DEPS 项目本地依赖(local jars)
  • SUB_PROJECTS_LOCAL_DEPS 子项目的本地依赖(local jars)

Step3、注册Plugin

resources

META-INFO.gradle-plugins

pluginname.properties

图片

implementation-class=com.kwai.sdk.init.plugin.ModuleInitPlugin

Step4、使用自定义Plugin

4.1、build.gradle

添加classpath

dependencies {
    classpath "com.android.tools.build:gradle:4.2.1"
    classpath 'com.kwai.sdk.init.plugin:moduleinitplugin:1.0.0'
}

4.2、module下build.gradle

使用自定义plugin

apply plugin: 'com.android.application'
apply plugin: 'moduleinitplugin'

5、Other

5.1 asm

5.3 修改Jar中的class

private void generateJarCode(CtClass ctClass) throws CannotCompileException, IOException {
  JarFile jarFile = new JarFile(mManagerSrc);
  File tempDir = mInvocation.getContext().getTemporaryDir();
  // 1、创建一个新jar文件
  File outputJar = new File(tempDir, mManagerSrc.getName());
  // 2、根据新jar文件,创建一个JarOutputStream
  JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(outputJar));
  Enumeration<JarEntry> enumeration = jarFile.entries();
  JarEntry jarEntry;
  // 3、遍历之前的jar
  while (enumeration.hasMoreElements()) {
    // 4、写入新的jar中
    jarEntry = enumeration.nextElement();
    jarOutputStream.putNextEntry(new ZipEntry(jarEntry.getName()));
    if (checkPackage(jarEntry.getName()) && isManagerClass(jarEntry.getName())) {
      jarOutputStream.write(ctClass.toBytecode());
    } else {
      InputStream inputStream = jarFile.getInputStream(jarEntry);
      jarOutputStream.write(IOUtils.toByteArray(inputStream));
      inputStream.close();
    }
    jarOutputStream.closeEntry();
  }
  jarOutputStream.close();
  jarFile.close();
  // 5、替换jar
  saveModifiedFile(outputJar, mManagerSrc);
}

private void saveModifiedFile(File newFile, File oldFile) throws IOException {
  if (oldFile.exists()) {
    oldFile.delete();
  }
  FileUtils.copyFile(newFile, oldFile);
}