ButterKnife 原理及部分实现代码

1.ButterKnife介绍

主要是解决掉 findViewById 和 setOnclick ,还包括资源的注入 ,编译时注解。

2. ButterKnife原理分析

主要采用编译时注解,就是用 apt 生成代码

3.注解处理器是什么?

注解处理器是(Annotation Processor)是javac的一个工具,用来在编译 时扫描注解(Annotation)。

3.理解处理器AbstractProcessor
public class ButterKnifeProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv){
        super.init(processingEnv);
    }
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return null;
    }
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();//使用最新支持版本
    }
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return true;
    }
}
  • init():会自动被注解处理工具调用,通过并传入ProcessingEnviroment参数参数可以获取到很多有用的工具类:Elements , Types , Filer等工具
  • getSupportedAnnotationTypes()指定注解处理器是注册给哪一个注解的(Annotation),它是一个字符串的集合,可以支持多个类型的注解(Annotation),注解(Annotation)指定必须是完整的路径。
  • process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) 这个也是最主要的,扫描出的结果会存储进roundEnv中,可以在这里获取到注解内容,编写你的操作逻辑。注意,process()函数中不能直接进行异常抛出,否则的话,运行Annotation Processor的进程会异常崩溃,然后弹出一大堆让人捉摸不清的堆栈调用日志显示。
  • getSupportedSourceVersion():用于指定你的java版本,一般使用:SourceVersion.latestSupported()
3. 自己动手实现

工程介绍

以下配置以 [Android Studio 3.2.1] 为例

代码 ButterKnife ViewBind 为例子

项目工程分为4部分

app主工程

ButterKnife分为3个module

      butterknife-annotation(定义注解)

      butterknife-complier(AbstractProcessor 处理器代码)

      butterknife(ButterKnife.bind 代码)

Gradle配置

app.gradle

在gradle配置文件中添加‘annotationProcessorOptions.includeCompileClasspath = true’的配置,如果不添加该配置,编译时将会报错。

使用implementation引入工程 butterknife_annotations、butterknife

使用annotationProcessor引入butterknife_compiler

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.maqf.androidplugindemo"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    implementation project(':butterknife_annotations')
    implementation project(':butterknife')
    annotationProcessor project(':butterknife_compiler')
}

butterknife-complier gradle

引入AbstractProcessor依赖库

compile 'com.google.auto.service:auto-service:1.0-rc3'
compile 'com.squareup:javapoet:1.8.0'

完整的gradle如下

apply plugin: 'java-library'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.google.auto.service:auto-service:1.0-rc3'
    compile 'com.squareup:javapoet:1.8.0'
    implementation project(':butterknife_annotations')
}

sourceCompatibility = "7"
targetCompatibility = "7"

MainActivty.java

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.btn)
    Button mBtn ;


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

    @CheckNet
    public void click(View view){
        startActivity(new Intent(this,ProxyActivity.class));
    }
}

通过注解生成的类 MainActivity_ViewBinding

public final class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  @UiThread
  public MainActivity_ViewBinding(MainActivity target) {
    this.target = target;
    target.mBtn = Utils.findViewById(target,2131165218);
  }

  @Override
  @CallSuper
  public void unbind() {
    MainActivity target = this.target;;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");;
    this.target = null;;
    target.mBtn = null;
  }
}

butterknife工程的关键类ButterKnife

通过反射创建 MainActivity_ViewBinding 对象

public class ButterKnife {
   public static Unbinder bind(Activity activity){
       try {
           
           Class<?> clazz = Class.forName(activity.getClass().getName()+"_ViewBinding" ) ;
           Constructor<?> cons = clazz.getConstructor(activity.getClass());
           Unbinder unbinder = (Unbinder) cons.newInstance(activity);
           return unbinder ;
       } catch (Exception e) {
           e.printStackTrace();
       }

       return Unbinder.EMPTY ;
   }
}

ButterKnife-annotation工程

RetentionPolicy.CLASS 作用域class字节码上,生命周期只有在编译器间有效。编译时注解注解处理器的实现主要依赖于AbstractProcessor来实现

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS) 
public @interface BindView {
    int value();
}

