【译】从java注解分析ButterKnife工作流程

博客原地址:从java注解分析ButterKnife工作流程
译文原链接:How ButterKnife actually works?
翻译修改:Anthony

在我的上一篇文章中,绝对不容错过,ButterKnife使用详谈中,讲解了对ButterKnife的使用。这篇文章将接着一篇文章使用之后,对ButterKnife的工作流程进行概要分析。这里Butterknife分析来自参考自链接How ButterKnife actually works?,并作出部分修改。这里做一个整理和学习。
考虑到ButterKnife的入口使用java注解,比如。

class ExampleActivity extends Activity {
 @Bind(R.id.user) EditText username;
 @Bind(R.id.pass) EditText password;
@OnClick(R.id.submit) void submit() {
 // TODO call server…
 }
@Override public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.simple_activity);
 ButterKnife.bind(this);
 // TODO Use fields…
 }
}

首先这里会对java注解(java annotation)分析,然后面接上Butterknife的工作流程。

1 什么是java注解?

在java语法中,使用@符号作为开头,并在@后面紧跟注解名。被运用于类,接口,方法和字段之上,例如:

@Override
void myMethod() { 
......
}

这其中@Override就是注解。这个注解的作用也就是告诉编译器,myMethod()方法覆写了父类中的myMethod()方法。

2 java中内置的注解

java中有三个内置的注解:

@Override,表示当前的方法定义将覆盖超类中的方法,如果出现错误,编译器就会报错。
@Deprecated:如果使用此注解,编译器会出现警告信息。
@SuppressWarnings:忽略编译器的警告信息。

2.1 @Override 注解

当我们的子类覆写父类中的方法的时候,我们使用这个注解,这一定程度的提高了程序的可读性也避免了维护中的一些问题,比如说,当修改父类方法签名(方法名和参数)的时候,你有很多个子类方法签名也必须修改,否则编译器就会报错,当你的类越来越多的时候,那么这个注解确实会帮上你的忙。如果你没有使用这个注解,那么你就很难追踪到这个问题。
示例:

public class MyParentClass {

    public void justaMethod() {
        System.out.println("Parent class method");
    }
}

public class MyChildClass extends MyParentClass {

    @Override
    public void justaMethod() {
        System.out.println("Child class method");
    }
}
2.2 @Deprecated注解

一个弃用的元素(类,方法和字段)在java中表示不再重要,它表示了该元素将会被取代或者在将来被删除。
当我们弃用(deprecate)某些元素的时候我们使用这个注解。所以当程序使用该弃用的元素的时候编译器会弹出警告。当然我们也需要在注释中使用@deprecated标签来标示该注解元素。
示例:

class DeprecatedDemo {
   /* @deprecated This field is replaced by 
    * MAX_NUM field
    */
   @Deprecated
   int num=10;

   final int MAX_NUM=10;

   /* @deprecated As of release 1.5, replaced 
    * by myMsg2(String msg, String msg2)
    */
   @Deprecated
   public void myMsg(){
       System.out.println("This method is marked as deprecated");
   }

   public void myMsg2(String msg, String msg2){
       System.out.println(msg+msg2);
   }

   public static void main(String a[]){      
        DeprecatedDemo obj = new DeprecatedDemo();
        obj.myMsg();
        System.out.println(obj.num);
   }
}
2.3 @SuppressWarnings注解

当我们想让编译器忽略一些警告信息的时候,我们使用这个注解。比如在下面这个示例中,我们的deprecatedMethod()方法被标记了@Deprecated注解,所以编译器会报警告信息,但是我们使用了@SuppressWarnings(“deprecation”)也就让编译器不在报这个警告信息了。

@SuppressWarnings("deprecation")
    void myMethod() {
        myObject.deprecatedMethod();
}

3 自定义注解

3.1 来看看一个自定义注解

可以通过下面这种形式添加自己的自定义注解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation{
    int studentAge() default 18;
    String studentName();
    String stuAddress();
    String stuStream() default "CSE";
}

自定义注解使用@interface来声明一个注解,这里对这个自定义的注解进行使用:

@MyCustomAnnotation(
    studentName="Chaitanya",
    stuAddress="Agra, India"
)
public class MyClass {
...
}

可以看到上面的studentAge 和stuStream字段已经在注解定义阶段设置了默认值(当然也可以对这些默认值进行修改),studentName和stuAddress没有默认值,在使用的时候必须定义值。

3.2 元注解

在上面的注解定义阶段,你一定注意到了这四个注解,那么他们是什么呢?

