注解@Retention可以用来修饰注解,是注解的注解,称为元注解。
Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
@Retention 注解指定标记注解的存储方式:
RetentionPolicy.SOURCE - 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略。 这是默认的生命周期;
RetentionPolicy.RUNTIME - 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,因此运行时环境可以使用它。
@Target 注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:
ElementType.ANNOTATION_TYP可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
package com.example.inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
在Activity 中增加注解标注,通过注解+反射+动态代理等,来优化Activity中代码。省去 setContentView、findViewById、setOnClickListener等操作。
Layout注解
1 2 3 4 5
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface InjectLayout { @LayoutRes int value(); }
|
View注解
1 2 3 4 5
| @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface InjectView { @IdRes int value(); }
|
OnClick注解
1 2 3 4 5 6
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @InjectEvent(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, methodName = "onClick") public @interface OnClick { int[] value(); }
|
OnLongClick注解
1 2 3 4 5 6
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @InjectEvent( listenerSetter= "setOnLongClickListener", listenerType= View.OnLongClickListener.class,methodName = "onLongClick") public @interface OnLongClick { int[] value(); }
|
元注解 标注 OnClick OnLongClick 的注解
1 2 3 4 5 6 7
| @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface InjectEvent { String listenerSetter(); Class<?> listenerType(); String methodName(); }
|
注解管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
| package com.example.inject;
import android.app.Activity; import android.view.View;
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class InjectManage {
public static void inJect(Activity activity) { injectLayout(activity); injectView(activity); injectEvent(activity); }
/** * 依赖注入 layoutId布局 * 获取 Activity 上的注解 * * @param activity */ private static void injectLayout(Activity activity) { if (activity == null) { throw new NullPointerException(); } // 1.获取Activity的class对象 Class<? extends Activity> clazz = activity.getClass(); // 2.判断当前Activity是否为InjectLayout修饰 if (clazz.isAnnotationPresent(InjectLayout.class)) { // 3.获取InjectLayout注解 InjectLayout annotation = clazz.getAnnotation(InjectLayout.class); if (annotation != null) { try { // 4.通过反射获取 当前Activity 的 setContentView 方法 Method method = clazz.getMethod("setContentView", int.class); // 5.获取注解上的value layoutId int resourceId = annotation.value(); // 6.调用setContentView method.invoke(activity, resourceId); } catch (Exception e) { e.printStackTrace(); } }
} }
/** * 依赖注入 获取View * * @param activity */ private static void injectView(Activity activity) { if (activity == null) { throw new NullPointerException(); } // 1.获取Activity的class对象 Class<? extends Activity> clazz = activity.getClass(); // 2.判断当前Activity中所有的属性字段 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 3. 判断字段是否为InjectView修饰 if (field.isAnnotationPresent(InjectView.class)) { InjectView injectView = field.getAnnotation(InjectView.class); if (injectView != null) { try { // 4.反射获取findViewById方法 Method method = clazz.getMethod("findViewById", int.class); // 5.获取资源id int resourceId = injectView.value(); // 6.获取View对象 View view = (View) method.invoke(activity, resourceId); // 7.设置访问权限 field.setAccessible(true); // 8.将反射获取到的view赋值到Activity上 field.set(activity, view); } catch (Exception e) { e.printStackTrace(); } } } } }
/** * 依赖注入 获取 OnClick OnLongClick注解事件 * 注解 + 反射 + 动态代理 * * @param activity */ private static void injectEvent(Activity activity) { if (activity == null) { throw new NullPointerException(); } try { // 1.获取Activity的class对象 Class<? extends Activity> clazz = activity.getClass(); // 2.获取Activity的所有成员方法 排除继承方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { // 3.是否是自定义注解修饰 if (method.isAnnotationPresent(OnClick.class)) {//TODO OnLongClick事件处理 OnClick onClick = method.getAnnotation(OnClick.class); int[] value = onClick.value(); // 4.获取注解上的注解 元注解 InjectEvent injectEvent = onClick.annotationType().getAnnotation(InjectEvent.class); String listenerSetter = injectEvent.listenerSetter(); Class<?> listenerType = injectEvent.listenerType(); String methodName = injectEvent.methodName(); // 5.动态代理 生成代理的listener ProxyHandler handler=new ProxyHandler(activity); Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler); handler.mapMethod(methodName,method); // 6.反射获取 findViewById方法的Method对象 Method findViewByIdMethod = clazz.getMethod("findViewById", int.class); findViewByIdMethod.setAccessible(true); for (int id : value) { // 7.通过findViewById获取view View btn = (View) findViewByIdMethod.invoke(activity, id); // 8.根据listenerSetter方法名和listenerType方法参数找到method Method listenerSetMethod = btn.getClass().getMethod(listenerSetter, listenerType); listenerSetMethod.setAccessible(true); listenerSetMethod.invoke(btn, listener); } } } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); }
}
}
|
ProxyHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package com.example.inject;
import android.app.Activity; import android.util.Log;
import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.HashMap;
public class ProxyHandler implements InvocationHandler {
private WeakReference<Activity> mHandlerRef;
private HashMap<String, Method> mMethodHashMap;
public ProxyHandler(Activity activity) { mHandlerRef = new WeakReference<>(activity); mMethodHashMap = new HashMap<>(); }
public void mapMethod(String name, Method method) { mMethodHashMap.put(name, method); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Object handler = mHandlerRef.get(); if (null == handler) return null; String name = method.getName(); //将onClick方法的调用映射到activity 中的注解标注的方法 Method realMethod = mMethodHashMap.get(name); // Log.i("injectEvent", "proxy=" + proxy + ",method=" + method.getName() + ",realMethod=" + realMethod); if (null != realMethod) { return realMethod.invoke(handler, args); } } catch (Exception e) { e.printStackTrace(); } return null; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @InjectLayout(value = R.layout.activity_network) public class NetworkActivity extends AppCompatActivity {
@InjectView(R.id.get_data) Button button; @InjectView(R.id.get_data2) Button button2; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectManage.inJect(this); // setContentView(); // button=findViewById(R.id.button) // button.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // testNetWorkApi(); // } // }); // button.setOnLongClickListener(new View.OnLongClickListener() { // @Override // public boolean onLongClick(View v) { // return false; // } // }); } @OnClick({R.id.get_data,R.id.get_data2}) public void onClick(View view){ Log.e("Inject","依赖注入实现 onClick"); } @OnLongClick(R.id.get_data) public void onLongClick(View view){ Log.e("Inject","依赖注入实现 onLongClick"); }
|
期间遇到点小问题,就是动态代理这块,对这块理解不够深。首先 Activity中 使用OnClick注解修饰的方法onClick()是需要传入参数的,否则在动态代理里,动态代理回调这个方法就会出错。