前言
Java的所有框架基本都是基于反射的,所以有句话是这么说的,无反射,无框架。所以Android的注入框架也是基于反射的,接下来就简单的介绍一下Android的注入框架你应该知道的一切。
注解简介
注解(Annotation)在Java里面是比较重要的一部分,但是通常很少接触到这一部分,这里就简单的过一下。现在我们简单的写一个注解然后解释它。
通过Eclipse右键->New->Annotation然后敲入下面的代码。
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { int vauls(); String test(); }
可以看到Target这个标注我们定义为FIELD就是类里面的属性的意思,Retention这个标注是表示是运行时的注解。每个注解的意思大家可以收一下自己看看。然后看看我们怎么使用这个注解。我们随便在某一个类里面声明一个对象。如下
@TestAnnotation(test="hello",vauls=12) private Button button3;
这样就声明好了我们的注解。然后就是注解的使用。也简单的看一下怎么使用的。
Class<?> clas = getClass(); //获取属性 Field fields[] = clas.getDeclaredFields(); for (Field field : fields) { //获取注解 TestAnnotation testAnnotation = field.getAnnotation(TestAnnotation.class); if (testAnnotation != null) { //得到注解里面的值 String test = testAnnotation.test(); int id = testAnnotation.vauls(); System.out.println(test + id); } }
就这样简单的使用,如果需要深入的理解注解可以在查一下注解的资料。
关于注入框架你应该知道的一切
打造自己的注入框架
首先说一下我们这次要实现怎么样的一个东西,注入View和注入Onclick事件,首先我们先解决注入View的问题.
View的注入
首先我们还是新建一个注解,敲入以下代码。
package com.edsheng.inject; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /*** * Copyright (c) 2015, TNT All Rights Reserved. * View注解在声明VIew控件的时候进行注解就行了 * @author bobo * @date 2015-6-9 * @filename ViewInject.java * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ViewInject { int value(); }
然后我们在新建一个ViewInjectUtile类,在里面实现这样的一个方法。
/*** * 注入控件View * 简单说一下注入控件的流程 * 1:根据Filed遍历所有的Filed * 2:得到我们需要的注解 * 3:根据注解拿到id * 4:通过反射去调用找查方法 * 5:最后通过反射赋值 * * @param activity */ private static void injectView(Activity activity) { Class<?> cls = activity.getClass(); Field field[] = cls.getDeclaredFields();// 获取所有的filed for (Field field2 : field) { ViewInject inject = field2.getAnnotation(ViewInject.class);// 获取注解 if (inject != null) { int id = inject.value(); // 得到id try { // findViewById Method method = cls.getMethod("findViewById", int.class); Object resView = method.invoke(activity, id);// 得到控件 field2.setAccessible(true); field2.set(activity, resView);// 赋值给View } catch (Exception e) { e.printStackTrace(); } } } }
注释都写的很清楚我就不解释了,这样就很简单的实现了View的注入,使用也很简单。
事件的注入
我们还是新建一个注解来完成我们的事件注入,敲入以下代码。
/*** * * Copyright (c) 2015, TNT All Rights Reserved. * 方法的注解类在需要回调OnlickLisenler的时候进行注解就行了 * * @author bobo * @date 2015-6-9 * @filename MethodInject.java * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MethodInject { int[] value(); }
然后在ViewInjectUtile这个类里面实现这样一个方法。
/** * 注入监听的方法 所有的框架基本都是基于反射来实现的,不是有一句话么?无反射无框架。 * 简单的说一下这个流程 * 1:在我们的acitvity里面注入方法 * 2:生成动态代理 * 3:通过东动态代理去回调我们注入的方法 * * @param activity */ private static void injectMethod(Activity activity) { Class<?> cls = activity.getClass(); Method methods[] = cls.getMethods();// 获取这个类的public方法 for (Method method : methods) { MethodInject meathdInject = method.getAnnotation(MethodInject.class); // 获取方法上的注解 if (meathdInject != null) {// 当有注解的时候生成动态代理 Object proxy = (Object) Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(), new Class<?>[] { View.OnClickListener.class }, new DynaHanlder(activity, method)); int ids[] = meathdInject.value();// 获取注解里面的id try { Method findviewbyid = cls.getMethod("findViewById", int.class);// 得到方法 for (int id : ids) { Object view = findviewbyid.invoke(activity, id);// 根据方法获取view Method onclickMethod = view.getClass().getMethod("setOnClickListener", View.OnClickListener.class); onclickMethod.invoke(view, proxy);// 调用setOnClickListener方法回调在动态类里面 } } catch (Exception e) { e.printStackTrace(); } } } }
这里需要注意的就是动态类的生成和代理,我们把View.OnClickListener这个的接口通过代理和反射来回调给注解的地方,我们来看看这个DynaHanlder怎么实现的。
public static class DynaHanlder implements InvocationHandler { Object target = null; Method method = null; public DynaHanlder(Object target, Method method) { super(); this.target = target; this.method = method; } /** * 这个函数就是动态注册的回调方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里调用动注入的方法 return this.method.invoke(target, args); } }
也很简单就是保持我们的方法的应用当通过代理回调我们的时候我们也通过反射去调用我们的方法。
最后再给外部暴露一个接口方法。
//外部调用接口 static public void inject(Activity activity) { injectView(activity); injectMethod(activity); }
最后来看看我们怎么使用它吧。
public class MainActivity extends Activity { @ViewInject(R.id.button) private Button button; @ViewInject(R.id.button2) private Button button2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mainactivity); ViewInjectUtile.inject(this); button.setText("fuck"); button2.setText("asdfasdf"); } @MethodInject({ R.id.button, R.id.button2 }) public void onClick(View v) { switch (v.getId()) { case R.id.button: // System.out.println("asdfasdf"); Toast.makeText(this, "R.id.button", 0).show(); break; case R.id.button2: Toast.makeText(this, "R.id.button2", 0).show(); // System.out.println("asdf"); break; default: break; } }
当点击按钮会回调我们的方法,注入一开始就帮我们完成了id与控件的绑定,这就是注入框架主要的精髓了,需要更好更强大的框架还需要自己慢慢完成。这里贴上源代码地址:传送门