Android 学习(四):Android APT/Android 编译时技术

上一篇 Android 学习(三):Java 注解

Android APT学习

  1. 编译时技术作用生成模板代码
  2. 什么是编译时技术?


    6.png
1.0 学习目标
  • 模仿Databing findViewById() 功能,对APT 注解有一个简单实用认识
2.0 重点知识点
  • 谷歌注解处理器库 annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
  • 谷歌注解处理器库 compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
  • 抽象类 AbstractProcessor ,该类属于Java,所以建立module创建JavaLibrary
  • 注解处理器增加注解 @AutoService(Processor.class)
3.0 上代码学习
  1. 创建android项目
  2. 项目内创建注解的module,选择javalibrary ,名称为annotations
    注解Moudle.jpg
  3. 项目内创建注解处理器的module,选择 javalibrary,名称为annotations_compiler
    注解处理器Module.jpg
  4. 在项目/app/build.gradle下引入这个俩个moudle

正常引入module都为implementation因为annotations_compiler注解处理器所以改为 annotationProcessor

引入Module.jpg
  1. 在Module annotations注解中创建BindView 注解,用来传递控件ID(R.id.xxx)
    BindView.jpg
package com.zyj.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 定义注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value();
}
  1. 在Module annotations_compiler注解处理器 中创建AnnotationsCompiler,解析各个界面注解信息
    z5.jpg
  • 思路
  1. 初始化Filer ,作用:编译时获取到注解控件ID 写入文件就是把findViewById(R.id.xxx)写入文件。
  2. 设置注解处理器处理范围,只解读BindView 注解。
  3. 以下是process方法里面思路:
  4. 获取使用注解的所有Activity的节点。
    Set elements = roundEnvironment.getElementsAnnotatedWith(BindView.class)
  5. 把所有节点进行分类,Activity和其成员变量归为一类,存入map中。
    VariableElement Field 成员变量 、TypeElement class 类 、 PackageElement 包 、 等可查看Element 子类
  6. 分好类,存入map后,通过filer创建java文件,生成代码。
    **重点看注释
package com.zyj.anntotations_compiler;

import com.google.auto.service.AutoService;
import com.zyj.annotations.BindView;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * 注解处理器   用来生成代码
 * 

