您的位置:首页 > 移动开发 > Android开发

android依赖注入注解的实现

2017-04-01 15:06 399 查看
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("按钮二被点击");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: