前置知识:Annotation注解
一,APT的介绍
APT英文全称:Android annotation process tool是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成源文件和原来的源文件,将它们一起生成class文件。简言之:APT可以把注解,在编译时生成代码。
二,APT的处理要素
注解处理器(AbstractProcess)+代码处理(javaPoet)+处理器注册(AutoService)+apt
三,使用APT来处理annotation的流程
1.定义注解(如@MyButterKnife)
2.定义注解处理器
3.在处理器里面完成处理方式,通常是生成java代码。
4.注册处理器
5.利用APT完成如下图的工作内容。

下面介绍如何利用apt技术实现自己的依赖注入框架
首先new一个project(主工程),再new一个名为Annotation的java library,build.gradle 如下
apply plugin: 'java-library'
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar'])}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
编写如下的类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface MyButterKnife {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bind {
int value()default 0;
}
新建名为compiler的java library,build.gradle 如下
apply plugin:'java-library'
dependencies {
implementation fileTree(dir:'libs',include: ['*.jar'])
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation 'com.squareup:javapoet:1.7.0'
implementation project(':Annotation')
}
sourceCompatibility ="1.7"
targetCompatibility ="1.7"
注解处理器如下
@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
private Elements elementUtils;
@Override
public Set<String> getSupportedAnnotationTypes() {
// 规定需要处理的注解
return Collections.singleton(MyButterKnife.class.getCanonicalName());
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("DIProcessor");
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(MyButterKnife.class);
for (Element element : elements) {
// 判断是否Class
TypeElement typeElement = (TypeElement) element;
List<? extends Element> members = elementUtils.getAllMembers(typeElement);
MethodSpec.Builder bindViewMethodSpecBuilder = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.VOID)
.addParameter(ClassName.get(typeElement.asType()), "activity");
for (Element item : members) {
Bind diView = item.getAnnotation(Bind.class);
if (diView == null){
continue;
}
bindViewMethodSpecBuilder.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",item.getSimpleName(),ClassName.get(item.asType()).toString(),diView.value()));
}
TypeSpec typeSpec = TypeSpec.classBuilder("DI" + element.getSimpleName())
.superclass(TypeName.get(typeElement.asType()))
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(bindViewMethodSpecBuilder.build())
.build();
JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private String getPackageName(TypeElement type) {
return elementUtils.getPackageOf(type).getQualifiedName().toString();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
}
主工程引入注解处理器和自定义注解,app/build.gradle 如下
apply plugin:'com.android.application'
//apply plugin: 'com.neenbedankt.android-apt' 高版本gradle中已弃用
android {
compileSdkVersion28
defaultConfig {
applicationId"com.ql.aptapplication"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner"android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabledfalse
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir:'libs',include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation project(':Annotation')
annotationProcessor project(':compiler')
}
主工程中使用
@MyButterKnife
public class MainActivity extends AppCompatActivity {
@Bind(R.id.textview)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DIMainActivity.bindView(this);
textView.setText("hello annotation");
}
}
点击rebuild ,可以在 /app/build/generated/source/apt/debug 目录下面看到生成的文件DIMainActivity
public final class DIMainActivity extends MainActivity {
public static void bindView(MainActivity activity) {
activity.textView = (android.widget.TextView) activity.findViewById(2131165319);
}
}
网友评论