android butterKnife 原理及其手写实现

 

 

butterKnife

  • 强大的View 绑定和时间处理功能可以帮助程序简化代码节省开发时间
  • 能够方便你的处理adapter 里面的viewholder绑定问题
  • 使用过程中不会影响app运行的效率和性能 因为注解作用在编译器
  • 使用butterKnife 使我们的代码更清晰,可读性更强

使用流程

    在app build.gradle文件下面添加 
    implementation'com.jakewharton:butterknife:9.0.0'  //注解
    annotationProcessor'com.jakewharton:butterknife-compiler:9.0.0'//注解处理器 

    compilOptions{
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }

实现原理

ButterKnife 的原理就是利用注解和注解处理器针对每个activity都生成一个相对应的类将原本
需要进行绑定的view或者点击事件以及资源ID绑定等都写入相对应的类中,然后调用相应的类中的方法,
从而相当于原本需要程序员手写的代码现在由butterKnife自动实现了 

注解和注解处理器

注解作用 :用于标记没有什么实际的作用 仅仅作用于区分
注解处理器作用: 处理相关的注解
注解处理器怎么声明   在注解处理器moudle中build.gradle添加下面的
//注册我们的注解  告诉JVM 我们这个moudle里面自定义注解处理器  androidstudio 小于3.4
implementation 'com.google.auto.service:auto-service:1.0-rc3'
//androidStudio 版本大于3.4用下面的这种形式
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'

代码手写实现ButterKnife

首先创建两个javaLibrary 
annotations
annotation_compiler

annotation_compiler下面的类

要添加依赖喔  
implementation project(path: ':annotations')





package com.example.annotation_compiler;

import com.example.annotations.BindView;
import com.google.auto.service.AutoService;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaFileObject;

/**
 * 注解处理器
* 生成activity 相对应的类
*/
@AutoService(Process.class)   // 注册注解处理器  就是gradle文件添加依赖的原因
public class AnnotationComiler  extends AbstractProcessor {
// 生成文件的对象
Filer filer;

@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
    super.init(processingEnvironment);
    filer = processingEnvironment.getFiler();
}

/**
 * 声明这个注解处理器需要处理的注解
 * @return
 */
@Override
public Set<String> getSupportedAnnotationTypes() {
    Set<String> types = new HashSet<>();
    types.add(BindView.class.getCanonicalName());
    return types ;
}

/**
 * 声明当前注解处理器支持的java版本
 * @return
 */
@Override
public SourceVersion getSupportedSourceVersion() {
    return processingEnv.getSourceVersion();
}

/**
 * 在这个方法里面我们就要去写文件
 * @param set
 * @param roundEnvironment
 * @return
 */
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    // 拿到整个模块中《app》用到BindView的注解的节点
    Set<? extends Element> elementsAnnotatedWith =
            roundEnvironment.getElementsAnnotatedWith(BindView.class);

    // key -- activity名字
    Map<String, List<VariableElement>> map = new HashMap<>();
    for (Element element:elementsAnnotatedWith) {
        //获取到成员变量的节点 也就是控件
        VariableElement variableElement= (VariableElement)element;
        String activityName = variableElement.getEnclosingElement().getSimpleName().toString(); // 获取到activity名字
        List<VariableElement> variableElements = map.get(activityName);
        if (variableElements==null){
            variableElements = new ArrayList<>();
            map.put(activityName,variableElements);
        }
        variableElements.add(variableElement);
    }
    if (map.size()>0){
        Writer writer=null;
        Iterator<String> iterator = map.keySet().iterator();
        while (iterator.hasNext()){
            String activityName = iterator.next();
            List<VariableElement> variableElements = map.get(activityName);   // 得到的是 activity对应的控件
            //通过控件的成员变量节点   获取到他的上一个节点 也就是类节点
            TypeElement enclosingElement = (TypeElement)variableElements.get(0).getEnclosingElement();
            // 通过成员变量的到包名
            String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
            try {
                JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");

                writer = sourceFile.openWriter();
                writer.write("package"+packageName+";\n");
                writer.write("import"+packageName+".IBinder;\n");
                writer.write("public class"+activityName+
                        "_ViewBinding implements IBinder<"+packageName+"."+activityName+">{\n");
                writer.write("@Override\n" +
                        "    public void bind("+packageName+"."+activityName+" target) {");

                //遍历所有的成员变量 添加代码
                for (VariableElement variableElement :variableElements) {
                      //获取到控件的名字
                    String variableName = variableElement.getSimpleName().toString();
                    //获取到控件的id
                    int id = variableElement.getAnnotation(BindView.class).value();
                    //获取到这个控件的类型
                    TypeMirror typeMirror = variableElement.asType();
                    writer.write("target."+variableName+"=("+typeMirror+")target.findViewById("+id+");\n");
                }

                writer.write("}\n}\n");

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if (writer!=null){
                    try {
                        writer.flush();
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }

        }
    }


    return false;
}

}

annotations下面的类

package com.example.annotations;

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

@Target(ElementType.FIELD) // 声明该注解是作用在什么上面的 也就是作用域      这个是属性的
@Retention(RetentionPolicy.SOURCE) //声明定义的注解声明周期 这边是源码期       源码时期  编译器    运行期   java-->class -->run
public @interface BindView {
int value();
}

模仿ButterKnife

package com.example.myapplication;

import android.app.Activity;

public class ButterKnife {
public static void bind(Activity activity){
    String name = activity.getClass().getName()+"_ViewBinding";

    try {
        Class<?> aClass = Class.forName(name);
        IBinder iBinder  = (IBinder)aClass.newInstance();
        iBinder.bind(activity);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

具体用法

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.example.annotations.BindView;

public class MainActivity extends AppCompatActivity {
@BindView(R.id.textview)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ButterKnife.bind(this);
    setContentView(R.layout.activity_main);
}
}





----记得添加依赖 ————————
  annotationProcessor project(path: ':annotation_compiler')
 implementation project(path: ':annotations')

感觉有帮助朋友可以随机打赏喔  谢谢   

 

发布了8 篇原创文章 · 获赞 3 · 访问量 973

猜你喜欢

转载自blog.csdn.net/flying0916/article/details/98390628