(1).@Target,    
(2).@Retention,   
(3).@Documented,   
(4).@Inherited

这是java 5.0之中引入的四个元注解,元注解也就是负责注解其他的注解。
那么来分别看看这四个注解是什么意思?
(1)@Target

表示该注解用于什么地方,可能的ElementType参数包括:
CONSTRUCTOR:构造器的声明
FIELD:域声明
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类,接口或enum声明

比如说这个注解表示只能在方法中使用。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
public @interface MyCustomAnnotation {

}
public class MyClass {
   @MyCustomAnnotation
   public void myMethod()
   {
    ......
   }
}

(2)@Retention

表示在什么级别保留此信息,可选的RetentionPolicy参数包括:
SOURCE:注解仅存在代码中,注解会被编译器丢弃
CLASS:注解会在class文件中保留,但会被VM丢弃
RUNTIME:VM运行期间也会保留该注解,因此可以通过反射来获得该注解

比如说这个注解表示VM运行期间也会保留该注解。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface MyCustomAnnotation {
}

(3)@Documented

将注解包含在javadoc中

示例:

java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation { //Annotation body}

(4)@Inherited

允许子类继承父类中的注解

示例,这里的MyParentClass 使用的注解标注了@Inherited,所以子类可以继承这个注解信息:

java.lang.annotation.Inherited
@Inherited
public @interface MyCustomAnnotation {
}
@MyCustomAnnotation
public class MyParentClass { 
  ... 
}
public class MyChildClass extends MyParentClass { 
   ... 
}

4 ButterKnife工作流程解析

有了上面的java注解的基础知识,那么接着学习。

4.1 java 注解工作流程

1 注解是在编译(compile)时期进行处理的。
2 注解处理器(Annotation Processor)读取java代码处理相应的注解,并且生成对应的代码。
3 生成的java代码被当做普通的java 类再次编译。
4 注解处理器不能修改存在java输入文件,也不能对方法做修改或者添加。

java编译流程

4.2 ButterKnife对应的注解工作流程

当你编译你的Android工程时,ButterKnife工程中ButterKnifeProcessor类的process()方法会执行以下操作:

1 开始它会扫描Java代码中所有的ButterKnife注解@Bind、@OnClick、@OnItemClicked等。
2 当它发现一个类中含有任何一个注解时, ButterKnifeProcessor会帮你生成一个Java类,名字<类名>$$ViewInjector.java,这个新生成的类实现了ViewBinder接口。
3 这个ViewBinder类中包含了所有对应的代码,比如@Bind注解对应findViewById(), @OnClick对应了view.setOnClickListener()等等。
4 最后当Activity启动ButterKnife.bind(this)执行时,ButterKnife会去加载对应的ViewBinder类调用它们的bind()方法。

4.3 实例分析

首先ExampleActivity 中使用ButterKnife,如下面所示

class ExampleActivity extends Activity {
 @Bind(R.id.user) EditText username;
 @Bind(R.id.pass) EditText password;
@OnClick(R.id.submit) void submit() {
 // TODO call server…
 }
@Override public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.simple_activity);
 ButterKnife.bind(this);
 // TODO Use fields…
 }
}

接着编译器就会生成ExampleActivity$$ViewBinder.java文件

“`
public class ExampleActivity

ViewBinder<Textendscom.lgvalle.samples.ui.ExampleActivity>implementsViewBinder<T>@Overridepublicvoidbind(finalFinderfinder,finalTtarget,Objectsource)Viewview;view=finder.findRequiredView(source,21313618,fielduser);target.username=finder.castView(view,21313618,fielduser);view=finder.findRequiredView(source,21313618,fieldpass);target.password=finder.castView(view,21313618,fieldpass);view=finder.findRequiredView(source,21313618,fieldsubmitandmethodsubmit);view.setOnClickListener(newbutterknife.internal.DebouncingOnClickListener()@OverridepublicvoiddoClick(android.view.Viewp0)target.submit(););@Overridepublicvoidreset(Ttarget)target.username=null;target.password=null;>1ButterKnifefindViewBinderForClass(ExampleActivity.class)ExampleActivity
ViewBinder.java*
2 ExampleActivity
ViewBinder.bind()viewviewExampleActivity.classButterKnifePublic访访private3onClickListenersExampleActivity
ViewBinder.bind()
方法方法中被包装处理点击事件。

5 参考链接

How ButterKnife actually works?
butterknife 源码分析
最新ButterKnife框架原理
Java注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值