公司网站建设的重要性,网站开发 实战,婚恋网站排名,个体工商户查询APT简介
APT英文全称#xff1a;Android annotation process tool是一种处理注释的工具#xff0c;它对源代码文件进行检测找出其中的Annotation#xff0c;使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文…APT简介
APT英文全称Android annotation process tool是一种处理注释的工具它对源代码文件进行检测找出其中的Annotation使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件文件具体内容由Annotation处理器的编写者决定APT还会编译生成源文件和原来的源文件将它们一起生成class文件。简言之APT可以把注解在编译时生成代码。
是一种处理注释的工具它对源代码文件进行检测并找出其中的 Annotation根据注解自动生成代码如果想要自定义的注解处理器能够运行必须要通过 APT 工具来处理。
简单说根据规则帮我们生成代码、生成类文件
编译时注解就是通过 APT 来通过注解信息生成代码来完成某些功能典型代表有 ButterKnife、Dagger、ARouter 等 ButterKnife 原理分析使用APT来处理
annotation的流程
1.定义注解如MyButterKnife2.定义注解处理器3.在处理器里面完成处理方式通常是生成java代码。4.注册处理器5.利用APT完成如下图的工作内容。 手写ButterKnife
想要完全理解ButterKnife底层的APT技术手写实现ButterKnife可以帮助更好地吸收这种技术。
准备工作
1创建Android工程并且在此项目中新建一个java Module取名为annotataion用于存放注解。 注意必须新建java library而不能是Android lib为什么只能新建java工程不能是Android工程后面讲 (2)创建第二个java library取名为annotation_process故名思议此module是存放注解处理器。 3为了简单起见此处就在annotaiton中创建两个注解BindView和OnClick分别作用于编译期因此定于为CLASS。
BindView作用初始化View成员变量替开发者实现findViewById。
Target(ElementType.FIELD)
Retention(RetentionPolicy.CLASS)
public interface BindView {int value();
}OnClick作用初始化Button等按钮替用户实现setOnClickListener等等。
Retention(RetentionPolicy.CLASS)
Target(ElementType.METHOD)
public interface OnClick {int[] value();
}4导入项目依赖
app module依赖annotation和annotation_process anontation_process依赖annotation。从此处可以说明为什么annotation和annotation_process必须定义为java libraryandroid工程可以依赖java工程但反之不行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sdm1iXSz-1676966714695)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/538be825bf864a1ea7b45d5379fd6519~tplv-k3u1fbpfcp-zoom-1.image)]
5在annotation_process的build.grade中添加以下两句依赖用于实现注解处理器。
annotationProcessorcom.google.auto.service:auto-service:1.0-rc4
compileOnly com.google.auto.service:auto-service:1.0-rc3定义注解处理器
1、继承AbstractAnnotation 2、在其类上添加AutoService 3、实现process方法
AutoService(Process.class)
public class AnnotationProcess extends AbstractProcessor {//通过io流动态生成代码private Filer filer;
Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);filer processingEnvironment.getFiler();}Overridepublic SourceVersion getSupportedSourceVersion() {return super.getSupportedSourceVersion();}Overridepublic SetString getSupportedAnnotationTypes() {SetString set new HashSet();set.add(BindView.class.getName());set.add(OnClick.class.getName());return set;}Overridepublic boolean process(Set? extends TypeElement set, RoundEnvironment roundEnvironment) {//将Activity和其内部所有被OnCLickBindView标记了的元素一一对应起来HashMapTypeElement, ElementForType hashMap findAnnotationForActivity(roundEnvironment);//写文件if (hashMap.size() ! 0) {Writer writer null;IteratorTypeElement iterator hashMap.keySet().iterator();while (iterator.hasNext()) {TypeElement element iterator.next();ElementForType elementForType hashMap.get(element);//获取Activity类名String className element.getSimpleName().toString();//生成新类名String newClassName className $$ButterKnife;//获取包名PackageElement packageElement processingEnv.getElementUtils().getPackageOf(element);String packageName packageElement.getQualifiedName().toString();JavaFileObject javaFileObject null;try {javaFileObject filer.createSourceFile(packageName . newClassName);writer javaFileObject.openWriter();//将所有要生成的代码存到StringBuffer中StringBuffer stringBuffer getStringBuffer(packageName, newClassName, element, elementForType);} catch (IOException e) {e.printStackTrace();}finally {try {writer.close();} catch (IOException e) {e.printStackTrace();}}}}return false;}
public StringBuffer getStringBuffer(String packageName, String newClazzName,TypeElement typeElement, ElementForType elementForType) {StringBuffer stringBuffer new StringBuffer();stringBuffer.append(package packageName ;\n);stringBuffer.append(import android.view.View;\n);stringBuffer.append(public class newClazzName {\n);stringBuffer.append(public newClazzName (final typeElement.getQualifiedName() target){\n);if (elementForType ! null elementForType.getViewElements() ! null elementForType.getViewElements().size() 0) {ListVariableElement viewElements elementForType.getViewElements();for (VariableElement viewElement : viewElements) {//获取到类型TypeMirror typeMirror viewElement.asType();//获取到控件的名字Name simpleName viewElement.getSimpleName();//获取到资源IDint resId viewElement.getAnnotation(BindView.class).value();stringBuffer.append(target. simpleName ( typeMirror )target.findViewById( resId );\n);}}if (elementForType ! null elementForType.getMethodElements() ! null elementForType.getMethodElements().size() 0) {ListExecutableElement methodElements elementForType.getMethodElements();for (ExecutableElement methodElement : methodElements) {int[] resIds methodElement.getAnnotation(OnClick.class).value();String methodName methodElement.getSimpleName().toString();for (int resId : resIds) {stringBuffer.append((target.findViewById( resId )).setOnClickListener(new View.OnClickListener() {\n);stringBuffer.append(public void onClick(View p0) {\n);stringBuffer.append(target. methodName (p0);\n);stringBuffer.append(}\n});\n);}}}stringBuffer.append(}\n}\n);return stringBuffer;}
private HashMapTypeElement, ElementForType findAnnotationForActivity(RoundEnvironment roundEnvironment) {HashMapTypeElement, ElementForType hashMap new HashMap();//获取到所有被BindView标记了的View元素Set? extends Element bindViewSet roundEnvironment.getElementsAnnotatedWith(BindView.class);//获取所有被OnClick标记了的Method元素Set? extends Element onClickSet roundEnvironment.getElementsAnnotatedWith(OnClick.class);for (Element element : bindViewSet) {VariableElement bindView (VariableElement) element;//获取含有该BindView标记对应的ActivityTypeElement typeElement (TypeElement) element.getEnclosingElement();//获取该Activity中所有被注解标记的元素ElementForType elementForType hashMap.get(typeElement);ListVariableElement variableElementList;if (elementForType ! null) {//获取该Activity中所有被BindView标记的View元素variableElementList elementForType.getViewElements();if (variableElementList null) {variableElementList new ArrayList();elementForType.setViewElements(variableElementList);}} else {//若ElementForType不存在则创建elementForType new ElementForType();variableElementList new ArrayList();elementForType.setViewElements(variableElementList);//判断hashmap中是否保存了elementForType和typeElementif (!hashMap.containsKey(typeElement)) {hashMap.put(typeElement, elementForType);}}variableElementList.add(bindView);}//onclick同理for (Element element : onClickSet) {ExecutableElement onClick (ExecutableElement) element;//获取含有该OnClick标记对应的ActivityTypeElement typeElement (TypeElement) element.getEnclosingElement();//获取该Activity中所有被注解标记的元素ElementForType elementForType hashMap.get(typeElement);ListExecutableElement executableElementList;if (elementForType ! null) {//获取该Activity中所有被OnClick标记的Method元素executableElementList elementForType.getMethodElements();if (executableElementList null) {executableElementList new ArrayList();elementForType.setMethodElements(executableElementList);}} else {//若ElementForType不存在则创建elementForType new ElementForType();executableElementList new ArrayList();elementForType.setMethodElements(executableElementList);//判断hashmap中是否保存了elementForType和typeElementif (!hashMap.containsKey(typeElement)) {hashMap.put(typeElement, elementForType);}}executableElementList.add(onClick);}return hashMap;}
}
ButterKnife在编译阶段会为每个使用了BindView、OnClick等注解的类生成一个新类因此创建了ElementForType用以封装同一个Activity中的所有被BindViewOnCLick等注解的元素。
/*** 此类用以封装同一个Activity中的所有被BindViewOnCLick注解的元素*/
public class ElementForType {//此出的VariableElement代表的控件元素private ListVariableElement viewElements;//此处的ExecutableElement代表的方法元素private ListExecutableElement methodElements;
public ElementForType() {}public ElementForType(ListVariableElement viewElements, ListExecutableElement methodElements) {this.viewElements viewElements;this.methodElements methodElements;}public ListVariableElement getViewElements() {return viewElements;}public void setViewElements(ListVariableElement viewElements) {this.viewElements viewElements;}public ListExecutableElement getMethodElements() {return methodElements;}public void setMethodElements(ListExecutableElement methodElements) {this.methodElements methodElements;}
}
定义ButterKnife
由于APT技术是在编译期替我们生成的一个新类因此需要注意的是ButterKnife.java中的newClassName类名必须要和AnnotationProcess.java中的newClassName一致否则无法生效。
public class ButterKnife {public static void bind(Context context){//获取类名String className context.getClass().getName();//获取生成的类的构造器String newClassName className $$ButterKnife;Constructor? constructor null;try {constructor Class.forName(newClassName).getConstructor();constructor.newInstance(context);} catch (Exception e) {e.printStackTrace();}}
}使用Butterknife
使用和ButterKnife一样即可
public class MainActivity extends AppCompatActivity {BindView(R.id.btn1)private Button btn1;BindView(R.id.btn2)private Button btn2;BindView(R.id.btn3)private Button btn3;BindView(R.id.text)private EditText text;
Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);text.setText(xixi);}OnClick({R.id.btn1,R.id.btn2,R.id.btn3})public void onClick(View view){switch (view.getId()){case R.id.btn1:Log.d(kkjj,按钮1);break;case R.id.btn2:Log.d(kkjj,按钮1);break;case R.id.btn3:Log.d(kkjj,按钮1);break;}}
}
上文解析了Android架构设计技能中APT技术实现butterknife更多的Android框架学习及高级Android进阶可参考《Android核心技术手册》里面内容包含上千个技术知识点。点击查看》》》
结
BUtterKnife通过注解注解处理器的方式在编译期动态地生成XXX$$ButterKnife.java文件在此文件中替开发者实现了findViewById、setOnClickListener等等操作。
所以ButterKnife等三方框架仍然通过findViewById、setOnClickListener操作实现的view绑定监听注册。因此并没有绕过Android原生的开发规则。