现在很多Android的库都是采用注解的方式,来完成某些功能。
今天,我们就仿照butterKnife,使用注解来完成一个简单的控件初始化。完成这个功能我们就需要知道一个东西,注解处理器。
什么是注解处理器?
注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。我们可以用自己定义的注解和注册注解处理器来做一些事情。
这里我们就需要自定义一个处理器,用来处理i注解并生成一个java文件,让这个文件跟其他普通手写的java文件一样被javac编译。
抽象处理器 AbstractProcessor
我们自定义的处理器需要继承这个抽象处理器。
一般我们会用到它的四个方法。
init(ProcessingEnvironment processingEnvironment)
里面提供了Filer等工具类。注解处理器可以用Filer类创建新文件(源文件、类文件、辅助资源文件)。由此方法创建的源文件和类文件将由管理它们的工具(javac)处理。
getSupportedSourceVersion()
支持JDK的版本,通常返回SourceVersion.latestSupported()。
public Set getSupportedAnnotationTypes()
注解处理器是注册给哪些注解使用的。
public boolean process(annotationsannotations, RoundEnvironment roundEnv)
核心方法,在这里你可以扫描和处理注解,并生成java文件。
实现的思路:
1,定义自定义的注解annotation
用来给控件添加注解。
@XXBindView(R.id.xx)
Button mBtn;
2,定义一个ViewBinder接口
用来让,我们自定义的注解处理器生成的类 来实现这个接口。
public interface XXViewBinder{
void bind(T target);
}
3,定义一个自定义processor注解处理器。
用来写一个activity的内部类,并实现ViewBinder的接口。并在bind方法里面实现控件的初始化
生成的样子类似下面
public void XXActivity$ViewBinder implements XXViewBinder{
public void bind(XXActivity target){
target.mBtn=(Button).findViewById(R.id.xx);
}
}
4,定义一个SimpleButterKnife绑定activity。
反射processor写入的内部类XXActivity$ViewBinder,并调用它的bind方法,并把activity传进去,实现初始化。
public class SimpleButterKnife{
public static void bind(Activity activity){
//反射获取XXActivity$ViewBinder内部类
//调用XXViewBinder.bind(activity)方法,把activity传进去
//这样就会调用上面写的activity内部类的bind方法,完成初始化
}
}
```
流程:跟ButterKnife一样,在Activity的里调用SimpleButterKinife.bind(activity)。
在bind()方法里,反射调用我们自定义processor注解处理器写入的内部类(XXActivity$ViewBinder)的bind()方法,并把activity传进去,来完成控件的初始化。
定义两个java library的module(不用android library)
一个是存放注解的module。(simple-butterknife-annotation)
一个是解析注解的module。(simple-butterknife-compiler)
app module 添加:
implementation project(':simple-butterknife-annotation')
annotationProcessor project(':simple-butterknife-compiler')
simple-butterknife-compiler 添加:
implementation project(':simple-butterknife-annotation')
/**
* 只处理绑定控件,只存在源码期
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface SimpleBindView {
int value();
}
public interface SimpleViewBinder {
void bind(T target);
}
@AutoService(Processor.class)
public class SimpleButterKnifeProcessor extends AbstractProcessor {
/**
* 用于生成java文件
*/
private Filer filer;
/**
* 被注解处理工具调用.
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//提供了Element,Filer,Messager等工具
filer = processingEnv.getFiler();
}
/**
* 支持的JDK版本
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 确认我们处理的注解
*/
@Override
public Set getSupportedAnnotationTypes() {
Set types = new HashSet<>();
types.add(SimpleBindView.class.getCanonicalName());
return types;
}
/**
* 核心类,在每个activity生成内部类,并初始化控件
*/
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
//获取所有使用SimpleBindView的集合
Set extends Element> elementSet = roundEnv.getElementsAnnotatedWith(SimpleBindView.class);
//key是activity的名字
//value是这个activity里面所有使用SimpleBindView注解的集合
Map> elementMap = handleElementIntoMap(elementSet);
//开始准备写XXActivity$ViewBinder内部类的java文件
Iterator iterator = elementMap.keySet().iterator();
while (iterator.hasNext()) {
String activityName = iterator.next();
//获取activity的所有元素
List variableElements = elementMap.get(activityName);
//获取包名
String packageName = getPackageName(variableElements.get(0));
//获取我们要写的内部类java文件名
String newActivityName = activityName + "$" + getInnerClassName();
//用流来写内部类的文件
createInnerClassFile(activityName, packageName, newActivityName, variableElements);
}
return false;
}
/**
* 按照格式写内部类。
* package xxx;
* import xxx.SimpleViewBinder;
* public class XXXActivity$SimpleViewBinder implements SimpleViewBinder {
* public void bind(XXXActivity target){
* //初始化控件
* target.xxx = (xxx)target.findViewById(id);
* ...
* }
* }
*/
private void createInnerClassFile(String activityName, String packageName, String newActivityName, List variableElements) {
Writer writer;
try {
String simpleActivityName = variableElements.get(0).getEnclosingElement().getSimpleName().toString()+"$"+getInnerClassName();
JavaFileObject sourceFile = filer.createSourceFile(newActivityName, variableElements.get(0).getEnclosingElement());
writer = sourceFile.openWriter();
writer.write("package " + packageName + " ;");
writer.write("\n");
writer.write("import com.liu.simple_butterknife_annotation." + getInnerClassName() + " ;");
writer.write("\n");
writer.write("public class " + simpleActivityName + " implements " + getInnerClassName() + "<" + activityName + ">{");
writer.write("\n");
writer.write("public void bind(" + activityName + " target){");
writer.write("\n");
//初始化控件
for (VariableElement variableElement : variableElements) {
SimpleBindView simpleBindView = variableElement.getAnnotation(SimpleBindView.class);
//获取控件的名字
String fileName = variableElement.getSimpleName().toString();
//获取控件的类型(Button,TextView)
TypeMirror typeMirror = variableElement.asType();
writer.write("target."+fileName+" = ("+typeMirror+")target.findViewById("+simpleBindView.value()+");");
writer.write("\n");
}
writer.write("}");
writer.write("\n");
writer.write("}");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 内部类的名字
*/
private String getInnerClassName() {
return "SimpleViewBinder";
}
/**
* 把所有的Element存放到map里
*/
private Map> handleElementIntoMap(Set extends Element> elementSet) {
Map> elementMap = new HashMap<>();
for (Element element : elementSet) {
VariableElement variableElement = (VariableElement) element;
//获取activity名字
String activityName = getActivityName(variableElement);
//通过activity名字,获取集合
List elementList = elementMap.get(activityName);
if (elementList == null) {
elementList = new ArrayList<>();
elementMap.put(activityName, elementList);
}
elementList.add(variableElement);
}
return elementMap;
}
/**
* 通过VariableElement获取包名
*/
private String getPackageName(VariableElement element) {
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
return processingEnv.getElementUtils().getPackageOf(typeElement).getQualifiedName().toString();
}
/**
* 通过VariableElement获取所在的activity名字
*/
private String getActivityName(VariableElement element) {
String packageName = getPackageName(element);
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
return packageName + "." + typeElement.getSimpleName().toString();
}
}
使用google提供的注册注解处理器
1.在compiler module 的build.gradle下添加
api 'com.google.auto.service:auto-service:1.0-rc3'
2.在compiler module 的build.gradle下添加
//防止出现“编码GBK的不可映射字符”
tasks.withType(JavaCompile){
options.encoding = "UTF-8"
}
3.在proccessor添加注解
@AutoService(Processor.class)
public class SimpleButterKnifeProcessor extends AbstractProcessor {
public class SimpleButterKnife {
public static void bind(Activity activity) {
//获取编写的内部类
String innerClassName = activity.getClass().getName() + "$SimpleViewBinder";
try {
Class> aClass = Class.forName(innerClassName);
SimpleViewBinder viewBinder = (SimpleViewBinder) aClass.newInstance();
//绑定activity
viewBinder.bind(activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
https://github.com/ecliujianbo/SimpleButterKnife