ButterKnife-complier工程

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
    private Filer mFiler ;
    private Elements mElementUtils ;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler() ;
        mElementUtils = processingEnvironment.getElementUtils() ;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        System.out.println("------------------------->");
        //BindView注解的Element
        Set<? extends Element>  bindViewElements  = roundEnvironment.getElementsAnnotatedWith(BindView.class) ;
        Map<Element,List<Element>> elementListMap = new LinkedHashMap<>() ;
        for(Element element:bindViewElements ){
            //Activity 类的Element
            Element enclosingElement = element.getEnclosingElement() ;
            List<Element> listElement = elementListMap.get(enclosingElement) ;
            if(listElement==null){
                listElement = new ArrayList<>() ;
                elementListMap.put(enclosingElement,listElement) ;
            }
            listElement.add(element) ;
        }
        //遍历map 生成类
        for(Map.Entry<Element, List<Element>> entry :elementListMap.entrySet()){
            Element enclosingElement = entry.getKey();
            List<Element> elements = entry.getValue();
            //生成类 classNameStr_ViewBinding
            String classNameStr = enclosingElement.getSimpleName().toString() ;
            ClassName activityClassName = ClassName.bestGuess(classNameStr) ;
            ClassName superClass = ClassName.get("com.maqf.butterknife","Unbinder") ;
            TypeSpec.Builder classBuilder = TypeSpec.classBuilder(classNameStr+"_ViewBinding")
                    .addModifiers(Modifier.FINAL,Modifier.PUBLIC) //类的属性,public final 
                    .addSuperinterface(superClass) //类实现的接口
                    .addField(activityClassName,"target",Modifier.PRIVATE); //设置属性 

            //unbind方法 实际代码样式
            /**
             * @Override
             *   @CallSuper
             *   public void unbind() {
             *     MainActivity target = this.target;
             *     if (target == null) throw new IllegalStateException("Bindings already cleared.");
             *     this.target = null;
             *
             *     target.textView1 = null;
             *     target.textView2 = null;
             *   }
             */
            ClassName callSuperClassName = ClassName.get("android.support.annotation", "CallSuper");
            MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
                    .addAnnotation(Override.class)
                    .addAnnotation(callSuperClassName)
                    .addModifiers(Modifier.PUBLIC);

            unbindMethodBuilder.addStatement("$T target = this.target;",activityClassName) ;
            unbindMethodBuilder.addStatement("if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");") ;
            unbindMethodBuilder.addStatement("this.target = null;") ;


            //构造函数
            ClassName uiThreadClassName = ClassName.get("android.support.annotation", "UiThread");
            MethodSpec.Builder  constructorBuilder = MethodSpec.constructorBuilder()
                    .addAnnotation(uiThreadClassName)
                    .addParameter(activityClassName,"target")
                    .addModifiers(Modifier.PUBLIC) ;
            //   this.target = target;
            constructorBuilder.addStatement("this.target = target") ;

            for(Element element:elements){
                // target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
                //   target.textView2 = Utils.findRequiredViewAsType(source, R.id.tv2, "field 'textView2'", TextView.class);
                int viewId = element.getAnnotation(BindView.class).value();
                String fieldName = element.getSimpleName().toString() ;
                ClassName utilClassName = ClassName.get("com.maqf.butterknife","Utils") ;
                constructorBuilder.addStatement("target.$L = $T.findViewById(target,$L)",fieldName,utilClassName,viewId) ;

                /**
                 * target.textView1 = null;
                 * target.textView2 = null;
                 */
                unbindMethodBuilder.addStatement("target.$L = null",fieldName) ;

            }

            classBuilder.addMethod(constructorBuilder.build()) ;
            classBuilder.addMethod(unbindMethodBuilder.build());
            /**
             * 获取包名
             */
            String packageName = mElementUtils.getPackageOf(enclosingElement).toString() ;
            try {
                JavaFile.builder(packageName,classBuilder.build())
                        .addFileComment("butterknife 自动生成")
                        .build()
                        .writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



        return false;
    }

    /**
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }

    /**
     * @return
     */
    private Set<Class<? extends Annotation>> getSupportedAnnotations() {
        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class);
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值