流行的注解框架是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.
Android注解框架实践
2068

被折叠的 条评论
为什么被折叠?



