刚进公司的时候,clone了花果山的代码下来看,很幸运,这个项目刚刚开始,我可以把leo的框架先完整的看一遍而不受其他细节的影响,Leo写了非常多的基础类,非常多的抽象类,整个app的网络请求,加载图片,ListView加载数据都已经封装好,在这个基础上,我们的代码一下简化了不少,但另一方面,我们需要去理解Leo写的这些类到底为我们做了什么,当我开始找所有的基类的时候,我发现有些类根本在project里找不到,甚至连第三方类库也找不到,后来在Leo帮忙配置环境的时候我才知道,原来这些类都在远程库里,需要gradle去下载相应的依赖(之前都是用的Eclipse开发,Android studio最近才用,所以gradle我并不熟悉),这次就不谈gradle了,因为让我觉得神奇的代码其实是Leo写的注解,java算是白学了,每次看书看到注解的时候都以为只是简单的注释没必要看,直到看到Leo用@ViewById代替findViewById方法让代码看上去简洁清晰的时候我知道我该看看Java的注解了:
java为我们提供了元注解,元注解负责注解其他注解:@Target,@Retention,@Documented,@Inherited,根据这四个我们可以自定义自己的注解
我们只讨论前两个,因为前两个比较常用,也是Leo使用的
1.@Target说明了自定义注解修饰的对象,取值(ElementType)可以是
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
2.@Retention说明了自定义注解保留的时间,取值(RetentionPoicy)可以是
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
知道这两个后,我们可以自定义注解了,自定义注解一共三步:一是定义注解,二是定义注解处理器,三是使用注解。
一.定义注解
为了和接口区分,Java在interface关键字前加一个@符号来定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewById {
int value() default -1;
int id() default -1;
}
可以看到Leo自定义ViewById注解修饰的对象是域,并且是在运行是有效,value表示域的值
public static int inject(Object object, View view) {
int injectCount = 0;
Field[] fields = object.getClass().getDeclaredFields();
Field[] arr$ = fields;
int len$ = fields.length;
for(int i$ = 0; i$ < len$; ++i$) {
Field field = arr$[i$];
field.setAccessible(true);
try {
if(field.get(object) == null) {
ViewById e = (ViewById)field.getAnnotation(ViewById.class);
if(e != null) {
int viewId = e.value() != -1?e.value():e.id();
if(viewId != -1) {
field.set(object, view.findViewById(viewId));
++injectCount;
}
}
}
} catch (IllegalAccessException var10) {
var10.printStackTrace();
} catch (IllegalArgumentException var11) {
Log.e(TAG, "Field name : " + field.getName());
var11.printStackTrace();
}
}
return injectCount;
}
可以看到,Leo先把对象里所有的域找出来放到一个数组,然后对数组的每一个域通过java的反射机制得到访问权限,用
ViewById e = (ViewById)field.getAnnotation(ViewById.class);获取到该域的注解,然后用
field.set(object, view.findViewById(viewId));为该域调用findViewById方法
三.使用注解
在BaseActivity中调用:
inject(activity, activity.getWindow().getDecorView());
之后所有继承BaseActivity的Activity中都可以直接使用
@ViewById(R.id.name)
private EditText mName;
这两行就代替了以下代码:
private EditText mName;
mName = findViewById(R.id.name);
而且关键是可以在Activity的域中使用,而不用在onCreate()方法中去调用findViewById方法。使代码看上去简洁清晰了许多。