android注解的基本原理和使用

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

最后运行结果:

这里写图片描述

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

猜你喜欢

转载自blog.csdn.net/ZACH_ZHOU/article/details/74127905