IOC框架手动实现

Android注解框架实践

流行的注解框架是butterknife,使用注解框架主要是为了提高编码效率,尽可能地少写findviewbyid这种重复代码。注解分为编译期注解和运行时注解,butterknife是编译期注解,接下来自己实现的该框架是运行时注解。编译期注解有难度,并且不会对性能有影响。
Android中的注解反射框架包含三个部分:
1、Layout布局注解;2、view注解;3、事件注解。
框架建立分析如下:
该框架的目标是自动化下面3个工作:
1、Layout的注解解决的是activity的this.setContentView(R.layout.mainLayout),所以layout的注解的值只需要传递一个int值,同时需要将context对象传进去,那么其注解类设计如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InjectLayout {
    int value();
}

具体的方法:

 private static void injectLayout(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class clazz = context.getClass();
        Method mtd_setContentView = clazz.getMethod("setContentView", int.class);
        Method mtd_findViewById = clazz.getMethod("findViewById", int.class);
        Annotation layoutAnnotation = clazz.getAnnotation(InjectLayout.class);
        if (layoutAnnotation != null && layoutAnnotation instanceof InjectLayout) {
            int layoutId = ((InjectLayout) layoutAnnotation).value();
            mtd_setContentView.invoke(context, layoutId);
        }
}

在Activity中的用法:

@InjectLayout(value=R.layout.activity_main)
public class MainActivity extends BaseActivity {}

2、View注解,实现的是this.findViewById(R.id.xxx),所以其注解类只需要传递一个整型的id值。注解类设计如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {
    int value();
}

具体的方法:

private static void injectView(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class clazz = context.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Method mtd_findViewById = clazz.getMethod("findViewById", int.class);
        for (Field field : fields) {
            Annotation[] annotations = field.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof InjectView) {
                    int viewId = ((InjectView) annotation).value();
                    field.setAccessible(true);
                    View view = (View) mtd_findViewById.invoke(context, viewId);
                    field.set(context, view);
                }
            }
        }
}

在activity中的用法:

   @InjectView(value = R.id.button)
    Button myBtn;

3、方法的注解。其实指的是事件监听的注解,其形式为:

  view.setOnClickListener(new View.OnClickListener(){
                       Public void onClick(View view){
                       Xxxx;
                      }
                      });

或者view.setOnLongClickListener();还有view.setOnTouchListener()等等
因为这个里面有匿名内部类,所以需要使用到动态代理。为了实现这个方法,需要知道是哪个view来绑定监听,所以注解需要包含R.id.xxx;需要知道监听的是view 的哪个方法,所以需要一个string来表示具体的方法名(匿名内部类listener实现的方法);在实现动态代理时,还需要知道匿名内部类的全类名,所以也需要一个Class;所以其注解类设计如下

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBus(value = 1)
public @interface InjectOnClick {
    String onClickListener();
    String onClickMethod();
    String bindListerMethod();
    int[] bindViewId();
    Class type();
}

注解实现方法:

private static void injectOnClick(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        Class clazz = context.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        if (null == methods || methods.length == 0) return;
        HashMap<String, Method> methodMap = new HashMap<>();
        for (Method method : methods) {
            //method:onClickText
            Annotation[] annotations = method.getAnnotations();
            if (null == annotations || annotations.length == 0)
                continue;
            for (Annotation annotation : annotations)
                if (annotation instanceof InjectOnClick) {
                    String onClickName = ((InjectOnClick) annotation).onClickMethod();
                    methodMap.put(onClickName, method);
                    int[] viewIds = ((InjectOnClick) annotation).bindViewId();
                    for (int viewId : viewIds) {
                        //Annotation eventBus = (Annotation)annotation.annotationType();
                        Method mtd_findViewById = clazz.getMethod("findViewById", int.class);
                        //获取VIEW
                        View view = (View) mtd_findViewById.invoke(context, viewId);
                        Log.d(TAG, "injectOnClick: " + "");
                        //Class clazz_annoymous = Class.forName("android.view.View.OnClickListener");
                        Method setOnclickListener = view.getClass().getMethod(((InjectOnClick) annotation).bindListerMethod(), ((InjectOnClick) annotation).type());
                        Class clz = ((InjectOnClick) annotation).type();
                        //动态代理
                        XutilsInvocationHandler handler = new XutilsInvocationHandler(context, methodMap);//该类继承InvocationHandler.
                        Object proxy = (Proxy) Proxy.newProxyInstance(context.getClassLoader(), new Class[]{clz}, handler);
                        setOnclickListener.invoke(view, proxy);
                    }
                }
        }
    }

在activity中的使用方法:

    @InjectOnClick(onClickListener = "View.OnClickListener",bindListerMethod ="setOnClickListener",
                   onClickMethod ="onClick",bindViewId = {R.id.button},type = View.OnClickListener.class)
    public void onButtonClick(View view){//此处的参数view,不能省略。
        Log.d("liudong", "onClick: ");
        Toast.makeText(this,"BUTTON 被点击了",Toast.LENGTH_SHORT).show();
    }

总体的使用方式是,新建一个BaseActivity extends Activity,在其onCreate()中,依次实现layout view onClick的注解。
public void onCreate(){
InjectUtils.injectLayout(this);
InjectUtils.injectView(this);
InjectUtils.injectOnClick(this);
}
然后后续的Activity都继承该BaseActivity.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值