android注解的基本原理和使用

您所在的位置:网站首页 android基本原理 android注解的基本原理和使用

android注解的基本原理和使用

2024-07-17 04:08| 来源: 网络整理| 查看: 265

在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); } }

最后运行结果:

这里写图片描述

哈哈哈,成功了。有什么问题,欢迎指导!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3