Personal blog
www.milovetingting.cn
Dynamic proxy + annotation + reflection to achieve View click event binding
The proxy mode is to provide an object with a proxy object that controls references to the original object. The proxy mode includes static proxy and dynamic proxy.
Static agent
Defines the interface
public interface Player {
void play(a);
}
Copy the code
Define concrete implementation classes
public class PlayerImpl implements Player {
@Override
public void play(a) {
System.out.println("PlayerImpl play..."); }}Copy the code
Defining proxy classes
public class ProxyImpl implements Player {
private Player player;
public ProxyImpl(Player player) {
this.player = player;
}
@Override
public void play(a) { player.play(); }}Copy the code
As you can see, both the concrete implementation class and the proxy class implement the same interface class, and in the implementation of the proxy class, the concrete implementation class is referenced.
A dynamic proxy
Static proxies are defined before they run. Dynamic proxy is the dynamic creation of proxies and instances at run time. The JDK provides the Proxy class to create dynamic proxies
public static Object newProxyInstance(ClassLoader loader,Class
[] interfaces,InvocationHandler h)throws IllegalArgumentException{}Copy the code
First look at the specific use
Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Player.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
returnmethod.invoke(player, args); }});Copy the code
NewProxyInstance corresponds to three parameters:
-
Class loader
-
The class of the interface, Proxy generates the Proxy class from this class
-
InvocationHandler, the callback to a method that is called by a method of the proxy class
The complete code for the dynamic proxy class
public class DynamicProxy {
private Player player;
public DynamicProxy(Player player) {
this.player = player;
}
public void setPlayer(Player player) {
this.player = player;
}
public Player getProxy(a) {
return (Player) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Player.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
returnmethod.invoke(player, args); }}); }}Copy the code
The proxy. newProxyInstance method ends up calling ProxyGenerator
public static byte[] generateProxyClass(finalString var0, Class<? >[] var1,int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('. ', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: "+ var4x); }}}); }return var4;
}
Copy the code
Proxy classes generated by dynamic proxies exist in memory.
Implement the binding of click events
The following is based on dynamic proxy + annotation + reflection, control click event binding.
Start by defining annotations for the event type
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventType {
Class listenerType(a);
String listenerSetter(a);
}
Copy the code
Then define annotations for click and hold events respectively
@EventType(listenerType = View.OnClickListener.class,listenerSetter = "setOnClickListener")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
@IdRes int[] value();
}
Copy the code
@EventType(listenerType = View.OnLongClickListener.class, listenerSetter = "setOnLongClickListener")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnLongClick {
@IdRes int[] value();
}
Copy the code
Click events are then defined in the Activity
@OnClick({R.id.btn1, R.id.btn2})
public void click(Button view) {
Toast.makeText(getApplicationContext(), view.getText(), Toast.LENGTH_SHORT).show();
}
@OnLongClick({R.id.btn1, R.id.btn2})
public boolean longClick(Button view) {
Toast.makeText(getApplicationContext(), view.getText() + "-LongClick", Toast.LENGTH_SHORT).show();
return true;
}
Copy the code
Bind in the Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectHelper.inject(this);
}
Copy the code
The realization of the InjectHelper
public class InjectHelper {
public static void inject(final Activity target) {
if (target == null) {
return;
}
Class<? extends Activity> clz = target.getClass();
Method[] declaredMethods = clz.getDeclaredMethods();
for (Method method : declaredMethods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.isAnnotationPresent(EventType.class)) {
EventType eventType = annotationType.getAnnotation(EventType.class);
Class listenerType = eventType.listenerType();
String listenerSetter = eventType.listenerSetter();
try {
Method valueMethod = annotationType.getDeclaredMethod("value");
int[] ids = (int[]) valueMethod.invoke(annotation);
method.setAccessible(true);
ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(method, target);
Object proxyInstance = Proxy.newProxyInstance(target.getClassLoader(), new Class[]{listenerType}, invocationHandler);
for (intid : ids) { View view = target.findViewById(id); Method setter = view.getClass().getMethod(listenerSetter, listenerType); setter.invoke(view, proxyInstance); }}catch (Exception e) {
}
}
}
}
}
static class ListenerInvocationHandler<T> implements InvocationHandler {
private Method method;
private T target;
public ListenerInvocationHandler(Method method, T target) {
this.method = method;
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return this.method.invoke(target, args); }}}Copy the code