Android进阶9:手写Bufferknife(编译时注解)

本文详细介绍了如何手写Bufferknife,通过讲解JavaPoet、注解和注解处理器(APT)等核心概念,阐述了利用编译时注解实现自动绑定View的原理。文章首先强调了需要掌握的四个知识点,接着逐步解析注解的生命周期、目标以及注解处理器的使用。最后,作者给出了项目结构和实现思路,包括创建注解、注解处理器和使用反射来调用生成的Java代码,从而实现Bufferknife的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说到Bufferknife,相信基本都用过。在Activity中使用:


class ExampleActivity extends Activity {
  @BindView(R.id.title) TextView title;
  @BindView(R.id.subtitle) TextView subtitle;
  @BindView(R.id.footer) TextView footer;
 
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
    // TODO Use fields...
  }
}

这样的话 我们就初始化绑定了View,那么其内部是什么原理呢? 我相信很多小伙伴都能说出来:注解+反射,但是具体怎么实现的,懵逼了吧?= - = ? 看完本篇博客我相信你也可以自己写一个。

需要掌握的知识点

  1. JavaPoet
  2. 注解
  3. 注解处理器(AbstractProcessor)
  4. apt (annotationProcessor)

上面的四个知识点是我们手写Bufferknife比需要掌握和理解的,下面我们一个一个讲解

apt (annotationProcessor)

ATP是一种处理注解的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 这里的源代码文件就是我们自己的包含@BindView注解的XXXActivity或者XXX Fragment。

annotationProcessor是APT工具的一种,是Google开发的内置框架,不需要特殊引入,比如我们是不是在引入Bufferknife的时候需要加入:

annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'

这就表明,要自动检测我们的代码从而在编译的时候生成代码类。生成什么代码类呢?

public class MainActivity$ViewBinder implements ViewBinder<MainActivity> 

也就是生成类似上面的代码类,这些自动生成的类的功能就是:findViewById 和 setOnClickListener。 好,那么生成这些类有什么用处呢? 试想一下,如果我们把这个类和我们的自己的XXXActivity关联起来,
也就是说让这些生成的类代替XXXActivity执行findViewById的操作,那么是不是就相当于实现了Bufferknife呢? 我想说是的!
好,现在总结一点:就是说,我们如果有我们自己的注解处理器,然后在我们的工程中引入:

annotationProcessor XXXX

这样的话,就可以自动扫描我们代码中使用该注解的Java文件,最后自动在生成Java文件。

我们创建了注解BindView, 然后在XXXActivity中使用该注解,通过APT在编译器的扫描,那最后可以产出Java文件,而这个Java文件是我们自己定义创建的。

好,这样的话APT我们知道了,上图中的注解是怎么定义的呢?

注解

说到注解我们肯定很熟悉Override吧? 看下源代码

package java.lang;

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

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

这是一个标准的注解定义,首先Retention定义了注解的“生命周期”,也就是起作用的时间段:只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
其次Target定义了该注解要用在什么用途?换句话说就是:这个注解要用在字段?类?方法?接口?枚举?

说到Retention,分三种类型:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

RUNTIME是运行时注解,在程序运行的时候才起作用,然后在程序运行的时候,再通过反射,所具体的操作。
比如:

   /**
     * 绑定View
     * @param object
     * @param view
     */
    private static void bindView(Object object, View view) {
        Field[] declaredFields = object.getClass().getDeclaredFields();
        for(Field field : declaredFields) {
            boolean annotationPresent = field.isAnnotationPresent(BindView.class);
            if(!annotationPresent) {
                return;
            }
            BindView fieldAnnotation = field.getAnnotation(BindView.class);
            field.setAccessible(true);
            try {
                field.set(object, view.findViewById(fieldAnnotation.resIdValue()));

                //view设置点击事件
                bindClick(object, view);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值