微信公衆號:Android研究院 關注可瞭解更多的Android知識,專一於移動領域。問題或建議,請公衆號留言; 若是你以爲文章對你有幫助,歡迎讚揚[^1]java
[TOC]android
apt爲什麼如此重要呢?現今愈來愈多的第三方庫使用了apt技術,Dagger二、ButterKnife、ARouter等,在編譯時根據annotation生成相關的代碼邏輯,動態的生成Java class文件給開發帶來了很大的便利。git
首先要懂得annotation (註解)相關的基礎知識。github
APT 的全稱爲:Annotation Processing Tool 能夠解釋爲註解處理器, 它對源代碼文件進行檢測找出其中的Annotation,使用指定的Annotation進行額外的處理。 Annotation處理器在處理Annotation時能夠根據源文件中的Annotation生成額外的源文件和其它的文件(文件具體內容由Annotation處理器的編寫者決定),APT還會編譯生成的源文件和原來的源文件,將它們一塊兒生成class文件。bash
下面咱們經過一個例子來說解apt實現的原理。本例實現gradle版本是3.0.1微信
@Target(ElementType.TYPE) // 註解做用在類上
@Retention(RetentionPolicy.CLASS)
public @interface Test {
String path();
}
複製代碼
build.gradle 配置app
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
複製代碼
注意gradle的配置框架
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotationlib')
implementation 'com.google.auto.service:auto-servic:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
複製代碼
com.google.auto.service:auto-service:1.0-rc4 是註冊Processor的ide
com.squareup:javapoet:1.11.1 是生成class文件的第三方庫函數
定義Processor類
@AutoService(Processor.class)
//指定編譯的Java版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//指定處理的註解
@SupportedAnnotationTypes({"demo.prim.com.annotationlib.Test"})
public class MyClass extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 建立方法
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.addParameter(String[].class,"args")
.addStatement("$T.out.println($S)",System.class,"Hello JavaPoet")
.build();
//建立類
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWord")
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addMethod(main)
.build();
//建立Java文件
JavaFile file = JavaFile.builder("com.ecample.test", typeSpec)
.build();
try {
file.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
複製代碼
上述代碼會生成以下的類
public final class HelloWord {
public static void main(String[] args) {
System.out.println("Hello JavaPoet");
}
}
複製代碼
配置項目根目錄的build.gradle
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
複製代碼
配置app的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
//...
dependencies {
//..
compile project(':annotation')
apt project(':compiler')
}
複製代碼
編譯使用
在隨意一個類添加@Test註解
@Test(path = "main")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
複製代碼
點擊Android Studio的Rebuild Project 會生成以下文件:
這時即可以調用HelloWord類了。
經過apt來簡化 textView = findViewById(R.id.textView);。
通常的能夠這樣寫:
public class ManagerFindByMainActivity {
public void findById(MainActivity activity) {
activity.textView = activity.findViewById(R.id.textView);
}
}
複製代碼
上面一段代碼,這樣寫和以前的沒有區別,反而每有一個Activity都要新建一個類去找到ID。那麼咱們是否能夠經過apt來動態生成這個類呢?
答案固然是能夠的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BYView {
int value() default 0;
}
複製代碼
gradle 配置:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
implementation project(':lib-annotation')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
複製代碼
核心代碼實現:
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({Constance.DIACTIVITY})
public class ByProcessor extends AbstractProcessor {
private Elements elementUtils;
private Types typeUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set != null) {
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(DIActivity.class);
if (elementsAnnotatedWith != null) {//獲取設置DIActivity 註解的節點
//判斷註解的節點是否爲Activity
TypeElement typeElement = elementUtils.getTypeElement(Constance.Activity);
for (Element element : elementsAnnotatedWith) {
TypeMirror typeMirror = element.asType();//獲取註解節點的類的信息
DIActivity annotation = element.getAnnotation(DIActivity.class);//獲取註解的信息
if (typeUtils.isSubtype(typeMirror, typeElement.asType())) {//註解在Activity的類上面
TypeElement classElement = (TypeElement) element;//獲取節點的具體類型
//建立參數 Map<String,Class<? extends IRouteGroup>>> routes
ParameterSpec altlas = ParameterSpec
.builder(ClassName.get(typeMirror), "activity")//參數名
.build();
//建立方法
MethodSpec.Builder method = MethodSpec.methodBuilder
("findById")
// .addAnnotation(Override.class)
.addModifiers(PUBLIC, STATIC)
.returns(TypeName.VOID)
.addParameter(altlas);
//建立函數體
//獲取TypeElement的全部成員變量和成員方法
List<? extends Element> allMembers = elementUtils.getAllMembers(classElement);//??
//遍歷成員變量
for (Element member : allMembers) {
//找到被BYView註解的成員變量
BYView byView = member.getAnnotation(BYView.class);
if (byView == null) {
continue;
}
//構建函數體
method.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",
member.getSimpleName(),//註解節點變量的名稱
ClassName.get(member.asType()).toString(),//註解節點變量的類型
byView.value()));//註解的值
}
//建立類
TypeSpec typeSpec = TypeSpec.classBuilder("ManagerFindBy" + element.getSimpleName())
.addModifiers(PUBLIC, FINAL)//做用域
.addMethod(method.build())//添加方法
.build();
//建立Javaclass 文件
JavaFile javaFile = JavaFile.builder("com.prim.find.by", typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
} else {
throw new IllegalArgumentException("@DIActivity must of Activity");
}
}
}
return true;
}
return false;
}
}
複製代碼
implementation project(':lib_annotation')
annotationProcessor project(':lib_compiler')
複製代碼
@DIActivity
public class MainActivity extends AppCompatActivity {
@BYView(R.id.textView)
public TextView textView;
@BYView(R.id.textView1)
public TextView textView1;
@BYView(R.id.textView2)
public TextView textView2;
@BYView(R.id.button)
public Button button;
}
@DIActivity
public class SencodActivity extends AppCompatActivity {
@BYView(R.id.sencodText)
public TextView sencodText;
}
複製代碼
而後RebuildProject,就會獲得:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ManagerFindByMainActivity.findById(this);
textView.setText("我是APT找到的");
textView1.setText("我是APT找到的 --> 1");
textView2.setText("我是APT找到的 --> 2");
button.setText("我被APT找到我要跳轉");
}
public void click(View view) {
Intent intent = new Intent(this, SencodActivity.class);
startActivity(intent);
}
複製代碼
TypeElement:類
ExecutableElement:成員方法
VariableElement:成員變量
經過包名和類名獲取TypeName TypeName targetClassName = ClassName.get(「PackageName」, 「ClassName」);
經過Element獲取TypeName TypeName type = TypeName.get(element.asType());
獲取TypeElement的包名 String packageName = processingEnv.getElementUtils().getPackageOf(type).getQualifiedName().toString();
獲取TypeElement的全部成員變量和成員方法 List<? extends Element> members = processingEnv.getElementUtils().getAllMembers(typeElement);
Android的組件化專題: 組件化配置
###讚揚 若是你以爲到Android研究院對你有幫助,歡迎讚揚,有你的支持,Android研究院必定會愈來愈好!