注解是一种很优雅的书写方式,也是我们的代码变的简洁,更加快捷方便编写代码。下面以绑定onclick实践为例阐述注解的原理。
1、定义Onclick注解类
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @BaseEvent(listenerSetter = "setOnclickListener", listenerType = View.OnClickListener.class, methodName = "onclick") public @interface OnClick { int[] values(); }
BaseEvent用来制定需要绑定的方法名,事件的Listener,和绑定执行的方法名,BaseEvent的代码如下
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface BaseEvent { Class<?> listenerType(); String listenerSetter(); String methodName(); }
2、自定义的注解使用方式
@OnClick(R.id.download_btn) public void testUpload(View view) {}
3、注解的解析(重点):
思路:可以通过我们的注解对象中取得添加onclick注解的方法(testUpload)反射方法对象,对这个对象进行事件的绑定。动态代理View事件中的OnclickLister,向代理对象中添加方法对象(testUpload)。方法的添加在EventListenerManager管理类中完成。
EventLisernerManager中定义动态代理类:
public static void addEventMethod(Method method, Annotation annotation, Object value, Object handler, ViewFinder finder){ try{ View view = finder.findViewByInfo(value); if(view!=null){ BaseEvent baseEvent = annotation.annotationType().getAnnotation(BaseEvent.class); Class<?> listenerType = baseEvent.listenerType(); String listenerSetter = baseEvent.listenerSetter(); String methodName = baseEvent.methodName(); Object listener = listenerCache.get(value, listenerType); boolean addMethod = false; DynamicHandler dymicHandler = null; if(listener!=null){ dymicHandler = (DynamicHandler) Proxy.getInvocationHandler(listener); addMethod = (handler.equals(dymicHandler.getHandler())); if(addMethod){ dymicHandler.addMethod(methodName, method); } } if(!addMethod){ dymicHandler = new DynamicHandler(handler); dymicHandler.addMethod(methodName, method); listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[]{listenerType},dymicHandler); listenerCache.put(value, listenerType, listener); } //setListener Method setterMethod = view.getClass().getMethod(listenerSetter, listenerType); setterMethod.invoke(view, listener); } }catch(Exception e){ LogUtils.e(e.getMessage(),e); } }listener = Proxy.newProxyInstance(listenerType.getClassLoader(),new Class<?>[]{listenerType},dymicHandler);动态的代理了onClickListener。类中对代理对象进行的缓存:
//缓存代理对象 public final static DoubleKeyValueMap<Object,Class<?>, Object> listenerCache = null;
解析注解添加方法:
Method[] methods = handlerType.getDeclaredMethods(); if(methods.length>0){ for(Method method:methods){ Annotation[] annotations = method.getDeclaredAnnotations(); for(Annotation annotation : annotations){ Class<?> anType = annotation.annotationType();//拿到Annotation的Class if(anType.getAnnotation(BaseEvent.class)!=null){ method.setAccessible(true); try{ Method valueMethod = anType.getDeclaredMethod("values"); Object values = valueMethod.invoke(annotation); int len = Array.getLength(values); if(len>0){ for(int i=0;i<len;i++){ Object value = Array.get(values, i); EventListenerManager.addEventMethod(method, annotation, value, handlerType, finder); } } }catch(Exception e){ e.printStackTrace(); } } } } }这里可以拿到自定义method和注解本身。不太了解的可以研究下java的annotation的用法。
由以上的方法的可以实现注解事件的绑定,可以自己定义事件的注解,优雅的开始你的代码吧!