主要是解決掉 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();
}
複製代碼