Android編譯期代碼生成之apt實踐入門

如今 Android 主流庫中使用 apt 的愈來愈多,如Dagger2,ButterKnife,DBflow等。不研究一下其怎麼玩的,內心實在是不舒服斯基,因此就有了這篇apt代碼簡單生成的文章。文章的末尾,會附上一些關於註解的基礎知識,有興趣的童鞋能夠再去看看。html

Annotation庫-定義註解

首先,咱們得須要新建一個名稱爲annotation的Java Library。這裏簡單的建一個@interfact的註解類便可。以下:java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Test {
    String value();
}

能夠看到的是,這是編譯時期的註解,主要做用於Class。以後,在調用的地方就是須要使用咱們的這個註解。android

Compiler庫-註解處理器

1.使用庫引入

這裏,也使用的是Java Library,咱們把報名定爲 compiler,先定義gradle文件:git

apply plugin: 'java'

sourceCompatibility = 1.7
targetCompatibility = 1.7

dependencies {
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile 'com.squareup:javapoet:1.7.0'
    compile project(':annotation')
}

代碼中,引入兩個庫,AutoService主要的做用是註解processor類,並對其生成 META-INF 的配置信息。github

JavaPoet這個庫的主要做用就是幫助咱們經過類調用的形式來生成代碼。app

2. 定義Processor類

創建一個名稱爲TestProcessor的類,以下:ide

@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(Test.class.getCanonicalName());
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }
}

其中要注意的是使用AutoSerivce的註解,這樣就不用再手動配置 META-INF文件了。方法getSupportedAnnotationTypes則是定義咱們針對生成的註解類,方法process則是咱們的重頭戲,其中則是咱們生成代碼的主要邏輯之處:學習

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Test.class);
  for (Element element : set) {
    if (element.getKind() != ElementKind.CLASS) {
      processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "only support class");
    }
    MethodSpec main = MethodSpec.methodBuilder("main")
      .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
      .returns(void.class)
      .addParameter(String[].class, "args")
      .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
      .build();

    TypeSpec helloWorld =
      TypeSpec.classBuilder("HelloWorld").addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(main).build();
    JavaFile javaFile = JavaFile.builder("com.lighters.apt", helloWorld).build();

    try {
      javaFile.writeTo(processingEnv.getFiler());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  return false;
}

這裏簡單使用JavaPoet文檔中的第一個example, 生成一個簡單的HelloWorld類。你們可本身行去查看JavaPoet的更多用法,支持各類姿式生成Java的代碼,並與Processor完美契合。gradle

代碼調用

準備工做都完成以後,接下來就在咱們的主目錄app下面,經過添加註解,來查看咱們的代碼生成邏輯。ui

1.添加依賴

在根目錄的build.gradle文件中的dependencies節點下面添加以下代碼:

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

app的build.gradle中添加以下代碼:

apply plugin: 'com.neenbedankt.android-apt'
dependencies {
    compile project(':annotation')
    apt project(':compiler')
}

2.添加註解

這裏,就偷一個小懶,在MainActivity上,添加註解Test,格式以下:

@Test("haha")
public class MainActivity extends AppCompatActivity {
}

3.代碼生成

注意,這裏定義的註解爲編譯期的註解,因此代碼的生成,只須要經過執行Rebuild便可。執行完成以後,在app的build/generated/source/apt目錄下,便可看到我們的代碼,如圖:
代碼生成

總結

apt代碼的生成是定義編譯期的註解,再經過繼承Proccesor實現代碼生成邏輯,實現了編譯期生成代碼的邏輯。相對於在運行期經過反射來講,提升了程序的運行速度。這裏只是簡單引導你們搭建本身的apt處理器,更多的內容期待你們各自玩出花來。

學習資料

附上一篇標準的編譯期代碼生成,以及trinea關於annotation的詳細介紹。

另外,使用apt的代碼庫Dagger2, Butterknife你們可自行深刻研究了。

轉載請註明原文連接: http://alighters.com/blog/2016/05/10/apt-code-generate/

相關文章
相關標籤/搜索