现在很多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<T>{ void bind(T target); }
-
3,定义一个自定义processor注解处理器。
用来写一个activity的内部类,并实现ViewBinder的接口。并在bind方法里面实现控件的初始化
生成的样子类似下面public void XXActivity$ViewBinder implements XXViewBinder<XXActivity>{ 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();
}
创建ViewBinder(最后要构造成activity的内部类)
public interface SimpleViewBinder<T> {
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<String> getSupportedAnnotationTypes() {
Set<String> 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<String, List<VariableElement>> elementMap = handleElementIntoMap(elementSet);
//开始准备写XXActivity$ViewBinder内部类的java文件
Iterator<String> iterator = elementMap.keySet().iterator();
while (iterator.hasNext()) {
String activityName = iterator.next();
//获取activity的所有元素
List<VariableElement> 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<T extends XXXActivity> implements SimpleViewBinder<T> { * public void bind(XXXActivity target){ * //初始化控件 * target.xxx = (xxx)target.findViewById(id); * ... * } * } */
private void createInnerClassFile(String activityName, String packageName, String newActivityName, List<VariableElement> 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<String, List<VariableElement>> handleElementIntoMap(Set<? extends Element> elementSet) {
Map<String, List<VariableElement>> elementMap = new HashMap<>();
for (Element element : elementSet) {
VariableElement variableElement = (VariableElement) element;
//获取activity名字
String activityName = getActivityName(variableElement);
//通过activity名字,获取集合
List<VariableElement> 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 {
创建类来绑定activity
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();
}
}
}