当前位置: 首页 > 科技观察

带注释的APT应用详解(手把手教你写ButterKnife工具)

时间:2023-03-19 09:51:53 科技观察

本文转载自微信公众号《安卓开发编程》,作者安卓开发编程。转载本文请联系Android开发编程公众号。1、什么是APT?有什么用,带着疑惑学习APT(AnnotationProcessingTool),注解处理器,是处理注解的工具。准确的说是javac的一个工具,用来编译Scan和处理注解。注解处理器将Java代码(或编译后的字节码)作为输入并生成.java文件作为输出;简单来说就是编译时通过注解生成.java文件;使用APT的好处是方便,简单,重复代码少很多;使用过ButterKnife、Dagger、EventBus等注解框架的同学可以感觉到,使用这些框架可以节省很多代码,写一些注解就行了,他们只是用注解来帮助生成一些高效的代码;2、APT应用——像ButterKnife这样写一个注释通过APT实现一个功能,通过注释View变量实现View的绑定1、创建几个Libraries声明AndroidLibrary:aptlibs正常写AndroidlibJavaorKotlinLibrary:aptlib-anno(具体把注释放上我们写了)JavaorKotlinLibrary:aptlib-processor(编写动态生成文件的逻辑)aptlibsplugins{id'com.android.library'id'kotlin-android'}aptlib-annoplugins{id'java-library'}aptlib-processorisplugins{id'java-library'}。这个要记清楚。很多博主估计没写过apt,分不清AndroidLib和javaLibapt的区别。本来是java提供的,另外Android库不允许继承AbstractProcessor2,定义注解-自定义注解记得在@Retention(RetentionPolicy.CLASS)@Target(ElementType.FIELD)public@interfaceBindView{intvalue();}下创建aptlib-anno库定义运行时注解BindView,其中value()用于获取对应View的id;@Retention(RetentionPolicy.CLASS):表示编译时注解@Target(ElementType.FIELD):表示注解的范围是类成员(构造函数、方法、成员变量)@Retention:保留定义的长度时间RetentionPoicy.SOURCE,RetentionPoicy.CLASS、RetentionPoicy.RUNTIME@Target:定义修改对象范围TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、LOCAL_VARIABLE等。3.定义注解处理器——动态生成关联文件。aptlib-processor库先在这个lib下添加依赖dependencies{implementation'com.google.auto.service:auto-service:1.0-rc2'implementationproject(':aptlib-anno')}createBindViewProcessor@AutoService(Processor.class)publicclassBindViewProcessorextendsAbstractProcessor{privateMessarmMessager;privateElementsmElementCreUtils;privateMapmProxyMap=newHashMap<>();@Overridepublicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnv){super.init(processingEnv);mMessager=processingEnv.getMessager();mElementUtils=processingEnv.getElementUtils();}@OverridepublicSetgetSupportedString>supportTypes=newLinkedHashSet<>();supportTypes.add(BindView.class.getCanonicalName());returnssupportTypes;}@OverridepublicSourceVersiongetSupportedSourceVersion(){returnSourceVersion.latestSupported();}@Overridepublicbooleanprocess(Setset,RoundEnvironmentroundEnv){mMessager.printMessage(Diagnostic.Kind.NOTE,"processing...");mProxyMap.clear();//得到所有的注解Set<?extendsElement>elements=roundEnvironment.getElementsAnnotatedWith(BindView.class);for(Elementelement:elements){VariableElementvariableElement=(VariableElement)element;TypeElementclassElement=(TypeElement)variableElement.getEnclosingElement();StringfullClassName=classElement.getQualifiedName().toString();ClassCreatorProxyproxy=mProxyMap.get(fullClassName);if(proxy==null){proxy=newClassCreatorProxy(mElementUtils,classElement);mProxyMap.put(fullClassName,proxy);}BindViewbindAnnotation=variableElement.getAnnotation(BindView.class);intid=bindAnnotation.value();proxy.putElement(id,variableElement);}//通过遍历mProxyMap,创建java文件for(Stringkey:mProxyMap.keySet()){ClassCreatorProxyproxyInfo=mProxyMap.get(key);try{mMessager.printMessage(诊断.种类.NOTE,"-->create"+proxyInfo.getProxyClassFullName());JavaFileObjectjfo=processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(),proxyInfo.getTypeElement());Writerwriter=jfo.openWriter();作者。write(proxyInfo.generateJavaCode());writer.flush();writer.close();}catch(IOExceptione){mMessager.printMessage(Diagnostic.Kind.NOTE,"-->create"+proxyInfo.getProxyClassFullName()+"error");}}mMessager.printMessage(Diagnostic.Kind.NOTE,"processfinish...");returntrue;}}publicclassClassCreatorProxy{privateStringmBindingClassName;privateStringmPackageName;privateTypeElementmTypeElement;privateMapmVariableElementMap=newHashMap<>();publicClassCreatorProxy(ElementselementUtils,TypeElementclassElement){this.mTypeElement=classElement;PackageElementpackageElement=elementUtils.getPackageOf(mTypeElement);StringpackageName=packageElement.getQualifiedName().toString();StringclassName=mTypeElement.getSimpleName().toString();this.mPackageName=packageName;this.mBindingClassName=className+"_ViewBinding";}publicvoidputElement(intid,VariableElementelement){mVariableElementMap.put(id,element);}/***创建Java代码*@return*/publicStringgenerateJavaCode(){StringBuilderbuilder=newStringBuilder();builder.append("package").append(mPackageName).append(";\n\n");builder.append("importcom.example.gavin.apt_library.*;\n");builder.append('\n');builder.append("publicclass").append(mBindingClassName);builder.append("{\n");generateMethods(builder);builder.append('\n');builder.append("}\n");returnbuilder.toString();}/***加入方法*@parambuilder*/privatevoidgenerateMethods(StringBuilderbuilder){builder.append("publicvoidbind("+mTypeElement.getQualifiedName()+"host){\n");for(intid:mVariableElementMap.keySet()){VariableElementelement=mVariableElementMap.get(id);Stringname=element.getSimpleName().toString();Stringtype=element.asType().toString();builder.append("host."+name).append("=");builder.append("("+type+")(((android.app.Activity)host).findViewById("+id+"));\n");}builder.append("}\n");}publicStringgetProxyClassFullName(){returnmPackageName+"."+mBindingClassName;}publicTypeElementgetTypeElement(){returnmTypeElement;}}init:初始化可以获取ProcessingEnviroment,ProcessingEnviroment提供了很多有用的工具类Elements、Types和FilergetSupportedAnnotationTypes:指定这个注解处理器注册到哪个注解,这里是注解BindViewgetSupportedSourceVersion:指定使用的Java版本,一般这里返回SourceVersion.latestSupported()过程:可以在这里编写扫描、求值、处理注解的代码,生成Java文件auto-servicelibrary:自动生成代码需要用到的库){Classclazz=activity.getClass();try{ClassbindViewClass=Class.forName(clazz.getName()+"_ViewBinding");Methodmethod=bindViewClass.getMethod("bind",activity.getClass());method.invoke(bindViewClass.newInstance(),activity);}catch(Exceptione){e.printStackTrace();}}}5.引入implementationproject(path:':aptlib')annotationProcessorproject(path:':aptlib-process'intothemainprojectapp)在MainActivity中,在fron中添加BindView注解View的t,传入idpublicclassMainActivityextendsAppCompatActivity{@BindView(R.id.tv)TextViewmTextView;@BindView(R.id.btn)ButtonmButton;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);BindViewTools.bind(this);mTextView.setText("bindTextViewsuccess");mButton.setText("bindButtonsuccess");}}总结1,APT该技术实际上是自定义注释和注释处理器。编译时生成Java文件,类似于IOC控制反转,可以轻松解耦;2.如果你也可以实现很多不同的项目,比如路由框架等,我以后也会写一些apt的项目