本文转载自微信公众号「Android开发编程」,解手教写具作者Android开发编程。把手转载本文请联系Android开发编程公众号。注解
通过APT实现一个功能,通过对View变量的注解,实现View的绑定
1、创建几个Library来声明
- Android Library:aptlibs 正常的写Android的lib
- Java or Kotlin Library:aptlib-anno (专门放我们编写的注解)
- Java or Kotlin Library :aptlib-processor (编写动态生成文件的逻辑)
- aptlibs
- plugins {
- id 'com.android.library'
- id 'kotlin-android'
- }
- aptlib-anno
- plugins {
- id 'java-library'
- }
- aptlib-processor
- 是plugins {
- id 'java-library'
- }
这个要记清楚,很多博主估计自己都没有写过apt,分不清楚AndroidLib和javaLib
apt 本来java 提供的,另外 Android库中不允许继承AbstractProcessor
2 、定义注解-自定义注解
记住要在 aptlib-anno 库下面创建
- @Retention(RetentionPolicy.CLASS)
- @Target(ElementType.FIELD)
- public @interface BindView {
- int value();
- }
定义了运行时注解BindView,其中value()用于获取对应View的id;
3、定义注解处理器-动态生成关联文件
aptlib-processor 库
首先在本lib下添加依赖
- dependencies {
- implementation 'com.google.auto.service:auto-service:1.0-rc2'
- implementation project(':aptlib-anno')
- }
创建BindViewProcessor
- @AutoService(Processor.class)
- public class BindViewProcessor extends AbstractProcessor {
- private Messager mMessager;
- private Elements mElementUtils;
- private Map<String, ClassCreatorProxy> mProxyMap = new HashMap<>();
- @Override
- public synchronized void init(ProcessingEnvironment processingEnv) {
- super.init(processingEnv);
- mMessager = processingEnv.getMessager();
- mElementUtils = processingEnv.getElementUtils();
- }
- @Override
- public Set<String> getSupportedAnnotationTypes() {
- HashSet<String> supportTypes = new LinkedHashSet<>();
- supportTypes.add(BindView.class.getCanonicalName());
- return supportTypes;
- }
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
- @Override
- public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
- mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
- mProxyMap.clear();
- //得到所有的注解
- Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
- for (Element element : elements) {
- VariableElement variableElement = (VariableElement) element;
- TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
- String fullClassName = classElement.getQualifiedName().toString();
- ClassCreatorProxy proxy = mProxyMap.get(fullClassName);
- if (proxy == null) {
- proxy = new ClassCreatorProxy(mElementUtils, classElement);
- mProxyMap.put(fullClassName, proxy);
- }
- BindView bindAnnotation = variableElement.getAnnotation(BindView.class);
- int id = bindAnnotation.value();
- proxy.putElement(id, variableElement);
- }
- //通过遍历mProxyMap,创建java文件
- for (String key : mProxyMap.keySet()) {
- ClassCreatorProxy proxyInfo = mProxyMap.get(key);
- try {
- mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName());
- JavaFileObject jfo = processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(), proxyInfo.getTypeElement());
- Writer writer = jfo.openWriter();
- writer.write(proxyInfo.generateJavaCode());
- writer.flush();
- writer.close();
- } catch (IOException e) {
- mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName() + "error");
- }
- }
- mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
- return true;
- }
- }
- public class ClassCreatorProxy {
- private String mBindingClassName;
- private String mPackageName;
- private TypeElement mTypeElement;
- private Map<Integer, VariableElement> mVariableElementMap = new HashMap<>();
- public ClassCreatorProxy(Elements elementUtils, TypeElement classElement) {
- this.mTypeElement = classElement;
- PackageElement packageElement = elementUtils.getPackageOf(mTypeElement);
- String packageName = packageElement.getQualifiedName().toString();
- String className = mTypeElement.getSimpleName().toString();
- this.mPackageName = packageName;
- this.mBindingClassName = className + "_ViewBinding";
- }
- public void putElement(int id, VariableElement element) {
- mVariableElementMap.put(id, element);
- }
- /**
- * 创建Java代码
- * @return
- */
- public String generateJavaCode() {
- StringBuilder builder = new StringBuilder();
- builder.append("package ").append(mPackageName).append(";\n\n");
- builder.append("import com.example.gavin.apt_library.*;\n");
- builder.append('\n');
- builder.append("public class ").append(mBindingClassName);
- builder.append(" { \n");
- generateMethods(builder);
- builder.append('\n');
- builder.append("}\n");
- return builder.toString();
- }
- /**
- * 加入Method
- * @param builder
- */
- private void generateMethods(StringBuilder builder) {
- builder.append("public void bind(" + mTypeElement.getQualifiedName() + " host ) { \n");
- for (int id : mVariableElementMap.keySet()) {
- VariableElement element = mVariableElementMap.get(id);
- String name = element.getSimpleName().toString();
- String type = element.asType().toString();
- builder.append("host." + name).append(" = ");
- builder.append("(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));\n");
- }
- builder.append(" }\n");
- }
- public String getProxyClassFullName()
- {
- return mPackageName + "." + mBindingClassName;
- }
- public TypeElement getTypeElement()
- {
- return mTypeElement;
- }
- }
4、写工具类BindViewTools
在aptlib项目中写绑定类
- public class BindViewTools {
- public static void bind(Activity activity) {
- Class clazz = activity.getClass();
- try {
- Class bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
- Method method = bindViewClass.getMethod("bind", activity.getClass());
- method.invoke(bindViewClass.newInstance(), activity);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
5、主项目app中引入
- implementation project(path: ':aptlib')
- annotationProcessor project(path: ':aptlib-process')
在MainActivity中,在View的前面加上BindView注解,把id传入即可
- public class MainActivity extends AppCompatActivity {
- @BindView(R.id.tv)
- TextView mTextView;
- @BindView(R.id.btn)
- Button mButton;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- BindViewTools.bind(this);
- mTextView.setText("bind TextView success");
- mButton.setText("bind Button success");
- }
- }
1、APT技术其实就是自定义注解和注解处理器,在编译期间生成Java文件,类似于IOC控制反转,可以方便的进行解耦;
2、如果你也可以实现很多不同的项目,比如路由框架等等,后续也会写一些apt的项目
责任编辑:武晓燕 来源: Android开发编程 ButterKnife工具APT
(责任编辑:探索)
凯撒文化(002425.SZ)业绩快报:2020年度净利润降40.8% 基本每股收益0.15元
白鹤滩水电站2022年累计生产清洁电能100亿千瓦时 可节约标准煤约306万吨
央行上海总部:10月人民币存款增加3311亿元 住户存款减少72亿元