* 1. 注解处理器一定要继承一个抽象类 AbstractProcessor (AbstractProcessor 属于javax) * 2。需要依赖于谷歌服务库(这样注解就会在这边自动处理) * annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' * compileOnly 'com.google.auto.service:auto-service:1.0-rc4' * 3.依赖使用注解 @AutoService(Processor.class) 代表这个类就是一个注解处理器的类 */ @AutoService(Processor.class) public class AnnotationsCompiler extends AbstractProcessor { // 注意: 我们调试的时候需要打断点或者打印日志,而在处理器里面不起作用,所以我们通过 Messager去操作 Filer filer; Messager messager; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); filer = processingEnvironment.getFiler();// 文件 messager = processingEnvironment.getMessager();// 日志 messager.printMessage(Diagnostic.Kind.WARNING, "我们开始可以看日志了!日志类型是警告!"); } /** * 因为我们注解处理器不需要都要去处理, * 所以这个方法是声明注解处理器要处理的注解 * * @return */ @Override public Set getSupportedAnnotationTypes() { Set types = new HashSet<>(); // 1. String 类型里面可以添加包名类名,就能去筛选了 // 2. 注解处理器模块需要依赖注解模块 // 3. 这就说明这个注解处理器要处理的注解就是我们声明的这个注解BindView types.add(BindView.class.getCanonicalName()); return types; } /** * 1. 方法一 :实现该方法 * 2. 方法二:在该类处理器加注解 @SupportedSourceVersion() * 必须要有一个版本声明 * 声明支持的java 版本 * * @return */ @Override public SourceVersion getSupportedSourceVersion() { return processingEnv.getSourceVersion(); } // 该方法专门用来搜索注解的方法 @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { // 生成代码 // 去搜索出用到了BindView注解的节点 // 如果有多个Activity 则会有多个Element 元素,可通过上下文 activity.findViewById()获取节点元素 // VariableElement Field 成员变量 TypeElement class 类 PackageElement 包 等等都是节点 Set elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);// 根据注解获取节点 // 把每个Activity 和它里面的内容放到一起 HashMap> map = new HashMap<>(); for (Element element : elements) { VariableElement variableElement = (VariableElement) element; // 获取这个成员变量所有在的类的类名 TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();//获取成员变量的上一个节点===》就是这个类 typeElement.getQualifiedName().toString();// 获取类名--- 带包名 String className = typeElement.getSimpleName().toString();// 获取类名----不带包名 List variableElements = map.get(className);// 通过类名 获取 成员变量 if (variableElements == null) { variableElements = new ArrayList<>(); map.put(className, variableElements); } variableElements.add(variableElement); } // 生成代码 if (map.size()>0){ Writer writer = null; Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()){ String className = iterator.next(); List variableElements = map.get(className); // 获取包名 String packName = getPackName(variableElements.get(0)); // 创建一个类名 String newName = className+"_ViewBinder"; try { // 创建 java 文件 JavaFileObject sourceFile = filer.createSourceFile(packName + "." + newName); writer = sourceFile.openWriter(); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("package "+ packName+";\n"); stringBuffer.append("import android.view.View ;\n"); stringBuffer.append("public class "+ newName +" implements IButterKnifer<"+packName+"."+className+">{\n"); stringBuffer.append("public void bind("+packName+"."+className+" target){\n"); for (VariableElement variableElement :variableElements) { // 遍历成员变量,每一个变量都生成findViewById代码 // 获取成员变量名字 String variableName = variableElement.getSimpleName().toString(); // 获取到上面的注解所持有的value redID int resID = variableElement.getAnnotation(BindView.class).value(); stringBuffer.append("target."+variableName+"=target.findViewById("+resID+");\n"); } stringBuffer.append("}\n}\n"); writer.write(stringBuffer.toString()); } catch (IOException e) { e.printStackTrace(); }finally { try { if (writer!=null){ writer.close(); } } catch (IOException e) { e.printStackTrace(); } } } } return false; } /** * 根据成员变量获取包名 * @param variableElement * @return */ private String getPackName(VariableElement variableElement) { Element enclosingElement = variableElement.getEnclosingElement();// 获取上一级元素 PackageElement packageOf = processingEnv.getElementUtils().getPackageOf(enclosingElement);// 获取包节点 String packName = packageOf.getQualifiedName().toString();// 获取到包名 return packName; } }

  1. annotations_compiler注解处理器引入注解module和谷歌库

因为处理器annotations_compiler需要解读注解annotations,所以需要引入module,而引入谷歌库就是为了自动调用该注解处理器类


引入库.jpg
  1. 在项目app中新建包为apt,在其中创建IButterKnifer接口,AptActivity和IButterKnife类
    apt包.jpg
  • IButterKnifer接口

public interface IButterKnifer {
    void bind(T target);
}
  • IButterKnife类

传递上下文获取当前Activity内控件ID


public class IButterKnife {
    public static void bind(Object target){
        String name = target.getClass().getName()+"_ViewBinder";;
        try {
            Class aClass = Class.forName(name);
            if (IButterKnifer.class.isAssignableFrom(aClass)){
                // 判断 aClass 是不是 IButterKnifer类或子类
                IButterKnifer iButterKnifer = (IButterKnifer) aClass.newInstance();
                iButterKnifer.bind(target);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}
  • AptActivity
import android.os.Bundle;
import android.widget.TextView;
import com.zyj.annotations.BindView;
import com.zyj.obslove.R;
public class AptActivity extends AppCompatActivity {
    @BindView(R.id.text1)
    TextView text1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_apt);
        IButterKnife.bind(this);
        text1.setText("---------------------");
    }
}


    

你可能感兴趣的:(Android 学习(四):Android APT/Android 编译时技术)