在android的开发中注解使用是非常常见的,注解可以使代码阅读更加的清晰,整洁,可读性大大增强。但是如果不知道注解的原理,那么用起来也不会得心应手。通过本篇的学习,你可以掌握注解的基本原理,自己搭建注解框架。
注解的原理: 使用interface来用作标记,@Target来用作描述类型(包括类、成员变量、方法等)@Retention来描述生命周期。其内部是通过类的反射机制,调用指定对象的方法,从而达到与对象直接调用方法相同的效果。
类的注解:
注解的基本使用格式之前就介绍过,下面我们就通过注解来实现setContentView()方法。来为acitivity填充布局。
@Target(ElementType.TYPE) //用于描述类
@Retention(RetentionPolicy.RUNTIME) //运行时注解
public @interface ContentView {
int value(); //注解的值
}
这个就是我们要添加注解的activity。
//指定注解的类
@ContentView(R.layout.activity_annotation_aty2)
public class AnnotationAty extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.InjectContentView(this);
}
}
此方法是InjectUtils类中的方法(自定义的工具类),这个方法就是通过反射机制调用activity的setContentView()方法了,此方法要添加到activity的oncreate中。
public static void InjectContentView(Activity activity) {
//获取activity对应的class
Class a = activity.getClass();
//判断当前class是否有ContentView的注解
if(a.isAnnotationPresent(ContentView.class)){
//获取注解实例
ContentView contentView = (ContentView) a.getAnnotation(ContentView.class);
//获取注解中的值
int layoutIt = contentView.value();
try {
//获取class的方法,第一个参数是方法名,第二个是方法参数的类型
Method method = a.getMethod("setContentView",int.class);
method.setAccessible(true);
//调用指定对象的此方法,第二个是方法的参数
method.invoke(activity,layoutIt);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
运行结果:
实在是爽的不要不要的了,已经成功了一小步,我们就通过注解完成了setContentView()的调用了。下面我们依次来看View的注解与事件的注解。
成员变量的注解: 成员变量的注解与类的注解格式一样,稍微不同的就是在代码的实现上有那么一丢丢的差异。
@Target(ElementType.FIELD) //描述类型为成员变量
@Retention(RetentionPolicy.RUNTIME)
public @interface FindView {
int value();
}
acitivity中我们添加注解,然后通过注解找到我们的view,最后改变view的值。
@ContentView(R.layout.activity_annotation_aty2)
public class AnnotationAty extends Activity {
@FindView(R.id.tv_annotataion)
public TextView tv_annotation;
@FindView(R.id.tv_annotataion2)
public TextView tv_annotation2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.injectContentView(this);
InjectUtils.injectView(this);
tv_annotation.setText("hello");
tv_annotation2.setText("world");
}
}
InjectUtils类中的injectView()用来反射调用findviewById()方法。
public static void injectView(Activity activity){
Class c = activity.getClass();
//获取所有的public成员变量
Field[] fields = c.getFields();
//包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段
// Field[] fields = c.getDeclaredFields();
for(Field field:fields){
if(field.isAnnotationPresent(FindView.class)){
FindView findView = field.getAnnotation(FindView.class);
int id = findView.value();
try {
Method method = c.getMethod("findViewById",int.class);
method.setAccessible(true);
//获取到view
Object view= method.invoke(activity, id);
field.setAccessible(true);
//将获取的view赋值给指定的成员变量field
field.set(activity,view);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
运行结果:
事件的注解: 事件的注解用到了java中的动态代理模式,这个要是不了解可以自行百度一下。
//描述方法的注解类型
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Onclick {
int [] value();
}
注解的activity,通过添加事件注解,点击TextView的时候,弹出TextView中的值。
@ContentView(R.layout.activity_annotation_aty2)
public class AnnotationAty extends Activity {
@FindView(R.id.tv_annotataion)
public TextView tv_annotation;
@FindView(R.id.tv_annotataion2)
public TextView tv_annotation2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.injectContentView(this);
InjectUtils.injectView(this);
InjectUtils.injectEvent(this);
tv_annotation.setText("hello");
tv_annotation2.setText("world");
}
//注解的方法
@Onclick({R.id.tv_annotataion,R.id.tv_annotataion2})
public void click(View view){
//弹出TextView中的值
T.showShort(this,((TextView)view).getText());
}
}
InjectUtils类中的injectEvent()用来反射调用view的setOnClickListener事件,一下我们没有使用动态代理来实现,代码阅读起来也很多简单,就是先通过反射获取view然后在为view的class反射调用setOnclickListener事件,然后在onClick()方法中调用我们自定义的方法。
public static void injectEvent(final Activity activity){
Class c = activity.getClass();
Method methods [] = c.getDeclaredMethods();
for(final Method method :methods){
if(method.isAnnotationPresent(Onclick.class)){
Onclick onclick = method.getAnnotation(Onclick.class);
int[] ids = onclick.value();
for(int id :ids){
try {
Method findViewBiId = c.getMethod("findViewById",int.class);
//根据id获取view
findViewBiId.setAccessible(true);
View view = (View) findViewBiId.invoke(activity,id);
//获取setOnClickListener方法实例
Method click = view.getClass().getMethod("setOnClickListener",View.OnClickListener.class);
click.setAccessible(true);
//调用view的setOnClickListener方法
click.invoke(view, new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//回调自定义的方法
method.setAccessible(true);
method.invoke(activity,v);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
使用动态代理,动态代理主要是在第三步不同,那么我们直接改变第三步的操作。
public static void injectEventByProxy(final Activity activity){
Class c = activity.getClass();
Method methods [] = c.getDeclaredMethods();
for(final Method method :methods){
if(method.isAnnotationPresent(Onclick.class)){
Onclick onclick = method.getAnnotation(Onclick.class);
int[] ids = onclick.value();
for(int id :ids){
try {
Method findViewBiId = c.getMethod("findViewById",int.class);
//根据id获取view
findViewBiId.setAccessible(true);
View view = (View) findViewBiId.invoke(activity,id);
//添加动态代理
MyDynamicProxy dynamicProxy = new MyDynamicProxy(activity,method);
//生成listener对象
Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader()
,new Class[]{View.OnClickListener.class},dynamicProxy);
//获取setOnClickListener方法实例
Method click = view.getClass().getMethod("setOnClickListener",View.OnClickListener.class);
click.setAccessible(true);
//调用view的setOnClickListener方法
click.invoke(view,listener);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
代理类要实现InvocationHandler接口,实际上最后会回调到MyDynamicProxy的invoke方法中。
class MyDynamicProxy implements InvocationHandler{
private Activity activity;
private Method targetMethod;
public MyDynamicProxy(Activity activity,Method method){
this.activity = activity;
targetMethod = method;
}
/**
*
* 实际执行的是targetMethod的方法
* @param proxy 代理对象
* @param method 代理的方法
* @param args 方法中的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return targetMethod.invoke(activity,args);
}
}
最后运行结果:
哈哈哈,成功了。有什么问题,欢迎指导!