您当前的位置: 首页 > 
  • 1浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

开源框架解读--ButterKnife简析

沙漠一只雕得儿得儿 发布时间:2020-08-12 11:45:08 ,浏览量:1

ButterKnife 算是一款知名老牌 Android 开发框架了,通过注解绑定视图,避免了 findViewById() 的操作,广受好评!由于它是在编译时对注解进行解析完成相关代码的生成,所以在项目编译时会略耗时,但不会影响运行时的性能。接下来让我们从使用到原理一步步深入了解这把黄油刀的故事!转载

零、简述:

较早期的注解库是如何工作的呢?

我们可以看到在使用时需要ButterKnife.bind(this),大家会认为利用反射获取到这个target的Activity,然后获取到这个Activity上面的所有注解,最终通过反射获取到findViewById。这种是之前比较早期的注解库的实现方式。这种方式有个较大的缺点,就是在Activity运行时如果大量使用反射,则会影响APP的运行效率,造成卡顿、产生临时对象造成频繁GC。

较早期的注解框架是通过RUNTIME运行时工作的。也就是我们注解的生命周期是RunTime,然后在运行时通过反射完成注入。这种方式的实现比较简单,但是致命的问题就是会影响APP的运行时效率。

那么ButterKnife是如何解决上述问题的呢?

ButterKnife是利用APT技术,也就是编译时解析技术。ButterKnife虽然使用的注解注入框架,但是它并不是运行时的注解,而是编译期利用注解生成代码,因此这对程序的运行期是没有任何副作用的;当然缺点就是在编译期会有一点点的时间成本。因此这里有三个知识点是必须清楚的:注解、反射、注解处理器。

具体可以参考,运行时注解和编译期注解的工作流程:

Android组件化系列之编译期注解手写注解、注解处理器、及注解工作流程

Android组件化系列之动态注解手写注解、注解处理器、及注解工作流程

ButterKnife的工作原理

1、编译的时候扫描注解,并做相应的处理,生成java代码,生成Java代码是调用javapoet库生成的;

2、调用ButterKnife.bind(this);方法的时候,将ID与对象的上下文绑定在一起。ButterKnife通过自身的注解处理器,扫描this这个类中的所有相关注解,根据注解生成相应的java代码,这些java代码就是我们之前需要手写的findViewById这些方法。

以下内容基于 butterknife:8.8.1 版本,主要包括如下几个方面的内容:

  • 简单使用
  • 原理分析
  • 注解处理器
  • JavaPoet
一、简单使用

首先编写一个 ButterKnife 简单使用的例子,方便后续的分析。先在 app的 build.gradle 中加入如下配置,完成 ButterKnife 引入:

dependencies {
    ......
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}

接下来在 Activity 中使用,界面上一个TextView一个Button,很简单就不解释了:

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tv_title)
    TextView title;

    @OnClick(R.id.bt_submit)
    public void submit() {
        title.setText("hello world");
    }

    private Unbinder unbinder;

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

    @Override
    protected void onDestroy() {
        unbinder.unbind();
        super.onDestroy();
    }
}
二、原理分析

点击下@BindView,可以看到ButterKnife是使用了编译期的注解

/**
 * 定义了一个用在属性上的运行时注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
    int value();
}

 @Retention定义了该注解被保留的时间长短,有三种值可以选择 1、SOURCE,表示注解保留在源码层面,编译的后就会被擦除 2、CLASS,表示注解只保留到编译阶段 3、RUNTIME,表示注解保留到运行时,可以反射获取注解信息 @Target定义了注解用在什么地方 1、FIELD,属性 2、METHOD,方法函数 3、TYPE,类/接口 4、PARAMETER,参数 5、CONSTRUCTOR,构造方法

然后我们编译一下项目。直觉告诉我们应该从ButterKnife.bind(this)开始分析,因为它像是 ButterKnife 和 Activity 建立绑定关系的过程,看具体的代码:

@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
   View sourceView = target.getWindow().getDecorView();
   return createBinding(target, sourceView);
}

sourceView代表当前界面的顶级父 View,是一个FrameLayout,继续看createBinding()方法:

private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
    Class targetClass = target.getClass();
    if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
    Constructor bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //noinspection unchecked
      bindingCtor = (Constructor            
关注
打赏
1657159701
查看更多评论
0.1283s