首先说说java反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为Java的反射机制。
Annotations、ButterKnife、eventBus、afinal、ThinkAndroid还有国内的xutils等
主要说明xUtils里面的ViewUtils 在BaseActivity中初始化 init 在子集的Activity中赋值
@Inject(R.id.text)TextView text;首先定义注解User类
public class User { PRivate String name; private int age; private String eat(String eat){ System.out.println("eat = "+eat); return eat+"真好吃"; }}定义注解对象
@Target(ElementType.FIELD)//用于限制当前自定义注解类作用的对象//@Retention(RetentionPolicy.SOURCE)//用于源码阶段 该注解类只会在源码出现,当将源码编译成字节码的时候注解信息就被清除//@Retention(RetentionPolicy.CLASS) //该注解类会被编译到字节码中,但是当虚拟机去加载这个字节码的时候,注解信息就被清除@Retention(RetentionPolicy.RUNTIME)//该注解类永远保留到被加载到虚拟机中public @interface ViewInject { int age(); String name();}然后将注解对象放到对应的注解类上
@ViewInject(name="张三",age = 12)private String name;这里如果将注解放到eat方法上面就会报错,因为定义的注解是FIELD类型
然后开始反射赋值,在字段上面赋值
@ViewInject(name="张三",age = 12)private String name;最后在开始通过Java反射赋值
//需求:获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User属性,将name 设置给user的name属性
User user = new User();第一步 获取user类的字节码
// user.getClass();// User.class;// Class.forName("xxxxx")Class clazz = User.class;第二步 将字节码里面的name字段获取到,其中getField的方法只能获取public方法
// clazz.getField(name) 这个方法只能获取声明为public的方法 Field declaredField = clazz.getDeclaredField("name"); Field declaredField2 = clazz.getDeclaredField("age");第三步 获取字段上面的注解对象
ViewInject annotation = declaredField.getAnnotation(ViewInject.class); if(annotation!=null){ int age = annotation.age(); String name = annotation.name(); printlns("age = "+ age + ",name = "+ name); /** * 通过反射将这个两个值设置给user对象 */ declaredField.setaccessible(true);//设置允许反射,其实就是允许暴力反射 declaredField2.setAccessible(true); declaredField.set(user,name); declaredField2.set(user,age); printlns(user.toString()); }else { printlns("字段上面没有定义注解"); } //通过反射调用eat的方法 Method declaredMethod = clazz.getDeclaredMethod("eat",String.class); declaredMethod.setAccessible(true); Object object = declaredMethod.invoke(user, "牛肉拉面"); printlns((String) object);这里注意几点:(干货)
getDeclaredField和getField区别:第一个是能获取所有字段,第二个是只能获取定义了public的字段,所有如果要获取所有的字段就要用到getDeclaredFieldsetAccessible作用:默认是false,设置是否能够获取私用字段,方法同理。命名value 和 id 区别:value是抽象方法,在使用时候不需要写,下面就会将到。首先我们新建 ViewInject注解类,ViewUtils实现类
在ViewInject类中定义注解的抽象方法名,这里说一下,定义value 和 其他名字比如 id
如果定义了id方法,在使用的时候,需要出现 id 名称
@ViewInject(id = R.id.text)TextView text;而如果注解抽象方法明是 value 的话,则不需要
@ViewInject(R.id.text)TextView text;然后再ViewUtils类里面定义 init()方法,这里需要订单形参,网上说的好多方法参数大部分使用的是Activity,因为这样是面向父类编程,但是我不这么认为,既然面向父类编程,为啥,不彻底恩,直接用Context 上下文 做为形参,当然也不是说用Activity不对。
写了这些,可以在MainActivity中先定义上
public class MainActivity extends AppCompatActivity { @ViewInject(R.id.text) TextView text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewUtils.init(this); if(text != null){ text.setText("这是成功后赋值"); } }}最后我们开始编写最核心的逻辑 ViewUtils 类
第一:先编写字段View的绑定
先获取字节码获取所有的字段遍历字段查找还有注解字段获取注解字段的值通过反射,获取类的对应方法,将值赋值给字段代码实现如下
/** * 这是绑定视图View * @param context * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */private static void bindView(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { /** * 1 获取字节码 */ Class clazz = context.getClass(); /** * 2 获取所以的字段 */ Field[] fields = clazz.getDeclaredFields(); if(fields != null && fields.length > 0){ for (int i =0 ;i<fields.length;i++){ Field field = fields[i]; /** * 3.遍历字段查找还有注解字段 */ ViewInject viewInject = field.getAnnotation(ViewInject.class); if(viewInject!=null){ /** * 4.获取注解字段的值 */ int valueId = viewInject.value(); if(valueId>0){ /** * 通过反射,获取类的对应方法,将值赋值给字段 */ Method findByIdM = clazz.getMethod("findViewById",int.class); findByIdM.setAccessible(true); Object findByIdObj = findByIdM.invoke(context,valueId); field.setAccessible(true); field.set(context,findByIdObj); } } } }}下面开始实现绑定点击事件
先获取字节码获取所有的方法遍历方法,获取方法上所有的注解遍历注解,获取注解的类型,获取注解的注解,判断是否存通注解类型获取反射注解的方法,获取所有的字段利用动态代理模式将注解的onclickListener进行代理设置,这里需要新建代理类获取注解信息,并且实例化将实例化的对象进行事件绑定代理类
public class DayHandler implements InvocationHandler{ private Map<String,Method> mtdMap; private WeakReference<Object> object; public DayHandler(Object object){ this.object = new WeakReference<Object>(object); } public void addMethod(String name,Method method){ if(mtdMap == null){ mtdMap = new HashMap<>(); } mtdMap.put(name,method); } @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { Object handler = object.get(); if(handler!=null){ String name = method.getName(); method = mtdMap.get(name); if(method!=null){ return method.invoke(handler,objects); } } return null; }}注解类
@Target(ElementType.ANNOTATION_TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface EventBus { String listenerSetter(); Class listenerType(); String listenerBackName();}这个
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@EventBus(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", listenerBackName = "onClick")public @interface OnClick { int[] value();}注解实现
private static void bindOnClick(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class<? extends Context> clazz = context.getClass(); Method[] methods = clazz.getDeclaredMethods(); //遍历所有的方法 for (Method method : methods) { Annotation[] annotations = method.getAnnotations(); //拿到方法上的所有的注解 for (Annotation annotation : annotations) { Class<? extends Annotation> annotationType = annotation.annotationType(); //拿到注解上的注解 EventBus eventBus = annotationType.getAnnotation(EventBus.class); //如果设置为EventBase if (eventBus != null) { //取出设置监听器的名称,监听器的类型,调用的方法名 String listenerSetter = eventBus .listenerSetter(); Class<?> listenerType = eventBus.listenerType(); String methodName = eventBus.listenerBackName(); try { //拿到Onclick注解中的value方法 Method aMethod = annotationType.getDeclaredMethod("value"); //取出所有的viewId int[] viewIds = (int[]) aMethod.invoke(annotation, null); //通过InvocationHandler设置代理 DayHandler handler = new DayHandler(context); handler.addMethod(methodName, method); Object listener = Proxy.newProxyInstance( listenerType.getClassLoader(), new Class<?>[] { listenerType }, handler); //遍历所有的View,设置事件 for (int viewId : viewIds) { Method findViewById = clazz.getMethod("findViewById", int.class); Object view = findViewById.invoke(context, viewId); Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType); setEventListenerMethod.invoke(view, listener); } } catch (Exception e) { e.printStackTrace(); } } } }}新闻热点
疑难解答