注解框架的原理理解

来源:互联网 发布:linux开机启动流程 编辑:程序博客网 时间:2024/06/06 04:01

关注注解 我想都不陌生了,Android上采用注解来快速开发 我在实际工作中还没试过,这里简介记录一下我对Android注解框架的理解。

注解在Android开发中大致可以分为用在 方法 、成员、和 事件上,其他暂时不考虑。

本文参考知名博主张鸿洋的 关于IOC的描述  地址:

http://blog.csdn.net/lmj623565791/article/details/39269193

一、注解反射成员变量和 方法。编写反射@interface  xxx

引用注解,反射得到注解的值,
传递进入当前Context反射得到Method。Field 等,然后执行invoke 方法即可
这样可以通过注解反射成员变量(可以初始化控件),成员方法(加载布局文件)等。比较简单。

btn = findViewById:可能需要多个btn,因此写注解是多个的。@inject(value=id)Class<? extends Activity> clazz = activity.getClass();          Field[] fields = clazz.getDeclaredFields();          // 遍历所有成员变量          for (Field field : fields)          {                   ViewInject viewInjectAnnotation = field.getAnnotation(ViewInject.class); }setOnContentView(id) :由于一个类只有个layout布局设置,所以只需要一个annotation就行了.(@contentview(value=id))        Class<? extends Activity> clazz = activity.getClass();          ContentView contentView = clazz.getAnnotation(ContentView.class);  

以上是注解反射字段和 反射方法。


二、通过注解注入事件。

下面这样的代码btn.setOnclickListener(new View.OnclickListener(){onclick })将改为:@Onclick(value=R.id.btn1,value=R.id.btn2...)public void myClick(View v){}

1、反射得到btn
利用注解拿到id后 findviewById(id),得到Button
2、利用Proxy InvocationHandler 创建接口(View.OnclickListener)的对象,可以自定义一个类实现InvocationHandler 。
例如下面代码:

main:test:InvocationHandler i = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("方法名:"+method.getName());System.out.println((args==null)?"没有参数":"参数:"+args[0]);System.out.println(method.getReturnType().getName());System.out.println();return "test result";}};//利用Proxy创建出接口InterfaceTest的对象,Object o = Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{InterfaceTest.class}, i);//转化为InterfaceTest对象InterfaceTest interfaceTest = (InterfaceTest) o;//当调用对象的方法时,也就是接口中的方法时就会调用InvocationHandler的invoke方法。上面的i就是代理对象。String result1 = interfaceTest.printInfo1();interfaceTest.printInfo2();String result = interfaceTest.printInfo3("1");System.out.println(result);publicinterface InterfaceTest{public  String printInfo1();public  void printInfo2();public  String printInfo3(String p);}

3、反射执行setOnclickListener

总体思想是把下面这句代码,封装起来,只把控件id和自己定义的事件处理方法 提供给程序员来写。

btn.setOnclickListener(new View.OnclickListener(){onclick })@Onclick(value=R.id.btn1,value=R.id.btn2...)public void myClick(View v){}

那么可以推测到有个注解 @interface Onclick。这个@interface Onclick注解里面得完成btn.setOnclickListener(new View.OnclickListener(){onclick }) 操作。而@interface Onclick是个注解。
所以只有在这个注解上再次使用一个注解。于是乎有:

@Target(ElementType.METHOD)  @Retention(RetentionPolicy.RUNTIME)  @EventBase(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")  public @interface Onclick{//放置控件的idint[] value;}

那么在 @interface EventBase中来提供 设置listener的元素:监听类的类型(OnclickListener.class),类型名称(setOnclickListener),监听类的方法也就是事件的处理方法
(onClick),有了这三个元素就可以 在验证@interface EventBase注解时 完成btn.setOnClickListener(obj)操作.


下面是实现的代码:

/** * 注入所有的onclick事件 *  * @param activity */private static void injectEvents(Activity activity){Class<? extends Activity> clazz = activity.getClass();Method[] methods = clazz.getMethods();//遍历当前activity中的所有方法for (Method method : methods){Annotation[] annotations = method.getAnnotations();//拿到方法上的所有的注解,这里可以拿到 @interface Onclick注解for (Annotation annotation : annotations){  Class<? extends Annotation> annotationType = annotation.annotationType();//拿到@interface EventBase注解,因为这个注解上提供了反射执行btn.setOnclickListener(obj)的元素。//这个注解是@interface Onclick注解上的注解。EventBase eventBaseAnnotation = annotationType.getAnnotation(EventBase.class);//如果设置的为EventBase注解if (eventBaseAnnotation != null){//取出设置监听器的类型,监听器的名称,监听器的方法(事件回调执行方法onClick)String listenerSetter = eventBaseAnnotation.listenerSetter(); //setOnclickListenerClass<?> listenerType = eventBaseAnnotation.listenerType();//OnclickListener.classString methodName = eventBaseAnnotation.methodName();//onClicktry{//拿到Onclick注解中的value方法:拿到控件的id,以此可以得到btn控件实例Method aMethod = annotationType.getDeclaredMethod("value");//取出所有的viewId,value()方法不需要参数,所以是nullint[] viewIds = (int[]) aMethod.invoke(annotation, null);//通过Proxy InvocationHandler创建代理对象。即OnclickListener接口的对象。DynamicHandler handler = new DynamicHandler(activity);handler.addMethod(methodName, method);// 这句可以不要Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),new Class<?>[] { listenerType }, handler);//到此即可设置事件了:for (int viewId : viewIds){//遍历所有的View,创建出view ,View view = activity.findViewById(viewId);//再利用view反射出事件方法名并得到此方法的Method对象,Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);//最后利用Method对象设置事件即可.btn.setOnclickListener(obj);//setOnclickListener方法对象反射执行,参数为所属对象和方法参数。setEventListenerMethod.invoke(view, listener);}} catch (Exception e){e.printStackTrace();}}}}}

方便快速开发的 注解框架基本原理 我理解就是这个了,下面是如何使用的问题,比较简单。

@ContentView(value = R.layout.activity_main)  //在类上加上注解 ,用来设置布局文件public class MainActivity extends Activity implements OnClickListener  {      @ViewInject(R.id.id_btn)       private Button mBtn1;   //在定义控件的字段成员上加上注解,用来初始化控件。    @ViewInject(R.id.id_btn02)      private Button mBtn2;        @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);                    ViewInjectUtils.inject(this);  //验证自定义的注解,也就是一些初始化工作等。    }  }public static void inject(Activity activity)      {          injectContentView(activity);          injectViews(activity);          injectEvents(activity);      } 













0 0
原创粉丝点击