关注注解 我想都不陌生了,Android上采用注解来快速开发 我在实际工作中还没试过,这里简介记录一下我对Android注解框架的理解。
注解在Android开发中大致可以分为用在 方法 、成员、和 事件上,其他暂时不考虑。
本文参考知名博主张鸿洋的 关于IOC的描述 地址:
http://blog.csdn.net/lmj623565791/article/details/39269193
一、注解反射成员变量和 方法。编写反射@interface xxx
引用注解,反射得到注解的值,传递进入当前Context反射得到Method。Field 等,然后执行invoke 方法即可
这样可以通过注解反射成员变量(可以初始化控件),成员方法(加载布局文件)等。比较简单。
btn = findViewById:可能需要多个btn,因此写注解是多个的。@inject(value=id)
Class<? extends Activity> clazz = activity.getClass();
Field[] fields = clazz.getDeclaredFields();
// 遍历所有成员变量
for (Field field : fields)
{
ViewInject viewInjectAnnotation = field.getAnnotation(ViewInject.class);
}
setOnContentView(id) :由于一个类只有个layout布局设置,所以只需要一个annotation就行了.(@contentview(value=id))
Class<? extends Activity> clazz = activity.getClass();
ContentView contentView = clazz.getAnnotation(ContentView.class);
以上是注解反射字段和 反射方法。
二、通过注解注入事件。
下面这样的代码
btn.setOnclickListener(new View.OnclickListener(){onclick })
将改为:
@Onclick(value=R.id.btn1,value=R.id.btn2...)
public void myClick(View v){ }
1、反射得到btn
利用注解拿到id后 findviewById(id),得到Button
2、利用Proxy InvocationHandler 创建接口(View.OnclickListener)的对象,可以自定义一个类实现InvocationHandler 。
例如下面代码:
main:test:
InvocationHandler i = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("方法名:"+method.getName());
System.out.println((args==null)?"没有参数":"参数:"+args[0]);
System.out.println(method.getReturnType().getName());
System.out.println();
return "test result";
}
};
//利用Proxy创建出接口InterfaceTest的对象,
Object o = Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{InterfaceTest.class}, i);
//转化为InterfaceTest对象
InterfaceTest interfaceTest = (InterfaceTest) o;
//当调用对象的方法时,也就是接口中的方法时就会调用InvocationHandler的invoke方法。上面的i就是代理对象。
String result1 = interfaceTest.printInfo1();
interfaceTest.printInfo2();
String result = interfaceTest.printInfo3("1");
System.out.println(result);
public interface InterfaceTest{
public String printInfo1();
public void printInfo2();
public String printInfo3(String p);
}
3、反射执行setOnclickListener
总体思想是把下面这句代码,封装起来,只把控件id和自己定义的事件处理方法 提供给程序员来写。
btn.setOnclickListener(new View.OnclickListener(){onclick })
@Onclick(value=R.id.btn1,value=R.id.btn2...)
public void myClick(View v){ }
那么可以推测到有个注解 @interface Onclick。这个@interface Onclick注解里面得完成btn.setOnclickListener(new View.OnclickListener(){onclick }) 操作。而@interface Onclick是个注解。
所以只有在这个注解上再次使用一个注解。于是乎有:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")
public @interface Onclick{
//放置控件的id
int[] value;
}
那么在 @interface EventBase中来提供 设置listener的元素:监听类的类型(OnclickListener.class),类型名称(setOnclickListener),监听类的方法也就是事件的处理方法
(onClick),有了这三个元素就可以 在验证@interface EventBase注解时 完成btn.setOnClickListener(obj)操作.
下面是实现的代码:
/**
* 注入所有的onclick事件
*
* @param activity
*/
private static void injectEvents(Activity activity)
{
Class<? extends Activity> clazz = activity.getClass();
Method[] methods = clazz.getMethods();
//遍历当前activity中的所有方法
for (Method method : methods)
{
Annotation[] annotations = method.getAnnotations();
//拿到方法上的所有的注解,这里可以拿到 @interface Onclick注解
for (Annotation annotation : annotations)
{
Class<? extends Annotation> annotationType = annotation.annotationType();
//拿到@interface EventBase注解,因为这个注解上提供了反射执行btn.setOnclickListener(obj)的元素。
//这个注解是@interface Onclick注解上的注解。
EventBase eventBaseAnnotation = annotationType
.getAnnotation(EventBase.class);
//如果设置的为EventBase注解
if (eventBaseAnnotation != null)
{
//取出设置监听器的类型,监听器的名称,监听器的方法(事件回调执行方法onClick)
String listenerSetter = eventBaseAnnotation.listenerSetter(); //setOnclickListener
Class<?> listenerType = eventBaseAnnotation.listenerType();//OnclickListener.class
String methodName = eventBaseAnnotation.methodName();//onClick
try
{
//拿到Onclick注解中的value方法:拿到控件的id,以此可以得到btn控件实例
Method aMethod = annotationType.getDeclaredMethod("value");
//取出所有的viewId,value()方法不需要参数,所以是null
int[] viewIds = (int[]) aMethod.invoke(annotation, null);
//通过Proxy InvocationHandler创建代理对象。即OnclickListener接口的对象。
DynamicHandler handler = new DynamicHandler(activity);
handler.addMethod(methodName, method);// 这句可以不要
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),new Class<?>[] { listenerType }, handler);
//到此即可设置事件了:
for (int viewId : viewIds)
{
//遍历所有的View,创建出view ,
View view = activity.findViewById(viewId);
//再利用view反射出事件方法名并得到此方法的Method对象,
Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
//最后利用Method对象设置事件即可.btn.setOnclickListener(obj);
//setOnclickListener方法对象反射执行,参数为所属对象和方法参数。
setEventListenerMethod.invoke(view, listener);
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}
}
}
方便快速开发的 注解框架基本原理 我理解就是这个了,下面是如何使用的问题,比较简单。
@ContentView(value = R.layout.activity_main) //在类上加上注解 ,用来设置布局文件
public class MainActivity extends Activity implements OnClickListener
{
@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); //验证自定义的注解,也就是一些初始化工作等。
}
}
public static void inject(Activity activity)
{
injectContentView(activity);
injectViews(activity);
injectEvents(activity);
}