android依赖注入注解的实现

android依赖注入,注解功能,本demo实现view注入,点击事件注入.

首先实现两个注解类:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {
	//只接收一个int类型的值,用于表示view的id
    public @IdRes int value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
	//接收一个int[],表示可以接收多个view的id,绑定到同一个click执行方法上
    public @IdRes int[] value();
}

接下来需要编写注解的解释类:

首先抽象出一个接口类:

//这个泛型接口接收一个AnnotatedElement的子类,它是什么呢?
//在Java中,Field,Method,Constructor…一切可注解的对象都实现了AnnotatedElement接口。
public interface ProcessorIntf<T extends AnnotatedElement> {
	/*
	 * 每个不同的处理器都会通过这个方法来告诉最终的调度者,这个注解是否由我来处理 
	 */
    public boolean accept(AnnotatedElement t);
    
    /*
     *第一个object是目标对象,
     *第二个view是根view
     *第三个是加上注解的值
     */
    public void process(Object object, View view, T ao);
}
具体解释类需实现此接口,InjectViewProcessor类用来解释Field属性,OnClickProcessor类用来解释Method方法

public class InjectViewProcessor implements ProcessorIntf<Field>{
	
	@Override
    public boolean accept(AnnotatedElement e) {
		//如果当前这个AnnotatedElement实例加有InjectView注解,则返回true
        return e.isAnnotationPresent(InjectView.class);
    }

    @Override
    public void process(Object object, View view, Field field) {
    	//如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null
        InjectView iv = field.getAnnotation(InjectView.class);
        if(iv != null) {
	        final int viewId = iv.value(); //获取注解值(找到控件的id)
	        final View v = view.findViewById(viewId); //通过根view查找此id对应的view
	        field.setAccessible(true); //设置属性值的访问权限
	        try {
	            field.set(object, v); //将查找到的view指定给目标对象object
	        } catch (IllegalAccessException e) {
	            throw new RuntimeException(e);
	        }
        }
    }
}

public class OnClickProcessor implements ProcessorIntf<Method> {
    @Override
    public boolean accept(AnnotatedElement e) {
    	//这个很简单,就是告诉管理器我响应OnClick注解
        return e.isAnnotationPresent(OnClick.class);
    }

    @Override
    public void process(Object object,View view, Method method) {
    	//先拿到具体的注解对象 ,并拿到里面的值
        final OnClick oc = method.getAnnotation(OnClick.class);
        final int[] value = oc.value();
        //遍历id值设置点击事件将方法method与目标对象object传给监听事件
        for (int id : value) {
            view.findViewById(id).setOnClickListener(new InvokeOnClickListener(method,object));
        }
    }

    //这里面的InvokeOnClickListener是一个中间件,注册给系统,系统在得到点击事件后,
    //通知给InvokeOnClickListener,在这个里面再调用你所指定的方法。
    private static class InvokeOnClickListener implements View.OnClickListener {

        public Method method;
        public WeakReference<Object> obj;
        private boolean hasParam;

        InvokeOnClickListener(Method m, Object object) {
            this.method = m;
            //使用一个WeakReference
            this.obj = new WeakReference<Object>(object);
            //先拿到方法的参数,看看有没有参数 , 没有就纪录下hasParam为false 
            final Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes == null || parameterTypes.length == 0) {
                hasParam = false;
            //如果有参数的话,判断参数个数,并且判断参数类型是否为view
            //isAssignableFrom方法用来判断一个类Class1和另一个类Class2是否相同或是另一个类的子类或接口
            } else if (parameterTypes.length > 1 || !View.class.isAssignableFrom(parameterTypes[0])) {
                throw new IllegalArgumentException(String.format("%s方法只能拥有0个或一个参数,且只接收View", m.getName()));
            } else {
            	//有一个合格参数则返回true
                hasParam = true;
            }
        }

        @Override
        public void onClick(View v) {
            //点击事件触发了
            Object o = obj.get();
            if (o != null) {
                try {
                    if (hasParam) {
                        method.invoke(o, v); //有参数,就把view设置过去
                    } else {
                        method.invoke(o); //没有参数就直接调
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
接下来为了方便使用需要加一个管理类

public class Injector {

	private static List<? extends ProcessorIntf<? extends AccessibleObject>> chain
			                        = Arrays.asList(new InjectViewProcessor(), new OnClickProcessor());

    public static void inject(Activity act) {
    	//传入activity实例和rootview
        inject(act,act.getWindow().getDecorView());
    }

    public static void inject(Object obj, View rootView) {
        final Class<?> aClass = obj.getClass();
        //获取当前活动中所有声明的属性,包括私有属性
        final Field[] declaredFields = aClass.getDeclaredFields();
        for (Field f : declaredFields) {
           doChain(obj,f,rootView);
        }
        //获取当前活动中所有声明的方法,包括私有方法
        final Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method m : declaredMethods) {
            doChain(obj, m, rootView);
        }
    }
    //把每个遍历到的方法或者属性,甚至是构造方法,类等等通过处理器链来询问这个注解你accept吗?接受则交给它来处理
    private static void doChain(Object obj,AccessibleObject ao, View rootView) {
            for (ProcessorIntf p : chain) {
            	//判断当前AccessibleObject(Field,Method都继承了此类)是否为ProcessorIntf具体子类类型的注解
                if(p.accept(ao)) {
                	p.process(obj,rootView,ao);
                }
            }
    }
}

功能实现,最后看一下在MainActivity中的使用

public class MainActivity extends Activity {
    
	@InjectView(R.id.tv)
	private TextView mTextView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//初始化
		Injector.inject(this);	
	}
	
	@OnClick({R.id.bt1,R.id.bt2})
    public void btnClick(Button view) {
        final int id = view.getId();
        if (id == R.id.bt1) {
        	mTextView.setText("按钮一被点击");
        }else if (id == R.id.bt2) {
        	mTextView.setText("按钮二被点击");
        }
    }	  
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值