如何做一个简单的Android注解框架(JBInject)

反射(Reflect)

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性.

Person.java

public class Person {
    
    private String  name;
    private int     age;
                    
    private void intro() {
        System.out.println("我叫" + name + ",我今年" + age + "岁.");
    }
}

TestDemo.java


public class TestDemo {
    
    public static void main(String[] args) throws Exception {
        
        Person person = new Person();
        Class<?> cls = person.getClass();
        // 获取Person运行时的类
        
        Field nameField = cls.getDeclaredField("name");
        Field ageField = cls.getDeclaredField("age");
        // 获取name和age属性
        // cls.getDeclaredFields(); //获取所有属性
        
        nameField.setAccessible(true);
        ageField.setAccessible(true);
        // 打破name和age属性的封装性
        
        nameField.set(person, "小明");
        ageField.set(person, 19);
        // 为person对象对应的属性设置值
        
        Method method = cls.getDeclaredMethod("intro");
        // 获取intro方法
        // cls.getDeclaredMethods(); //获取所有方法
        
        method.setAccessible(true);
        // 打破intro方法的封装性
        
        method.invoke(person);
        //调用person的intro方法,
        // method.invoke(person, "arg1","arg2"...); 支持不定长参数作为方法的参数
    }
}

注解(Annotation)

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

Person.java

public class Person {
    
    private String  name;
    private int     age;
                    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "我叫" + name + ",今年" + age + "岁";
    }
    
}

PersonInject.java


package snippet;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD) // 改注解只能注解属性
                            // @Target({ElementType.FIELD,ElementType.METHOD})可指定多种类型
/**
 * TYPE(类型) FIELD(属性) METHOD(方法) PARAMETER(参数) CONSTRUCTOR(构造函数)
 * LOCAL_VARIABLE(局部变量) ANNOTATION_TYPE,PACKAGE(包),
 */

@Retention(RetentionPolicy.RUNTIME)
/**
 * SOURCE:代表的是这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译之后,Annotation的数据就会消失,
 * 并不会保留在编译好的.class文件里面。
 * 
 * ClASS:意思是这个Annotation类型的信息保留在程序源码里,同时也会保留在编译好的.class文件里面,在执行的时候,
 * 并不会把这一些信息加载到虚拟机(JVM)中去.注意一下,当你没有设定一个Annotation类型的Retention值时,系统默认值是CLASS.
 * 
 * RUNTIME:表示在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的.
 */

public @interface PersonInject {
    String name();
    
    int age();
    // 上面两个可以类比函数的形参
}


TestDemo.java


public class TestDemo {
    
    @PersonInject(name = "小明", age = 19) // 仅仅是添加注解
    Person person;
    
    public static void main(String[] args) throws Exception {
        
        // 现在才开始真正注射
        TestDemo demo = new TestDemo();
        Class<?> cls = demo.getClass();
        
        Field declaredField = cls.getDeclaredField("person");
        // 请看反射那一块
        
        PersonInject personInject = declaredField.getAnnotation(PersonInject.class);
        String name = personInject.name();
        int age = personInject.age();
        // 获取person属性的注解和它的值
        
        Person person = new Person();
        person.setName(name);
        person.setAge(age);
        
        declaredField.setAccessible(true);
        declaredField.set(demo, person);
        // 设置值
        
        System.out.println(demo.person);
    }
    
}

开始编写小型注解框架

好啦,现在我们把前面的东西用到Android里面吧.

布局文件


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="这是原来的文字" />

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="改变文字" />

    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="改变文字" />

    

</LinearLayout>

ViewInject注解Demo

ViewInject.java


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
    // 这里默认是value,如果是age的话注解是写(age=19),如果是value的话可以直接(19)
}

MainActivity.java


public class MainActivity extends Activity implements OnClickListener {
    
    @ViewInject(R.id.tv)
    private TextView    tv;
                        
