1.定义EventsBase注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE) //使用在注解上
@Retention(RetentionPolicy.RUNTIME)// 运行时
// 定义一个事件的基类注解,onClick onLongClick都可以基于此注解
public @interface EventsBase {
//方法名:setOnClickListener
String listenerSetter();
//监听的类型:View.OnClickListener.class
Class<?> listenerType();
// 回调方法名:onClick
String listenerCallback();
}
2.定义onClick注解
import android.view.View;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
// 第一步定义的base注解
// 名字千万不能打错,最好直接cv,因为是通过aop去切面拦截,名字错了就GG
@EventsBase(listenerSetter = "setOnClickListener", // 需要设置的方法名
listenerType = View.OnClickListener.class, // 具体监听的listener类型
listenerCallback = "onClick") // 需要替换掉的回调名
public @interface OnClick {
int[] values(); // 因为有可能多个id,所以定义成数组
}
3.定义InvocationHandler实现类
动态代理传送门: blog.csdn.net/yaomingyang…
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
public class ListenerInvocationHandler implements InvocationHandler {
//需要拦截的目标 这里是LoginActivity
private Object target;
//缓存需要 String:替换的方法名字 Method:要替换的方法
private HashMap<String, Method> map;
//构造函数
public ListenerInvocationHandler(Object target) {
this.map = new HashMap<>();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
try {
if (target != null) {
//获取到当前方法的名字,这里获取到的是onClick
String methodName = method.getName();
//通过这个名字去map里面取method
//这里取出来的就是我们自定义的login()方法
method = map.get(methodName);
//判空
if (method != null) {
//打开private权限
method.setAccessible(true);
//获取自定义的login方法有几个参数
int getParameterCount = method.getGenericParameterTypes().length;
//
if (getParameterCount > 0) {
//有参数返回args
return method.invoke(target, args);
} else {
//没参数不返回args
return method.invoke(target);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void addMethod(String methodName, Method method) {
//希望替换的方法名 methodName:onClick
//希望替换成那个方法 method:login()
map.put(methodName, method);
}
}
4.在InjectManager中实现具体的逻辑
private static void injectEvents(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//获取class所有的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
//遍历所有的方法
for (Method declaredMethod : declaredMethods) {
//得到方法上面的注解集合
Annotation[] annotations = declaredMethod.getAnnotations();
//遍历注解集合
for (Annotation annotation : annotations) {
//获取到每个注解的类型 :得到OnClick注解
Class<? extends Annotation> annotationType = annotation.annotationType();
//再从这个注解上获取注解,得到EventsBase注解
EventsBase eventsBase = annotationType.getAnnotation(EventsBase.class);
//如果eventsBase不为空
if (eventsBase != null) {
// 获取方法名 :setOnClickListener
String listenerSetter = eventsBase.listenerSetter();
// 获取监听类型:View.OnClickListener.class
Class<?> listenerType = eventsBase.listenerType();
// 获取回调的方法名 :onClick
String listenerCallBack = eventsBase.listenerCallback();
//实现方法替换的接口
ListenerInvocationHandler handle = new ListenerInvocationHandler(activity);
//添加要替换的方法 listenerCallBack:onClick是原本的回调方法名 declaredMethod:是在业务代码中自定义的方法login()
handle.addMethod(listenerCallBack, declaredMethod);
//设置代理监听
Object listenerProxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handle);
//
try {
//这个invoke里面的参数annotation就是onClick注解
int[] viewIds = (int[]) annotationType.getDeclaredMethod("values").invoke(annotation);
for (int viewId : viewIds) {
//找到findViewById方法 也可以直接用activity.findViewById(viewId);
Method findViewById = clazz.getMethod("findViewById", int.class);
//得到具体的View
Object view = findViewById.invoke(activity, viewId);
//这个方法就是setOnClick方法
Method setter = view.getClass().getMethod(listenerSetter, listenerType);
//打开私有权限,这里不设置为True的话private修饰的方法将不能访问
setter.setAccessible(true);
//执行方法 相当于 view.setOnClickListener(listener)
//view参数是要监听的对象,就是这个方法要在那个目标执行
// listenerProxy是代理监听对象,在这个类里面会把原本的onClick()方法替换成login()
setter.invoke(view, listenerProxy);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
5.onLongClick
新建一个@OnLongClick,将里面onClick对应的方法都换成onLongClick就行了
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventsBase(listenerSetter = "setOnLongClickListener",
listenerType = View.OnLongClickListener.class,
listenerCallback = "onClick")
public @interface onLongClick {
int[] values();
}
6.使用和总结
在activity中调用一下inject
逻辑其实不复杂,多熟悉API就行了。