关于注解,你须要知道的一些知识:
Annotation注解(一)- 基础
Annotation注解(二)- 进阶 - 自定义ButterKnifejava
在这篇博客中,咱们将利用前面讲解的知识,自定义一个轻量级的ButterKnife。segmentfault
尊重原创,转载请注明出处 https://segmentfault.com/a/11...
本文出自 强哥大天才的博客ide
在开始以前,咱们先来熟悉如下几个关键类:工具
Element
Elements
RoundEnvironment
ProcessingEnvironment
Element
:是一个接口
,表示注解修饰的对象
,例如类、成员变量、成员方法、包名等。ui
Element的分类this
package com.example; // PackageElement public class TestClass{ // ClassElement private String name; // VariableElement public TestClass() {} // ExecutableElement public void getName() {} // ExecutableElement }
Element的方法编码
public interface Element extends AnnotatedConstruct { Name getSimpleName(); Set<Modifier> getModifiers(); // 获取修饰符 Element getEnclosingElement(); // 获取父类元素 List<? extends Element> getEnclosedElements(); // 获取子类元素 <A extends Annotation> A getAnnotation(Class<A> var1); // 获取注解 TypeMirror asType(); // 能够根据TypeMirror,获取到Class对象 ElementKind getKind(); List<? extends AnnotationMirror> getAnnotationMirrors(); boolean equals(Object var1); int hashCode(); <R, P> R accept(ElementVisitor<R, P> var1, P var2); }
Elements
是一个操做Element的工具类
。code
public interface Elements { Name getName(CharSequence var1); PackageElement getPackageOf(Element var1); // 获取PackageElement PackageElement getPackageElement(CharSequence var1); TypeElement getTypeElement(CharSequence var1); // 获取TypeElement List<? extends Element> getAllMembers(TypeElement var1); // 获取子类Element }
了解了Element对象后,咱们就能够理解ClassNmae的另外2个get方法了对象
ClassName的3个重载方法接口
ClassName.get(String packageName, String... simpleName); ClassName.get(TypeElement element); ClassName.get(TypeMirror mirror);
RoundEnvironment
:主要用来获取,注解所修饰的Element对象
getElementsAnnotatedWith(Annotation.Class)
:获取全部注解了Annotation的Element对象Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Annotation.class); Set<? extends Element> elements = roundEnvironment.getRootElements();
ProcessingEnvironment
:主要用来获取,解析注解所需的工具类。
Elements
:操做Element的工具类Types
:操做TypeElement的工具类Filer
:建立文件的工具类public interface ProcessingEnvironment { Elements getElementUtils(); Types getTypeUtils(); Filer getFiler(); Locale getLocale(); Messager getMessager(); Map<String, String> getOptions(); SourceVersion getSourceVersion(); }
了解了上面几个关键类以后,咱们就能够开始定义属于咱们本身的 "轻量级ButterKnife" 了。
在这个例子中,咱们只须要2个注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface TargetClass { }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface InjectView { int value(); }
在注解处理器中,咱们重写了3个方法
init
:利用ProcessingEnvironment对象获取ElementsgetSupportedAnnotationTypes
:返回自定义注解的Set集合process
:实现具体的注入操做下面,咱们重点讲解下process的实现细节。
生成findViewById的方法
想要找到某个控件,就必须使用findViewById进行查找;所以咱们能够定义一个方法,这个方法会根据传入的activity对象,自动生成咱们所需的全部findViewById操做。
而咱们要作的就是:如何经过Annotation注解,自动生成下面这段代码?
public static void bind(Activity activity) { activity.field = (View)activity.findViewById(resId); .... .... }
第一步:找到所需的对象。
这里有3个重要的对象:activity、field、resId;这3个对象,能够根据@TargetClass、@InjectView注解获取到:
// 1. 获取全部注解了TargetClass的Element Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(TargetClass.class); for (Element element : elements) { TypeElement activityElement = (TypeElement) element; // 2. 获取单个类中,全部子Element List<? extends Element> members = elementUtils.getAllMembers(activityElement); for (Element fieldElement : members) { InjectView annotation = fieldElement.getAnnotation(InjectView.class); if (annotation != null) { // 3. 获取resID int redID = annotation.value(); } } }
第二步:生成所需的方法。
回忆一下,上个博客中介绍的JavaPoet的用法,咱们能够利用MethodSpec定义这个bind方法:
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(TypeName.VOID) .addParameter(ClassNmae.get(activityElement), "activity"); // 添加代码 methodBuilder.addStatement( "activity.$L= ($T) activity.findViewById($L)", fieldElement, ClassName.get(fieldElement.asType()), redID );
因为对于每个InjectView对象,都要生成一行代码,因此添加代码的这部分,须要放在for循环内部完成。
至此,就完成了bind方法的建立。
生成方法依赖的类
因为方法不能单独存在,咱们还须要给方法建立对应的容器:类。
类的建立,一样借助与JavaPoet,这里咱们用TypeSpec进行建立:
TypeSpec typeSpec = TypeSpec.classBuilder("View" + activityElement.getSimpleName()) .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .build();
为了区分每一个Activity,咱们给类取名:View + Activity类名。
同时,咱们还将以前建立的method方法,添加到了这个typeSpec类中。
建立Java文件
最后,咱们根据上面建立的TypeSpec类,利用JavaFile将真实的Java文件建立出来。
try { // 获取包名 PackageElement packageElement = elementUtils.getPackageOf(activityElement); String packageName = packageElement.getQualifiedName().toString(); // 建立文件 JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build(); javaFile.writeTo(processingEnv.getFiler()); } catch (Exception e) { e.printStackTrace(); }
完成上面步骤以后,咱们就能够在项目中使用这个轻量级的ButterKnife了。
注意:在注解完成以后,须要build下项目,才会在build目录生成对应的ViewXxxActivity类。
@TargetClass public class MainActivity extends AppCompatActivity { @InjectView(R.id.tv1) TextView tv1; @InjectView(R.id.tv2) TextView tv2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewMainActivity.bind(this); tv1.setText("Hello"); tv2.setText("World!"); } }
注意:
@AutoService(Processor.class) public class InjectViewProcessor extends AbstractProcessor { private Elements elementUtils; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); elementUtils = processingEnvironment.getElementUtils(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> set = new HashSet<>(); set.add(TargetClass.class.getCanonicalName()); set.add(InjectView.class.getCanonicalName()); return set; } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { // 1. 获取全部注解了TargetClass的Element Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(TargetClass.class); for (Element element : elements) { TypeElement activityElement = (TypeElement) element; // 建立方法 MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(TypeName.VOID) .addParameter(ClassName.get(activityElement), "activity"); // 2. 获取单个TypeElement中的,全部子Element List<? extends Element> members = elementUtils.getAllMembers(activityElement); for (Element fieldElement : members) { InjectView annotation = fieldElement.getAnnotation(InjectView.class); if (annotation != null) { // 3. 获取resID int redID = annotation.value(); // 添加代码 methodBuilder.addStatement( "activity.$L= ($T) activity.findViewById($L)", fieldElement, ClassName.get(fieldElement.asType()), redID ); } } MethodSpec methodSpec = methodBuilder.build(); // 建立类 TypeSpec typeSpec = TypeSpec.classBuilder("View" + activityElement.getSimpleName()) .addModifiers(Modifier.PUBLIC) .addMethod(methodSpec) .build(); try { // 获取包名 PackageElement packageElement = elementUtils.getPackageOf(activityElement); String packageName = packageElement.getQualifiedName().toString(); // 建立文件 JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build(); javaFile.writeTo(processingEnv.getFiler()); } catch (Exception e) { e.printStackTrace(); } } return false; } public String getPackageName(TypeElement typeElement) { PackageElement packageElement = elementUtils.getPackageOf(typeElement); return packageElement.getQualifiedName().toString(); } }