    @ViewInject(R.id.btn)
    private Button      btn;
                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try {
            Class<?> cls = MainActivity.class;
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    field.setAccessible(true);
                    View view = findViewById(viewInject.value());
                    field.set(MainActivity.this, view);
                }
            }
            
            btn.setOnClickListener(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    @Override
    public void onClick(View v) {
        tv.setText("ViewInject注解成功!");
    }
    
}


OnClick注解Demo

OnClick.java


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int value();
}

MainActivity.java


package com.example.viewinjectdemo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import viewinject.OnClick;
import viewinject.ViewInject;

public class MainActivity extends Activity implements OnClickListener {
    
    @ViewInject(R.id.tv)
    private TextView    tv;
                        
    @ViewInject(R.id.btn)
    private Button      btn;
                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try {
            Class<?> cls = MainActivity.class;
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    field.setAccessible(true);
                    View view = findViewById(viewInject.value());
                    field.set(MainActivity.this, view);
                }
            }
            
            btn.setOnClickListener(this);
            
            Method[] declaredMethods = cls.getDeclaredMethods();
            for (final Method method : declaredMethods) {
                OnClick onClick = method.getAnnotation(OnClick.class);
                if (onClick != null) {
                    method.setAccessible(true);
                    View view = MainActivity.this.findViewById(onClick.value());
                    view.setOnClickListener(new OnClickListener() {
                        
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(MainActivity.this);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    
                }
                
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    @Override
    public void onClick(View v) {
        tv.setText("ViewInject注解成功!");
    }
    
    @OnClick(R.id.btn1)
    public void clickBtn() {
        tv.setText("OnClick注解成功!");
    }
    
}

封装

从前面我们可以看得到,注射方法跟属性的代码都是固定的.
因此我们可以把代码封装成一个类JBInject,只需要Activity作为参数就可以了.

JBInject.java


public class JBInject {
    private static boolean flag;
    
    public static boolean inject(final Activity activity) {
        flag = true;
        try {
            Class<?> cls = activity.getClass();
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) {
                ViewInject viewInject = field.getAnnotation(ViewInject.class);
                if (viewInject != null) {
                    field.setAccessible(true);
                    View view = activity.findViewById(viewInject.value());
                    field.set(activity, view);
                }
            }
            
            Method[] declaredMethods = cls.getDeclaredMethods();
            for (final Method method : declaredMethods) {
                OnClick onClick = method.getAnnotation(OnClick.class);
                if (onClick != null) {
                    method.setAccessible(true);
                    View view = activity.findViewById(onClick.value());
                    view.setOnClickListener(new OnClickListener() {
                        
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(activity);
                            } catch (Exception e) {
                                e.printStackTrace();
                                flag = false;
                            }
                        }
                    });
                    
                }
                
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
    
}

MainActivity.java



public class MainActivity extends Activity implements OnClickListener {
    
    @ViewInject(R.id.tv)
    private TextView    tv;
                        
    @ViewInject(R.id.btn)
    private Button      btn;
                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        JBInject.inject(MainActivity.this);
        
        btn.setOnClickListener(this);
        
    }
    
    @Override
    public void onClick(View v) {
        tv.setText("ViewInject注解成功!");
    }
    
    @OnClick(R.id.btn1)
    public void clickBtn() {
        tv.setText("OnClick注解成功!");
    }
    
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,540评论 25 708
  • 上篇我们讲述了,我们每个人都需要团队,那加入团队,我们都需要遵循什么原则呢?这篇我们来详解一下。主要通过团队管理的...
    思维浪人阅读 716评论 1 1
  • 从前有一个小女孩,她和父母去野外玩,小女孩看那些纷飞的枫叶,突出奇想:“我可以做一个枫叶书签”,于是小女孩找起...
    程旭阳阅读 295评论 1 2
  • 知识是良好的创造性思维的根本,但是死记硬背一些知识是远远不够的,这些知识必须被消化,而且最终以“鲜活的结合和崭新的...
    茗姐说阅读 387评论 0 0