demo地址
1、簡介
註解?
說說我所知的註解吧:
標識,說到這兒可能就懵逼了,啥?
舉個例子吧,比如:@Override、SupportedOptions、SupportedSourceVersion、SuppressWarnings等等,當然需要具體瞭解可以查看更多運行時動態處理
這個大家見得就多了去了,在運行時拿到類的Class對象,然後遍歷其方法、變量,判斷有無註解聲明,然後做一些操作。這個東西性能低大多時候不建議使用,常見的框架有:xutils , afinal 等。編譯時動態處理
說道這個東西可能做Android都瞭解吧!butterknife 這個框架有木有沒有使用過的?大多數人都是用過吧,沒使用過也沒關係,建議大家去看看。這類註解會在編譯的時候,根據註解標識,動態生成一些類或者xml都可以,在運行時期,這類註解是沒有的,會依靠動態生成的類做一些操作,因爲沒有反射,效率和直接調用方法沒什麼區別~~~
2、註解
或者看看其他的對註解屬性介紹的博客,看過之後,才能更好的理解下面例子的註解
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地址