注解是一种很优雅的书写方式,也是我们的代码变的简洁,更加快捷方便编写代码。下面以绑定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的用法。
由以上的方法的可以实现注解事件的绑定,可以自己定义事件的注解,优雅的开始你的代码吧!