Android进阶AndroidAndroid Studio

注解框架Butterknife解析

2017-06-30  本文已影响100人  慕涵盛华

1.什么是注解
2.注解的分类
3.编译时注解的原理
4.APT
5.创建项目及依赖
6.编码实现
7.总结

我们首先了解一下什么是注解以及注解的核心原理,在掌握原理的前提下自己动手实现一个注解框架。通过代码的编写能够对Butterknife底层实现有更加清楚的认识。

注解

注解在Java文档中定义如下:

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

翻译一下,大概的意思是:

注解是一种元数据, 可以添加到java代码中. 类、方法、变量、参数、包都可以被注解,注解对注解的代码没有直接影响。

注解的分类

运行时注解和编译时注解,都可以理解为通过注解标识,然后进行相应处理。两者的区别是:前者是运行时执行的,反射的使用会降低性能;后者是编译阶段执行的,通过生成辅助类实现效果。

运行时注解由于性能问题被一些人所诟病,所以本文主要讲解编译时注解的原理,并实现自己的Butterknife框架。

编译时注解的原理

编译时注解的核心原理依赖APT(Annotation Processing Tools)实现:

编译时Annotation解析的基本原理是,在某些代码元素上(如类型、函数、字段等)添加注解,在编译时javac编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是ButterKnife Dragger等开源库的基本原理

那么APT又是什么呢?

APT(Annotation Processing Tool)是一种处理注解的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。

下面以Butterknife为例:

public class MainActivity extends AppCompatActivity {

  @BindView(R.id.tv_main)
  TextView tvMain;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      ButterKnife.bind(this);
  }
}

这是Butterknife最简单的用法,我们只需要加上一个注解 @BindView并指定对应的Id就可以了,从而避免了findViewById(),那么它底层是怎么实现的呢?本篇文章重点不是介绍Butterknife的实现原理,所以这里只是简单的说一下它底层的实现。这里我们只写了一个MainActivity.java文件,编译后我们查看一下class文件,我们会发现在MainActivity中多了一个内部类ViewBinder,其实Butterknife就是在这个内部类中关联对应控件的,下面以伪代码的形式简单说明一个它底层实现的原理。

字节码文件
public class MainActivity extends AppCompatActivity {

  @BindView(R.id.tv_main)
  TextView tvMain;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      ButterKnife.bind(this);
  }

  /*
  * 当bind()方法被调用之后,tvMain就有对应的值了
  * */
  private static class ViewBinder{
       public static void bind(MainActivity activity){
           activity.tvMain = (TextView) activity.findViewById(R.id.tv_main);
       }
   }
}

大概的原理是这样的:在编译的时候如果某个类中使用了注解,Butterknife就会在其中“添加”一个内部类,在内部类中实现控件的关联。我们知道编译java源文件的工具是javac,其实在javac中有一个注解处理工具(依赖APT)用来编译时扫描和处理的注解的工具。我们可以为特定的注解,注册你自己的注解处理器,来实现自己的处理逻辑。

上面我们了解了基本原理,接下来我们实战演练


项目结构

我们的项目结构如上图所示:每个库都有自己的实现功能,最中通过我们的项目依赖相应的库来使用。

创建App

我们新建一个工程,因为我们要处理注解需要用到APT,所以在app中需要使用apt的插件
<b>github:</b>https://github.com/Aexyn/android-apt

关联APT插件:
Step1: 在我们工程目录下的build.gradle文件中添加如下代码:
Step2: 在我们项目目录下的build.gradle文件中添加如下代码:

创建Java库(定义注解)

inject-annotion

创建Android库

inject

<b>关联Java库(inject-annotion)</b>

关联java库

创建Java库(处理注解库)

我们需要在编译的时候根据注解创建新的类并添加到源文件中,所有需要引用几个依赖。并且要关联上一个Java库

依赖

<b>全部创建完毕后,我们的工程目录如下:</b>

项目目录

关联库

让我们的项目(app)去关联注解库

关联库

编写代码

1.定义注解:inject-annotion

/**
 * @Retention(RetentionPolicy.CLASS):编译时被保留,在class文件中存在,但JVM将会忽略
 * @Target(ElementType.FIELD) :出现的位置(字段、枚举的常量)
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value();
}

2.定义方法:inject

3.处理注解:inject-compiler

测试

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.text)
    TextView textview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectView.bind(this);
        if(textview == null){
            Toast.makeText(this,"注解处理失败",Toast.LENGTH_SHORT).show();
        }else{
            textview.setText("世界你好!");
        }
    }
}

用法跟Butterknife一样,页面上有一个TextView,使用注解关联,如果关联失败,弹出提示信息。否则设置显示为“世界你好!”。

演示

总结

通过上述代码的编写,我们能更加对Butterknife的底层实现有更清楚的认识,虽然只是实现了绑定View。在编译时javac编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,然后将添加了注解的所有元素都传递到process函数中,我们需要继承该类重写此方法我们就能获取我们想要处理的注解。在里面做具体的绑定逻辑。
AutoService注解处理器是Google开发的,用来生成META-INF/services/javax.annotation.processing.Processor文件的。我们可以在注解处理器中使用注解。非常方便。

关注微信公众号获取更多相关资源

Android小先生
上一篇下一篇

猜你喜欢

热点阅读