注解可分为编译期注解和运行时注解
依赖注入
XUtils IOC (运行时注解,影响效率)
ButterKnife IOC(编译期注解)
本质:在编译时访问Java类,然后生成辅助类
JRE APT
Mirror 访问Java类
1.如何通过Activity的辅助类给Activity的控件属性赋值的?
->JasonKnife.inject中通过外部类Activity获取辅助类(Activity的内部类MainActivity$InjectAdapter)
->调用辅助类(MainActivity$InjectAdapter)的injects方法,完成注入
2.如何生成辅助类的?(重点)
->继承AbstractProcessor(在编译时期,可以处理被编译的Java类)
->带有我们指定注解的Java类,会经过我们的处理
->收集我们有用的信息,去生成辅助类
AbstractProcessor为处理注解,提供一个父类
生成辅助类
->获取到所有相关的注解信息
->生成辅助类
环境配置
1.jre
2.指定APT插件所需要的Processor,编译期运行
resources\META-INF\services\javax.annotation.processing.Processor
3.打成jar包
4.Java Compiler->Annotation Processor
反射:在运行时期,访问类的成员
Mirror:在编译期访问Java类文件的各个成员
环境配置中的1到3步是框架项目需要做的
步骤4是在引用项目中要做的
详细步骤如下:
1.jre步骤
2.指定APT插件所需要的Processor,编译期运行
项目中新建目录resources\META-INF\services,其中resources和src目录同级
在目录中新建文件javax.annotation.processing.Processor,在该文件里指定相关Processor,并且可以指定多个processor,以换行符分隔
com.jason.jasonknife.apt.ViewInjectorProcessor
3.打成jar包
把生成的jar包引入到需要使用的项目里并做如下配置
4.Java Compiler->Annotation Processor
点击advanced按钮,如果发现以下图片,则说明配置成功,分别点击应用和确定来结束配置
===============================================================
编译时注解
APT Annotation Processing Tools 注解处理工具
JDK1.5 之后自带的工具
APT是一个命令行工具(Eclipse自带APT插件)
配置:告诉Eclipse在编译我们的代码时,使用APT工具,对工程中的注解进行处理
怎么处理的?
通过Mirror API(com.sun.mirror.*)描述程序语义结构
通过Mirror API可以获取被注解的Java类型元素信息,交给了Processor的子类
通过Mirror API在编译期访问Java类,它把Java类分为这些部分:
PackageElement 包元素
TypeElement 类型元素
ExecutableElement 可执行元素
VariableElement 变量元素
TypeParameterElement 类型参数元素
通过ElementVisitor访问Java类的这些元素
JDK关于Mirror API的底层实现,并没有开源
代码如下:
----------------------------------------------------------------------------------
类库项目:
javax.annotation.processing.Processor
com.jason.jasonknife.apt.ViewInjectorProcessor
com.jason.jasonknife包:
package com.jason.jasonknife;
import java.util.HashMap;
import java.util.Map;
import com.jason.jasonknife.adapter.InjectAdapter;
import com.jason.jasonknife.adapter.NullAdapter;
import android.app.Activity;
public class JasonKnife {
//缓存,宿主类与辅助类对象的关系
static Map<Class<?>, InjectAdapter<?>> mInjectCache = new HashMap<Class<?>, InjectAdapter<?>>();
/**
* 辅助类类名
*/
public static String SUFFIX = "$InjectAdapter";
public static void inject(Activity target){
//获取Activity中的内部类(辅助类)
InjectAdapter<Activity> adapter = getViewAdapter(target.getClass());
adapter.injects(target);
}
/**
* 获取一个指定类的InjectAdapter内部类
* @param clazz
* @return
*/
private static <T> InjectAdapter<T> getViewAdapter(Class<?> clazz) {
//先从缓存获取
InjectAdapter<T> adapter = (InjectAdapter<T>)mInjectCache.get(clazz);
if(adapter != null){
return adapter;
}
//使用反射,通过内部类的Class实例化出该内部类的对象
//com.jason.jasonknife.demo.MainActivity$InjectAdapter
String adapterClassName = clazz.getName() + SUFFIX;
try {
//1.通过该内部类的完整类名,加载出一个class
Class<?> adapterClass = Class.forName(adapterClassName);
//2.通过Class实例化对象
adapter = (InjectAdapter<T>)adapterClass.newInstance();
//放入缓存
mInjectCache.put(clazz, adapter);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//Null Object Pattern 空对象设计模式
return adapter == null ? new NullAdapter() : adapter;
}
}
com.jason.jasonknife.adapter包:
package com.jason.jasonknife.adapter;
/**
* 辅助类的父接口
* @date 2016年3月23日
* @version 1.0
*/
public interface InjectAdapter<T> {
void injects(T target);
}
package com.jason.jasonknife.adapter;
public class NullAdapter implements InjectAdapter {
@Override
public void injects(Object target) {
}
}
com.jason.jasonknife.anno包:
package com.jason.jasonknife.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解存在于编译期
@Retention(RetentionPolicy.CLASS)
//用于属性上
@Target(ElementType.FIELD)
public @interface ViewInjector {
int value();
}
com.jason.jasonknife.anno.handler包:
package com.jason.jasonknife.anno.handler;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.VariableElement;
/**
* 注解处理器
* @date 2016年3月21日
* @version 1.0
*/
public interface AnnotationHandler {
/**
* 关联“处理环境”
* @param env
*/
void attachProcessingEnvironment(ProcessingEnvironment env);
/**
* 处理注解
* @param env “周边环境”
* @return Map<key 注解的宿主类名, value 宿主类中使用了我们注解的属性集合>
*/
Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment env);
}
package com.jason.jasonknife.anno.handler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import com.jason.jasonknife.anno.ViewInjector;
import com.jason.jasonknife.utils.AnnotationUtils;
import com.jason.jasonknife.utils.FileUtils;
/**
* ViewInjector注解的处理实现
* @date 2016年3月21日
* @version 1.0
*/
public class ViewInjectHandler implements AnnotationHandler {
private ProcessingEnvironment processingEnv;
@Override
public void attachProcessingEnvironment(ProcessingEnvironment env) {
this.processingEnv = env;
}
@Override
public Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment env) {
Map<String, List<VariableElement>> annotationMap = new HashMap<String, List<VariableElement>>();
//获取所有使用了ViewInjector注解的元素(属性)
Set<? extends Element> elementSet = env.getElementsAnnotatedWith(ViewInjector.class);
//搞清楚哪个属性属于哪一个类
for (Element element : elementSet) {
//VariableElement 代表属性、枚举等等
VariableElement varElement = (VariableElement)element;
//获取属性宿主类的完成类名
String className = getParentClassName(varElement);
//key->value
List<VariableElement> cacheElements = annotationMap.get(className);
if(cacheElements == null){
cacheElements = new ArrayList<VariableElement>();
annotationMap.put(className, cacheElements);
}
cacheElements.add(varElement);
}
//-----------------for test
//FileUtils.output(annotationMap.toString());
return annotationMap;
}
/**
* 获取属性宿主类的完成类名
* @param varElement
* @return
*/
private String getParentClassName(VariableElement varElement) {
TypeElement typeElement = (TypeElement)varElement.getEnclosingElement();
String packageName = AnnotationUtils.getPackageName(processingEnv, typeElement);
//完整类名
return packageName + "." + typeElement.getSimpleName().toString();
}
}
com.jason.jasonknife.apt包:
package com.jason.jasonknife.apt;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import com.jason.jasonknife.anno.handler.AnnotationHandler;
import com.jason.jasonknife.anno.handler.ViewInjectHandler;
import com.jason.jasonknife.writer.AbstractWriter;
import com.jason.jasonknife.writer.DefaultJavaFileWriter;
//使用了ViewInjector的注解的Java类,需要处理
@SupportedAnnotationTypes("com.jason.jasonknife.anno.ViewInjector")
//支持的Java版本
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ViewInjectorProcessor extends AbstractProcessor{
List<AnnotationHandler> mHandlers = new ArrayList<AnnotationHandler>();
AbstractWriter mWriter;
Map<String, List<VariableElement>> map = new HashMap<String, List<VariableElement>>();
//初始化工作
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//初始化注解处理器
registerHandler(new ViewInjectHandler());
//初始化辅助类生成器
mWriter = new DefaultJavaFileWriter(processingEnv);
}
/**
* 注册处理器
* @param handler
*/
protected void registerHandler(AnnotationHandler handler){
mHandlers.add(handler);
}
/**
* 在编译时期,会把指定的Java类传入进行处理(process)
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//怎么处理?
//1.拿到注解信息(暂时只有处理注入视图的注解,将来还有可能要处理注入布局、事件等等)
for (AnnotationHandler handler : mHandlers) {
//关联环境
handler.attachProcessingEnvironment(processingEnv);
//处理注解
map.putAll(handler.handleAnnotation(roundEnv));
}
//2.生成Java文件
mWriter.generate(map);
return true;
}
}
com.jason.jasonknife.utils包:
package com.jason.jasonknife.utils;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
public class AnnotationUtils {
/**
* 获取包名
* @param env
* @param element
* @return
*/
public static String getPackageName(ProcessingEnvironment env,Element element){
return env.getElementUtils().getPackageOf(element).getQualifiedName().toString();
}
}
package com.jason.jasonknife.utils;
import java.io.BufferedWriter;
import java.io.FileWriter;
public class FileUtils {
public static void output(String info){
if(info == null){
return;
}
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("d://jason.txt",true));
writer.newLine();
writer.append(info);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.jason.jasonknife.utils;
import java.io.Closeable;
import java.io.IOException;
public class IOUtil {
public static void closeQuietly(Closeable closeable){
try {
if(closeable != null){
closeable.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.jason.jasonknife.utils;
import android.app.Activity;
import android.view.View;
public class ViewFinder {
public static <T extends View> T findViewById(Activity activity, int id){
return (T)activity.findViewById(id);
}
}
com.jason.jasonknife.writer包:
package com.jason.jasonknife.writer;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;
import com.jason.jasonknife.JasonKnife;
import com.jason.jasonknife.utils.AnnotationUtils;
import com.jason.jasonknife.utils.FileUtils;
import com.jason.jasonknife.utils.IOUtil;
/**
* 辅助类生成器的模板方法
* @date 2016年3月21日
* @version 1.0
*/
public abstract class AbstractWriter implements AdapterWriter {
protected ProcessingEnvironment mProcessingEnv;
//用于创建源文件
protected Filer mFiler;
public AbstractWriter(ProcessingEnvironment mProcessingEnv) {
super();
this.mProcessingEnv = mProcessingEnv;
this.mFiler = mProcessingEnv.getFiler();
FileUtils.output("AbstractWriter 初始化");
}
/**
* 模板方法
*/
@Override
public void generate(Map<String, List<VariableElement>> map) {
Iterator<Entry<String, List<VariableElement>>> iterator = map.entrySet().iterator();
FileUtils.output("generate:"+map);
while(iterator.hasNext()){
Entry<String, List<VariableElement>> entry = iterator.next();
//属性集合
List<VariableElement> cacheElements = entry.getValue();
if(cacheElements == null || cacheElements.size() == 0){
continue;
}
//创建Java源文件
//拿集合中的第0个属性元素出来,构建一个InjectInfo
InjectInfo info = createInjectInfo(cacheElements.get(0));
Writer writer = null;
try {
JavaFileObject javaFileObject = mFiler.createSourceFile(info.getClassFullName());
//通过Writer编写Java代码
writer = javaFileObject.openWriter();
//头部
FileUtils.output("generateImport");
generateImport(writer, info);
//属性赋值部分
for (VariableElement variableElement : cacheElements) {
writeField(writer, variableElement, info);
}
FileUtils.output("writeField");
//尾部
writeEnd(writer);
FileUtils.output("writeEnd");
} catch (IOException e) {
e.printStackTrace();
} finally{
IOUtil.closeQuietly(writer);
}
}
}
private InjectInfo createInjectInfo(VariableElement element){
//获取变量的宿主(类-类型元素),转为TypeElement
TypeElement typeElement = (TypeElement)element.getEnclosingElement();
String packageName = AnnotationUtils.getPackageName(mProcessingEnv, element);
String className = typeElement.getSimpleName().toString();
return new InjectInfo(packageName, className);
}
/**
* 产生头部
* @param writer
* @param info
*/
protected abstract void generateImport(Writer writer, InjectInfo info)throws IOException;
/**
* 产生为属性赋值的代码
* @param writer
* @param element
* @param info
*/
protected abstract void writeField(Writer writer, VariableElement element, InjectInfo info)throws IOException;
/**
* 产生结尾部分
* @param writer
*/
protected abstract void writeEnd(Writer writer)throws IOException;
/**
* 注入信息
* @author Jason
* QQ: 1476949583
* @date 2016年3月23日
* @version 1.0
*/
class InjectInfo{
//宿主类的包名
public String packageName;
//宿主类的类名
public String className;
//要创建的辅助类的类名
public String newClassName;
public InjectInfo(String packageName, String className) {
super();
this.packageName = packageName;
this.className = className;
this.newClassName = className + JasonKnife.SUFFIX;
}
/**
* 获取辅助类的完整类名
* @return
*/
public String getClassFullName(){
return this.packageName + "." + this.newClassName;
}
}
}
ackage com.jason.jasonknife.writer;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.VariableElement;
/*
* 辅助类生成器
*/
public interface AdapterWriter {
/**
* 生成辅助类
* @param map
*/
void generate(Map<String, List<VariableElement>> map);
}
package com.jason.jasonknife.writer;
import java.io.IOException;
import java.io.Writer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.VariableElement;
import com.jason.jasonknife.anno.ViewInjector;
/**
* 默认的Java文件生成器
* @date 2016年3月21日
* @version 1.0
*/
public class DefaultJavaFileWriter extends AbstractWriter {
public DefaultJavaFileWriter(ProcessingEnvironment mProcessingEnv) {
super(mProcessingEnv);
}
@Override
protected void generateImport(Writer writer, InjectInfo info) throws IOException{
writer.write("package "+info.packageName +";");
writer.write("\n\n");
writer.write("import com.jason.jasonknife.adapter.InjectAdapter;");
writer.write("\n");
writer.write("import com.jason.jasonknife.utils.ViewFinder;");
writer.write("\n\n\n");
writer.write("/*This class is generated by JasonKnife, do not modify!*/");
writer.write("\n");
writer.write("public class "+info.newClassName+" implements InjectAdapter<"+info.className+"> { ");
writer.write("\n");
writer.write("\n");
writer.write("public void injects("+info.className+" target) {");
writer.write("\n");
}
@Override
protected void writeField(Writer writer, VariableElement element, InjectInfo info)throws IOException {
//获取ViewInjector注解的值
ViewInjector viewInjector = element.getAnnotation(ViewInjector.class);
String fieldName = element.getSimpleName().toString();
int id = viewInjector.value();
writer.write("target."+fieldName+" = ViewFinder.findViewById(target, "+id+");");
writer.write("\n");
}
@Override
protected void writeEnd(Writer writer)throws IOException {
writer.write(" }");
writer.write("\n\n");
writer.write(" }");
}
}
使用案例项目:
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文字游戏" />
<Button
android:id="@+id/btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="按钮" />
</LinearLayout>
MainActivity:
package com.jason.jasonknife.demo;
import com.jason.jasonknife.JasonKnife;
import com.jason.jasonknife.anno.ViewInjector;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
@ViewInjector(R.id.btn)
protected Button btn;
@ViewInjector(R.id.text)
protected TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JasonKnife.inject(this);
Log.d("jason", btn.getText().toString());
Log.d("jason", text.getText().toString());
}
}
运行结果如下:
整理自教程