demo地址
1、简介
注解?
说说我所知的注解吧:
标识,说到这儿可能就懵逼了,啥?
举个例子吧,比如:@Override、SupportedOptions、SupportedSourceVersion、SuppressWarnings等等,当然需要具体了解可以查看更多运行时动态处理
这个大家见得就多了去了,在运行时拿到类的Class对象,然后遍历其方法、变量,判断有无注解声明,然后做一些操作。这个东西性能低大多时候不建议使用,常见的框架有:xutils , afinal 等。编译时动态处理
说道这个东西可能做Android都了解吧!butterknife 这个框架有木有没有使用过的?大多数人都是用过吧,没使用过也没关系,建议大家去看看。这类注解会在编译的时候,根据注解标识,动态生成一些类或者xml都可以,在运行时期,这类注解是没有的,会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别~~~
2、注解
http://blog.youkuaiyun.com/ty_phone8/article/details/77718519
或者看看其他的对注解属性介绍的博客,看过之后,才能更好的理解下面例子的注解
3、例子
那我们就说一下过程:
前期准备:我们先看一看项目结构吧
可以看出有两个 java的jar 和一个Android的jar
首先呢,我们需要在整个项目的build文件中添加一句话:
classpath group: 'com.neenbedankt.gradle.plugins', name: 'android-apt', version: '1.8'
接下来呢,我们也需要在app项目的build文件中添加一句话:
apply plugin: 'com.neenbedankt.android-apt'
接下来,我们就按照inject-annotation、inject-apt、inject-api的顺序创建jar包,当然不必在意jar包的名字,咱们想怎么命名就怎么命名。
inject-annotation 主要用来自定义注解
我这里呢准备了两个注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value() default 0;
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnClick {
int[] value() default {};
}
inject-apt 主要在编译时获取您的注解并且进行一些操作
我这里呢,定义的处理器如下:
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AgentProcessor extends AbstractProcessor {
...
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
...
return false;
}
}
这儿直接使用了
compile 'com.google.auto.service:auto-service:1.0-rc3'
这个开元jar包来去除,建立复杂的 META-INF/services/javax.annotation.processing.Processor文件
对注解字段或者方法的操作:
//Gets all the elements of the "BindView" annotation
Set<? extends Element> bindViewElement = roundEnv.getElementsAnnotatedWith(BindView.class);
for (Element element : bindViewElement) {
//Because the "BindView" annotation is a field, the strong is converted to the "VariableElement" type
VariableElement variableElement = (VariableElement) element;
//Provides access to information about types and their members
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
String qualifiedName = typeElement.getQualifiedName().toString();
ProxyEntity entity = proxyEetityMap.get(qualifiedName);
if (null == entity) {
entity = new ProxyEntity(elementUtils, typeElement);
proxyEetityMap.put(qualifiedName, entity);
}
BindView bindView = element.getAnnotation(BindView.class);
int value = bindView.value();
entity.setVariableElementMapValue(value, variableElement);
}
//Gets all the elements of the "OnClick" annotation
Set<? extends Element> onClickElement = roundEnv.getElementsAnnotatedWith(OnClick.class);
for (Element element : onClickElement) {
//Because the "OnClick" annotation is a method, the strong is converted to the "ExecutableElement" type
ExecutableElement executableElement = (ExecutableElement) element;
//Provides access to information about types and their members
TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();
String qualifiedName = typeElement.getQualifiedName().toString();
ProxyEntity entity = proxyEetityMap.get(qualifiedName);
if (null == entity) {
entity = new ProxyEntity(elementUtils, typeElement);
proxyEetityMap.put(qualifiedName, entity);
}
OnClick onClick = element.getAnnotation(OnClick.class);
int[] values = onClick.value();
for (int value : values) {
entity.setExecutableElementMapValue(value, executableElement);
}
}
这里呢,我建立了一个实体类来实现我需要进行的操作:
/*
在这里我使用了 javapoet 来生成我需要的java文件
有需要可以区简单的了解一下 javapoet 的用法
*/
public class ProxyEntity {
public static final String PROXY = "ViewBinding";
private String qualifiedName;//Full name of the class
private String packageName;//The package name
private String className;//taxon
private String proxyClassName;//The proxy class name
public ProxyEntity(Elements elementUtils, TypeElement typeElement) {
qualifiedName = typeElement.getQualifiedName().toString();
PackageElement packageElement = elementUtils.getPackageOf(typeElement);
packageName = packageElement.getQualifiedName().toString();
className = qualifiedName.substring(packageName.length() + 1).replace(".", "_");
proxyClassName = className + "_" + PROXY;
}
public JavaFile GenerateProxyClasses() {
...
return javaFile;
}
private Map<Integer, VariableElement> variableElementMap;
private Map<Integer, ExecutableElement> executableElementMap;
public void setVariableElementMapValue(Integer key, VariableElement value) {
if (null == variableElementMap) {
variableElementMap = new LinkedHashMap<>();
}
variableElementMap.put(key, value);
}
public void setExecutableElementMapValue(Integer key, ExecutableElement value) {
if (null == executableElementMap) {
executableElementMap = new LinkedHashMap<>();
}
executableElementMap.put(key, value);
}
}
inject-api 在运行时的时候调用
这里建立了一个接口,和一个获取代理类的类
代理类需要实现的接口:
public interface ViewBinding<T> {
void bind(T t, Object source);
void unbind();
}
获取代理类的类
public class AgentInject {
private static final String SUFFIX = "_ViewBinding";
public static void injectView(Activity activity) {
ViewBinding proxyActivity = findProxyActivity(activity);
View view = activity.getWindow().getDecorView();
proxyActivity.bind(activity, view);
}
private static ViewBinding findProxyActivity(Object activity) {
try {
Class clazz = activity.getClass();
Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);
return (ViewBinding) injectorClazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
throw new RuntimeException(String.format("can not find %s , something when compiler.", activity.getClass().getSimpleName() + SUFFIX));
}
}
在app中调用
public class MainActivity extends AppCompatActivity {
@BindView(R.id.text1)
TextView text1;
@BindView(R.id.text2)
Button text2;
@BindView(R.id.text3)
TextView text3;
@BindView(R.id.text4)
Button text4;
@BindView(R.id.text5)
TextView text5;
@BindView(R.id.text6)
Button text6;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AgentInject.injectView(this);
text1.setText("This is my first TextView.");
text2.setText("This is my first Button.");
text3.setText("This is my second TextView.");
text4.setText("This is my second Button.");
text5.setText("This is my third TextView.");
text6.setText("This is my third Button.");
}
private static final String TAG = "MainActivity";
@OnClick({R.id.text1, R.id.text3, R.id.text5})
public void TestOnClickTxt(View v) {
switch (v.getId()) {
case R.id.text1:
Log.d(TAG, "TestOnClickTxt: 1 - " + v);
break;
case R.id.text3:
Log.d(TAG, "TestOnClickTxt: 2 - " + v);
break;
case R.id.text5:
Log.d(TAG, "TestOnClickTxt: 3 - " + v);
break;
}
}
@OnClick({R.id.text2, R.id.text4, R.id.text6})
public void TestOnClickBtn(View v) {
switch (v.getId()) {
case R.id.text2:
Log.d(TAG, "TestOnClickTxt: 1 - " + v);
break;
case R.id.text4:
Log.d(TAG, "TestOnClickTxt: 2 - " + v);
break;
case R.id.text6:
Log.d(TAG, "TestOnClickTxt: 3 - " + v);
break;
}
}
}
demo地址