#目录
1. 什么是注解
2. 分类
3. 定义注解
4. 定义注解处理器
5. ButterKnife原理
#1.什么是注解
JDK 5增加了注解,是代码中的特殊标志,可在编译,类加载是,运行时被读取并进行相应操作。
#2.分类
2.1 标准注解
@override : 对父类方法进行重写标志
@Deprecated : 过时方法添加的标志
@SuppressWarning : 选择性取消特定代码的警告
@SafeVarargs : 声明可变长度参数方法
2.2 元注解
元注解就是用于注解其他注解
@Target : 注解所修饰的对象范围
@Inherited:表示注解可被继承
@Documented: 表示注解应为JavaDoc工具记录
@Retention: 声明注解保留策略
@Repeatable:允许一个注解在同一个声明类型上多次使用
注:
#@Target 取值为ElementType 类型数组
TYPE: 修饰类,接口,枚举类型
FIELD: 修饰成员变量
METHOD: 修饰方法
PARAMETER: 修饰参数
CONSTRUCTOR: 修饰构造函数
LOCAL_VARIABLE: 修饰局部变量
ANNOTATION_TYPE: 修饰注解
PACKAGE: 修饰包
TYPE_USE: 使用类型
#@Retention 取值有三种不同级别保留策略
RetentionPolicy.SOURCE: 源码级别,注解信息只保留在.java源码中,编译后被丢弃
RetentionPolicy。CLASS: 编译级别,注解信息会保留在.java源码和.class中,运行时JVM会丢弃该注解信息。
RetentionPolicy.RUNTIME: 运行时注解,运行时JVM也会保留该注解信息
#3.定义注解
3.1.定义编译时注解
1.注解
@Rentention(RetentionPolicy.CLASS)
public @interface Man {
String name() default "霍华德";
int age() default 23;
}
2.编译时注解处理器
public class ClaaProcessor extends AbstractProcessor{
@override
public synchronized void init(ProcessingEnvironment var1) {
//注解工具初始化,ProcessingEnvironment提供很多工具类,如
//Elements、Types、Filer、Messager、SourceVersion
}
@override
public boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2){
//每个处理器的主函数,编写扫描处理注解的代码,以及生成Java文件。入参RoundEnviroument,可查询包含指定注解的被注解元素。
...
return true;
}
@override
public SourceVersion getSupportedSourceVersion() {
//指定使用的Java版本
return SourceVersion.latestSupported();
}
@override
public Set<String> getSupportedAnnotationTypes() {
//指定该注解处理器是注册给哪个注解的,返回值是字符串集合,包含注解类型的全称。
Set<String> annotations = new LinkedHashSet<String>();
annotations.add(BindView.class.getCannonicalName());
return annotations;
}
}
3.注册注解处理器
可以使用Google开源的AutoService进行注册,依赖中导入
dependencies {
implementation 'com.google.auto.service:auto-service:1.0-rc2'
}
使用:
@AutoService(Processor.class)
public class ClaaProcessor extends AbstractProcessor{
...
}
3.2.定义运行时注解
1.注解
@Rentention(RetentionPolicy.RUNTIME)
@Target(MENTHOD)
@Documented
public @interface GET {
String value() default "";
}
2.使用注解
public class Test (){
@GET(value = "http://baidu.com")
public String getMethod(){
return "";
}
}
3.运行时注解处理器
运行时注解处理器需要用到反射机制
public class AnnnotationProcessor {
public static void main (String[] args){
Menthods methods = Test.class.getDeclaredMethods();
for (Method method : methods){
GET get = method.getAnnotation(GET.class);
System.out.plintln (get.value());
}
}
}
#4.ButterKnife原理解析
4.1.使用
#导入依赖
//project..build.gradle添加
dependencies {
classpath "com.jakewharton:butterknife-gradle-plugin:10.0.0"
}
//app.build.gradle添加
apply plugin: "com.neenbedankt.android-apt"
dependencies {
implementation 'com.jakewharton:butterknife:10.0.0'
apt 'com.jakewharton:butterknife-compiler:10.0.0'
}
public class MainActivity extends AppCompatActivity {
//绑定控件
@BindView(R.id.text_tv)
TextView mText;
...
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
//绑定监听
@OnClick(R.id.text_tv)
public void onClick(View v){
}
@OnTextChanged(R.id.text_tv)
void onTextChanged(charSequence s, int start, int before , int count){
}
}
4.2.原理
ButterKnife使用的是编译时注解
@Rentention(RetentionPolicy.CLASS)
@Target(FIELD)
public @interface BindView{
@IdRes int value() ;
}
4.2.1.ButterKnifeProcessor源码分析
public final class ButterKnifeProcessor extends AbstractProcessor{
...
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);// 1
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {//2
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
javaFile.writeTo(filer); //3
} catch (IOException e) {
error(typeElement,"Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
}
注释1出findAndParseTargets
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
...
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
parseBindView(element, builderMap, erasedTargetNames);//1
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
...
return bindingMap;
}
注释1 parseBindView
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames){
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element); //1
...
if (hasError) {
return;
}
int id = element.getAnnotation(BindView.class).value();//2
Id resourceId = elementToId(element, BindView.class, id);
if (builder != null) {
String existingBindingName = builder.findExistingBindingName(resourceId);
if (existingBindingName != null) {
error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
BindView.class.getSimpleName(), id, existingBindingName,
enclosingElement.getQualifiedName(), element.getSimpleName());
return;
}
} else {
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);
builder.addField(resourceId, new FieldViewBinding(name, type, required));//3
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}
注释1 处 isInaccessibleViaGeneratedCode检查
- 修饰符不能为private和static
- 包含类型不能为非Class
isBindingInWrongPackage检查包名不能以android.和java.开头
注释2 获取注解的标注值
注释3 将注解修饰 的类型信息及注解的成员变量存储到BindingSet中
回到process方法中的注释2,通过遍历Map.Entry<TypeElement, BindingSet>集合,获取BindingSet对象,并调用BindingSet#brewJava方法,生成JavaFile文件,注释3处将其输出为Java文件。文件名使用注解类名 +_ViewBinding 如MainActivity_ViewBinding
4.2.2.ButterKnife的bind方法
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Dialog source) {
//获取Activity的DecorView
View sourceView = source.getWindow().getDecorView();
return bind(target, sourceView);
}
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);//1
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);//2
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
注释1处的 findBindingConstructorForClass方法
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);//1
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
...
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");//2
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);//3
} catch (ClassNotFoundException e) {
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);//4
return bindingCtor;
}
注释1从BINDING中获取对应Class的Constructor实例,它的定义如下
static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
以Class为Key,Constructor作为value的LinkedHashMap。
注释2通过反射生成Class类,并缓存到BINDINGS中。最后返回该类的Constructor
最后在bind函数的注释2处通过该Constructor实例化MainActivity_ViewBinding对象
4.2.3.生成辅助类分析
上面例子中我们在MainActivity中使用ButterKnife,绑定组件。因此生成的辅助类名为 MainActivity_ViewBinding
public class MainActivity_ViewBinding<T extends MainActivity> implements Unbinder {
protected T target;
@UiThread
public MainActivity_ViewBinding(T target, View source) {
this.target = target;
target.mText = Utils.findRequiredViewAsType(source, R.id.text_tv, "field 'mText'", TextView.class);//1
@Override
@CallSuper
public void unbind() {
T target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
target.mText = null;
this.target = null;
}
**注释1处findRequiredViewAsType **
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = findRequiredView(source, id, who);//2
return castView(view, id, who, cls);//将View强转为传入的Class类型,本例中就是TextView.class.
}
注释2 处findRequiredView
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);//通过findViewById将id对应的View返回
if (view != null) {
return view;
}
...
}
Brin233
发布了9 篇原创文章 · 获赞 3 · 访问量 2492
私信
关注