androidstudio中android:process,在 Android Studio 中使用 Annotation Processor

Java 的 Annotation Processor 是非常有用的功能,很多常用的库和框架都使用了 Annotation Processor 来生成代码,比如Butter Knife 就用来生成 findViewById 等代码。

对于一些模板代码使用 Annotation Processor 来自动生成可以提高编写代码的效率和质量,手工编写毕竟容易出现纰漏,工具自动生成是有质量保证的。本文是由 Aitor Viana 编写的如何在 Android Studio 中使用 Annotation Processor 的介绍。

Annotation Processor 主要涉及 3 部分,注解本身(Annotation)、注解处理器(Annotation Processor)以及 在 Android Studio 中如何使用注解处理器。

本文通过如何使用注解来自动生成 Parcelable 接口的代码。

注解

注解就不用详细介绍了,在 Android 编码中应该经常使用。自定义一个表示需要自动生成 Parcelable 的注解: AutoParcel。

在 Android Studio 中创建一个 Java module,名字为 library 。在这个模块中创建这个自定义的 AutoParcel 注解类:

03f654d29ea16732292c65d627289b35.png

Java

package com.example.autoparcel;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.TYPE) // 代表在类级别上才能使用该注解

@Retention(RetentionPolicy.SOURCE) // 代表该注解只存在源代码中,编译后的字节码中不存在

public @interface AutoParcel {}

1

2

3

4

5

6

7

8

9

packagecom.example.autoparcel;

importjava.lang.annotation.ElementType;

importjava.lang.annotation.Retention;

importjava.lang.annotation.RetentionPolicy;

importjava.lang.annotation.Target;

@Target(ElementType.TYPE)// 代表在类级别上才能使用该注解

@Retention(RetentionPolicy.SOURCE)// 代表该注解只存在源代码中,编译后的字节码中不存在

public@interfaceAutoParcel{}

由于 AutoParcel 只在源代码中存在,编译后没有在字节码中,所以最最终的运行时是没有影响的。

由于这个 library 库需要在 Android 项目中引用,所以需要修改其 gradle 文件制定编译的 Java 语言版本(library/build.gradle ):

Java

apply plugin: 'java'

// This module will be used in Android projects, need to be

// compatible with Java 1.7

sourceCompatibility = JavaVersion.VERSION_1_7

targetCompatibility = JavaVersion.VERSION_1_7

dependencies {

...

}

1

2

3

4

5

6

7

8

9

10

11

applyplugin:'java'

// This module will be used in Android projects, need to be

// compatible with Java 1.7

sourceCompatibility=JavaVersion.VERSION_1_7

targetCompatibility=JavaVersion.VERSION_1_7

dependencies{

...

}

注解处理器

注解处理器的功能就是用来读取代码中的注解然后来生成相关的代码。

创建一个 Java module 名字为 “compiler”。 该模块在编译的时候,来获取哪些类使用了 AutoParcel 注解,然后把继承这些类实现 Parcelable 的代码。该模块并不在 Android 项目中引用,只存在于编译的时候。所以这个模块的 Java 版本号可以随意指定(Java 8 、9)。

创建一个 AutoParcelProcessor 类来处理注解:

a8b6685d0ab0b59bffd9b33c102764f1.png

Java

package com.example.autoparcel.codegen;

@SupportedAnnotationTypes("com.example.autoparcel.AutoParcel")

public final class AutoParcelProcessor extends AbstractProcessor {

@Override

public boolean process(

Set extends TypeElement> annotations,

RoundEnvironment env) {

...

}

}

1

2

3

4

5

6

7

8

9

10

11

packagecom.example.autoparcel.codegen;

@SupportedAnnotationTypes("com.example.autoparcel.AutoParcel")

publicfinalclassAutoParcelProcessorextendsAbstractProcessor{

@Override

publicbooleanprocess(

Set<?extendsTypeElement>annotations,

RoundEnvironmentenv){

...

}

}

对于该类有几点要求:

1. 需要继承至 AbstractProcessor

2. 需要使用类的全称(包含包名)来指定其支持的注解类型(com.example.autoparcel.AutoParcel)

3. 实现 process() 函数,在该函数中来处理所支持的注解类型并生成需要的代码。

