一、前言
先给大家推荐一款非常强大的 Android-注解库 提高效率的神器,在 github 上的火热程度也值得你拥有。它是基于反射来实现的,有一句话说得好:反射,反射,程序员的快乐
,那么反射是怎么让程序员快乐的呢?
从 kotlin
rxJava
的火爆让我意识到牛逼的程序员每天都在思考怎么样用 最简洁的代码实现最繁琐的功能
以此来提高工作效率,在此强烈的推荐 :
androidannotations+kotlin+RxJava
二、事件注入
本篇是基于运行时的注入(RUNTIME),比较简单的实现;而 androidannotations 基于编译时的实现(CLASS)的注入。后期提供编译时的实现方式。
先来看看效果:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClickUtils.injectClick(this);//注入点击事件
}
@Click
void btn() {
Toast.makeText(this, "hello word", Toast.LENGTH_LONG).show();
}
}
怎么这么熟悉呢,这不正是 ButterKnife 框架里的点击事件吗? ButterKnife
也是基于编译时的注入方式。
注入实现
(1)、事件注解类(Click)
@Target(ElementType.METHOD)//针对方法
@Retention(RetentionPolicy.RUNTIME)//运行时
public @interface Click {
int[] value() default {-1};
}
默认值值为 -1 ,为 -1 时自动匹配(R.id.+方法名)的控件ID,如
@Click
void btn()
匹配 R.id.btn
的控件ID
(2)、注入实现类(ClickUtils)
Class<?> cls = activity.getClass();
Method[] methods = cls.getDeclaredMethods();
首先是获取当前 Class 所申明的方法,包含了(私有的,保护的,共有的)方法。注意与
cls.getMethods(); //自身及父类的共有方法
的区别。
接着是获取每个方法的注解并获取注解值。
Click clickInject = method.getAnnotation(Click.class);
if (clickInject != null) {
int[] clickIds = clickInject.value();
}
然后获取 Activity
的 findViewById
方法:
Method findViewMethod = cls.getMethod("findViewById", int.class);
然后使用代理类,来实现 View.OnClickListener.class
来实现点击的接口:
Object proxy = Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{View.OnClickListener.class}, new DynaHandler(activity, method));
public static class DynaHandler implements InvocationHandler {
Object target = null;
Method mMethod = null;
public DynaHandler(Object target, Method method) {
this.target = target;
mMethod = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.mMethod.setAccessible(true);
return this.mMethod.invoke(target);
}
}
this.mMethod.invoke(target);
相当于:
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//调用注入的方法
}
});
如果你对代理类不了解,请参考:
接着是处理默认值 -1 的情况:
for (int id : clickIds) {
if (id == -1) { //默认查找 R.id+方法名称 的Id 是否存在
Class idClass = R.id.class; //获取到 ID 类
Field field = idClass.getField(method.getName());
if (field == null) {
throw new NoSuchFieldException(method.getName() + " id Non-existent");
}
id = field.getInt(idClass); //获取ID 值
}
最后是调用 setOnClickListener
方法:
Object view = findViewMethod.invoke(activity, id);
Method clickMethod = view.getClass().getMethod("setOnClickListener", View.OnClickListener
.class);
clickMethod.invoke(view, proxy);
下面是 ClickUtils
类的全部代码:
public class ClickUtils {
public static void injectClick(Activity activity) {
Class<?> cls = activity.getClass();
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
Click clickInject = method.getAnnotation(Click.class);
if (clickInject != null) {
int[] clickIds = clickInject.value();
try {
Method findViewMethod = cls.getMethod("findViewById", int.class);
Object proxy = Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{View.OnClickListener.class}, new DynaHandler(activity, method));
for (int id : clickIds) {
if (id == -1) { //默认查找 R.id+方法名称 的Id 是否存在
Class idClass = R.id.class;
Field field = idClass.getField(method.getName());
if (field == null) {
throw new NoSuchFieldException(method.getName() + " id Non-existent");
}
id = field.getInt(idClass);
}
Object view = findViewMethod.invoke(activity, id);
Method clickMethod = view.getClass().getMethod("setOnClickListener", View.OnClickListener
.class);
clickMethod.invoke(view, proxy);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
}
public static class DynaHandler implements InvocationHandler {
Object target = null;
Method mMethod = null;
public DynaHandler(Object target, Method method) {
this.target = target;
mMethod = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.mMethod.setAccessible(true);
return this.mMethod.invoke(target);
}
}
}
基于运行时的事件注入就完成了,那么怎么去实现编译时的注入呢?
由于编译时的实现篇幅太长,接着会分几篇来讲解。
三、结语
一路有你们的陪伴
期待你们的期待
一同成长