前言:如下是通过对注解框架的阅读和理解,自个整理的一些笔记,想学习注解原理的朋友可以参考下。
建议:如果使用AS开发,需要用到注解,建议安装Android ButterKnife Zelezny插件,可以提高开发效率。(注意:AS默认有butterknife的jar包,使用该插件时需要先导入butterknife包才能使用,就像导入jar一样的操作)
总结:
- 这里注解框架和xUtils注解框架同理,我们可以自己封装起来,方便使用
- 注解事件是不需要控件对象的,其实在注解事件里面已经有获取控件对象的操作,并通过代理对象给控件对象设置了事件监听
- 注解事件还是需要布局视图的,不然没地方触发事件
- 下面代码其实是完整的框架代码,后面把控件的注解的注释去掉即可,这里是为了方便说明注解事件
getDeclaredMethod()和getMethod()的区别:
getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。
getMethod*()获取的是类的所有公有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
注意:getDeclaredMethod方法对自身的类有完整的访问权限,但针对当前类;getMethod方法只对public的方法有访问权限,由于是公有,那么对自身的,基类继承的,接口实现的方法都有访问权限
主Activity中(注意:即使处理的是控件的注解事件,也要加入布局,可以直接加入或者注解加入)
@ContentView(value = R.layout.activity_main)
public class MainActivity extends Activity {
// @ViewInject(R.id.id_btn)
// private Button mBtn1;
// @ViewInject(R.id.id_btn02)
// private Button mBtn2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewInjectUtils.inject(this);
}
@OnClick({ R.id.id_btn, R.id.id_btn02 })
public void clickBtnInvoked(View view) {
switch (view.getId()) {
case R.id.id_btn:
Toast.makeText(this, "Inject Btn01 !", Toast.LENGTH_SHORT).show();
break;
case R.id.id_btn02:
Toast.makeText(this, "Inject Btn02 !", Toast.LENGTH_SHORT).show();
break;
}
}
}
主界面的注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();//@ContentView(value = R.layout.activity_main)
}
onClick的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")
public @interface OnClick {
int[] value();//{ R.id.id_btn, R.id.id_btn02 } 注入到这个int数组中
}
onClick注解上的@EventBase注解:
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
Class<?> listenerType();//监听类型,泛型---》View.OnClickListener.class
String listenerSetter();//设置监听的方法---》setOnClickListener
String methodName();//监听回调方法名---》onClick
}
注解主要的处理类:
/**
* 主要处理类
* @author huangweicai
*
*/
public class ViewInjectUtils {
private static final String METHOD_SET_CONTENTVIEW = "setContentView";
private static final String METHOD_FIND_VIEW_BY_ID = "findViewById";
public static void inject(Activity activity) {
injectContentView(activity);//设置主界面视图
// injectViews(activity);//给控件注入资源对象(这里不需要)
injectEvents(activity);//事件处理
}
/**
* 注入所有的事件
* @param activity
*/
private static void injectEvents(Activity activity) {
//继承Activity的子类的反射
Class<? extends Activity> clazz = activity.getClass();
Method[] methods = clazz.getMethods();//获取所有方法,用方法数组接收
// 遍历方法数组,得到每一个方法的注解
for (Method method : methods) {
//也用注解数组接收,因为一个方法上可能不止一个注解
Annotation[] annotations = method.getAnnotations();
// 取得注解数组中的每一个注解
for (Annotation annotation : annotations) {
//得到注解的注解类型对象,该对象可以获取注解上的注解
Class<? extends Annotation> annotationType = annotation.annotationType();
// 拿到注解上的注解
EventBase eventBaseAnnotation = annotationType.getAnnotation(EventBase.class);
// eventBaseAnnotation存在
if (eventBaseAnnotation != null) {
//注意:注解对象是可以直接点出来其中的成员对象的(默认修饰符:protect)
//setOnClickListener
String listenerSetter = eventBaseAnnotation.listenerSetter();
//View.OnClickListener.class
Class<?> listenerType = eventBaseAnnotation.listenerType();
//onClick
String methodName = eventBaseAnnotation.methodName();
try {
//得到MainActivity中注解值为value的方法
Method aMethod = annotationType.getDeclaredMethod("value");//当前类所有方法(包括公有、私有)
//执行该方法并传入注解对象,返回注入值的数组,{ R.id.id_btn, R.id.id_btn02 }
int[] viewIds = (int[]) aMethod.invoke(annotation, null);
// 通过InvocationHandler设置代理
DynamicHandler handler = new DynamicHandler(activity);
// 往haspmap集合中,添加需要拦截的事件方法,通过“循环+判断”得到的事件方法名来指定:methodName
handler.addMethod(methodName, method);
// 创建listenertype的代理对象-----
Object listener = Proxy.newProxyInstance(
listenerType.getClassLoader(),//View.OnClickListener.class对象获取类加载器
new Class<?>[] { listenerType },
handler
);
// 遍历所有的View,设置事件
for (int viewId : viewIds) {
//得到资源对象
View view = activity.findViewById(viewId);//传入资源id
//这步就是:setOnClickListener(new View.OnClickListener.class)
Method setEventListenerMethod = view.getClass()
.getMethod(listenerSetter, listenerType);
//这步就是:view通过代理对象来执行设置监听
setEventListenerMethod.invoke(view, listener);//listener 代理对象传进来
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
/**
* 注入所有的控件
* @param activity
*/
private static void injectViews(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
Field[] fields = clazz.getFields();
// 遍历所有成员变量
for (Field field : fields) {
ViewInject viewInjectAnnotation = field
.getAnnotation(ViewInject.class);
if (viewInjectAnnotation != null) {
int viewId = viewInjectAnnotation.value();
if (viewId != -1) {
// 初始化View
try {
Method method = clazz.getMethod(METHOD_FIND_VIEW_BY_ID,
Integer.class);
Object resView = method.invoke(clazz, viewId);
field.setAccessible(true);
field.set(clazz, resView);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
/**
* 注入主布局文件
* @param activity
*/
private static void injectContentView(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
// 查询类上是否存在ContentView注解
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null)// 存在
{
int contentViewLayoutId = contentView.value();
try {
Method method = clazz.getMethod(METHOD_SET_CONTENTVIEW,
int.class);
method.setAccessible(true);
method.invoke(activity, contentViewLayoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
拦截代理类:
/**
* 代理拦截类
* @author huangweicai
*
*/
public class DynamicHandler implements InvocationHandler {
private WeakReference<Object> handlerRef;//弱引用
private final HashMap<String, Method> methodMap = new HashMap<String, Method>(1);
/**
* 构造函数
* @param handler
*/
public DynamicHandler(Object handler) {
this.handlerRef = new WeakReference<Object>(handler);
}
/**
* 添加拦截的方法到对应列表
* @param handler
*/
public void addMethod(String name, Method method) {
methodMap.put(name, method);
}
public Object getHandler() {
return handlerRef.get();
}
public void setHandler(Object handler) {
this.handlerRef = new WeakReference<Object>(handler);
}
//执行拦截回调
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object handler = handlerRef.get();//拦截到的onClick方法
if (handler != null) {
String methodName = method.getName();//得到方法名,即onClick
method = methodMap.get(methodName);//得到该方法名的方法对象
if (method != null) {
return method.invoke(handler, args);
}
}
return null;
}
}