下面只是介绍了实现 process() 函数的关键部分,完整代码参考最后的项目。

如果没有其他处理器需要继续处理该注解,则 process() 返回 true。针对我们这个情况,只有 AutoParcelProcessor 需要处理 AutoParcel 注解,所以该函数返回 true。

Java

package com.example.autoparcel.codegen;

...

@Override

public boolean process(

Set extends TypeElement> annotations,

RoundEnvironment env) {

Collection extends Element> annotatedElements =

env.getElementsAnnotatedWith(AutoParcel.class);

List types =

new ImmutableList.Builder()

.addAll(ElementFilter.typesIn(annotatedElements))

.build();

for (TypeElement type : types) {

processType(type);

}

// 返回 true ,其他处理器不关心 AutoParcel 注解

return true;

}

private void processType(TypeElement type) {

String className = generatedSubclassName(type);

String source = generateClass(type, className);

writeSourceFile(className, source, type);

}

private void writeSourceFile(

String className,

String text,

TypeElement originatingType) {

try {

JavaFileObject sourceFile =

processingEnv.getFiler().

createSourceFile(className, originatingType);

Writer writer = sourceFile.openWriter();

try {

writer.write(text);

} finally {

writer.close();

}

} catch (IOException e) {// silent}

}

...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

packagecom.example.autoparcel.codegen;

...

@Override

publicbooleanprocess(

Set<?extendsTypeElement>annotations,

RoundEnvironmentenv){

Collection<?extendsElement>annotatedElements=

env.getElementsAnnotatedWith(AutoParcel.class);

Listtypes=

newImmutableList.Builder()

.addAll(ElementFilter.typesIn(annotatedElements))

.build();

for(TypeElementtype:types){

processType(type);

}

// 返回 true ,其他处理器不关心 AutoParcel  注解

returntrue;

}

privatevoidprocessType(TypeElementtype){

StringclassName=generatedSubclassName(type);

Stringsource=generateClass(type,className);

writeSourceFile(className,source,type);

}

privatevoidwriteSourceFile(

StringclassName,

Stringtext,

TypeElementoriginatingType){

try{

JavaFileObjectsourceFile=

processingEnv.getFiler().

createSourceFile(className,originatingType);

Writerwriter=sourceFile.openWriter();

try{

writer.write(text);

}finally{

writer.close();

}

}catch(IOExceptione){// silent}

}

...

注解处理器类编写完后,还需要创建一个 java META_INF 文件来告诉系统具有注解处理功能。Java 代码在编译的时候,系统编译器会查找所有的 META_INF 中的注册的注解处理器来处理注解。

在 Android studio 的 compiler 项目中创建如下目录:

compiler/src/main/resources/META_INF/services

在 services 目录下面创建一个名字为 “javax.annotation.processing.Processor” 的文本文件:

e3aaab0c0e43427eb44c4498871b6de2.png

该文件中每行一个注解处理器的全名:

Java

com.example.autoparcel.codegen.AutoParcelProcessor

1

2

com.example.autoparcel.codegen.AutoParcelProcessor

这样,注解处理器就创建好了。

在 Android Studio 中使用

在 Android Studio 跟目录的 settings.gradle 中添加前面创建的两个模块:

Java

include ':app', ':compiler', ':library'

1

2

include':app',':compiler',':library'

在 app/build.gradle 中添加前面创建的两个模块为依赖项:

Java

...

dependencies {

...

provided project(':library')

apt project(':compiler')

...

}

1

2

3

4

5

6

7

8

...

dependencies{

...

providedproject(':library')

aptproject(':compiler')

...

}

注意上面 library 项目使用的是 provided 依赖,这是由于 provided 中的代码只在编译的时候存在,并不会打包到最终的应用中去,所以可以使用 provided。 二 compiler 项目为注解编译器,通过使用 android-apt 插件来指定 apt 选项。

apt 是 Annotation Processing Tool 的缩写。

现在就可以在项目中使用 AutoParcel 注解了:

Java

@AutoParcel

public class Foo{

...

}

1

2

3

4

5

@AutoParcel

publicclassFoo{

...

}

本文示例来源于真实的项目 auto-parcel.

使用 Auto-Parcel 就再也不需要手工编写 Parcelable 相关的代码啦。解放双手从今天开始!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值