主要是解决掉 findViewById 和 setOnclick ,还包括资源的注入 ,编译时注解。java
主要采用编译时注解,就是用 apt 生成代码android
注解处理器是(Annotation Processor)是javac的一个工具,用来在编译 时扫描注解(Annotation)。bash
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;
}
}
复制代码
代码 ButterKnife ViewBind 为例子app
app主工程
ButterKnife分为3个module
butterknife-annotation(定义注解)
butterknife-complier(AbstractProcessor 处理器代码)
butterknife(ButterKnife.bind 代码)ide
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以下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.javaui
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_ViewBindingthis
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();
}
复制代码