寫在前面
由于文章和代碼寫的比較久了
再次翻看閱讀,打開(kāi)工程運(yùn)行的時(shí)候,發(fā)現(xiàn)注解處理器不生效
浪費(fèi)了很多時(shí)間才搞清楚問(wèn)題所在,所以先記錄一下
之前使用的是
classpath 'com.android.tools.build:gradle:3.0.0'
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
由于AndroidStudio更新,再次clone代碼后,工程自動(dòng)更新配置
gradle 5.4.1版本,android tools3.5版本,發(fā)現(xiàn)注解處理器不生效
對(duì)比了ButterKnife 源碼的配置,發(fā)現(xiàn)還是使用的gradle4.10.3,android tools 3.4
索性降低版本試了下,發(fā)現(xiàn)是可以的
最終定位問(wèn)題原因:在Gradle 5.0將忽略compile classpath中的annotation processor,
需要手動(dòng)添加到annotation processor path
implementation 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
ButterKnife優(yōu)勢(shì)
Android之神Jake Wharton寫的ButterKnife
ButterKnife是一個(gè)應(yīng)用于Android系統(tǒng)的View注入框架
可以減少大量的findViewById以及setOnClickListener代碼
優(yōu)勢(shì):
- 代碼清晰,可讀性強(qiáng)
- 簡(jiǎn)化Adapter中ViewHolder綁定
- 運(yùn)行時(shí)不會(huì)影響APP效率,使用配置方便
- View綁定和Click事件處理功能,簡(jiǎn)化代碼,提高開(kāi)發(fā)效率
具體用法就不做介紹了,參考官方使用即可
可能有人會(huì)對(duì)第三點(diǎn)有疑問(wèn),覺(jué)得使用了反射,影響了效率
其實(shí)看了ButterKnife的源碼就知道
反射只用在了創(chuàng)建XXXActivity_ViewBinding對(duì)象時(shí)
ButterKnife注解是編譯時(shí)注解并非運(yùn)行時(shí)注解
ButterKnife框架使用到的技術(shù)
- 自定義注解
- APT(Annotation Process Tool) 注解處理器
- javapoet自動(dòng)生成代碼
自定義注解
參考鏈接:https://blog.csdn.net/kaifa1321/article/details/79622715
APT
APT(Annotation Process Tool),是一種在代碼編譯時(shí)處理注解,按照一定的規(guī)則,生成相應(yīng)的java文件
多用于對(duì)自定義注解的處理,目前比較流行的Dagger2, EventBus3,包括 ButterKnife,都是采用APT技術(shù)
對(duì)運(yùn)行時(shí)的性能影響很小
Androidstudio配置 APT

android module : app, butterknife
java module : butterknife-compiler, butterknife-annotations
app 依賴butterknife
butterknife 依賴 java工程 butterknife-compiler
butterknife-annotations 里面就是放了自定義注解
工程的 build.gradle
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
//新版本的Androidstudio 已經(jīng)不用這么配置了
//classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
java-library butterknife-compiler的build.gradle
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.auto.service:auto-service:1.0-rc3'
compile 'com.squareup:javapoet:1.9.0'
compile project(path: ':butterknife-annotations')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
自定義注解 BindView 標(biāo)記屬性
/**
* 編譯時(shí)注解 作用在屬性上 BindView
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value();
}
MainActivity 使用注解 BindView
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView mTextView1;
@BindView(R.id.tv2)
TextView mTextView2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mTextView1.setText("hello android");
mTextView2.setText("hello Python");
}
}
ButterKnife的bind方法
public class ButterKnife {
public static UnBinder bind(Activity activity) {
//獲取activity的Class
Class<?> activityClass = activity.getClass();
//獲取activity的全路徑名
String activityName = activityClass.getName();
//拼接className
String className = activityName + "_ViewBinding";
try {
//獲取生成類的Class
Class clazz = Class.forName(className);
//獲取構(gòu)造函數(shù)
Constructor constructor = clazz.getDeclaredConstructor(activityClass);
//創(chuàng)建生成類的對(duì)象
UnBinder unBinder = (UnBinder) constructor.newInstance(activity);
return unBinder;
} catch (Exception e) {
e.printStackTrace();
}
//如果出錯(cuò)返回EMPTY
return UnBinder.EMPTY;
}
}
activityName + "_ViewBinding這個(gè)類長(zhǎng)什么樣呢?
在app的build目錄下\build\generated\source\apt\debug\com\qingguoguo\followbutterknife
public final class MainActivity_ViewBinding implements UnBinder {
private MainActivity target;
public MainActivity_ViewBinding(MainActivity target) {
this.target = target;
this.target.mTextView1 = Utils.findViewById(target,2131165315);
this.target.mTextView2 = Utils.findViewById(target,2131165316);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target.mTextView1 = null;
this.target.mTextView2 = null;
this.target = null;
}
}
MainActivity_ViewBinding 怎么生成的
封裝 findViewById方法
public class Utils {
//封裝 findViewById方法
public static <T extends View> T findViewById(Activity activity, int id) {
return (T) activity.findViewById(id);
}
}
生成代碼
//@AutoService(Processor.class)這個(gè)注解一定要
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
private Filer mFile;
private Elements mElementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//初始化 java文件生成的位置
mFile = processingEnvironment.getFiler();
mElementUtils = processingEnvironment.getElementUtils();
}
/**
* 用來(lái)指定支持的 SourceVersion
*
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 用來(lái)指定支持的 AnnotationTypes
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
/**
* 參考 ButterKnife 的寫法
*
* @return
*/
private Set<Class<? extends Annotation>> getSupportedAnnotations() {
Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
//BindView目前只寫了這個(gè)注解
annotations.add(BindView.class);
return annotations;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 解析注解
HashMap<Element, List<Element>> elementMap = new HashMap<>(16);
//包含BindView注解的Set集合
Set<? extends Element> elementSet = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element element : elementSet) {
System.out.println("----" + element.getSimpleName() + "-----" + element.getEnclosingElement());
/**
*比較亂 并沒(méi)有給我們包裹好,需要自己組合,把一個(gè)類的所有屬性集合到一起
*----mTextView1-----com.qingguoguo.followbutterknife.LoginActivity
*----mTextView2-----com.qingguoguo.followbutterknife.LoginActivity
*----mTextView1-----com.qingguoguo.followbutterknife.MainActivity
*----mTextView2-----com.qingguoguo.followbutterknife.MainActivity
*/
//拿到類名
Element enclosingElement = element.getEnclosingElement();
//Map中查找是否已經(jīng)存了該類
List<Element> elements = elementMap.get(enclosingElement);
if (elements == null) {
//沒(méi)有找到,創(chuàng)建集合
elements = new ArrayList<>();
elementMap.put(enclosingElement, elements);
}
//把該屬性加到集合
elements.add(element);
}
System.out.println("----" + elementMap + "-----");
//生成代碼,遍歷Map集合
Set<Map.Entry<Element, List<Element>>> entries = elementMap.entrySet();
for (Map.Entry<Element, List<Element>> entry : entries) {
Element element = entry.getKey();
//父類
ClassName unBinderClassName = ClassName.get("com.qingguoguo.butterknife", "UnBinder");
//生成類
//activity Class Name
String activityClassNameStr = element.getSimpleName().toString();
ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);
TypeSpec.Builder activityBuilder = TypeSpec.classBuilder(activityClassNameStr + "_ViewBinding")
.addModifiers(Modifier.FINAL, Modifier.PUBLIC);
//---------添加父接口-----------------//
activityBuilder.addSuperinterface(unBinderClassName);
//---------添加屬性 private XXXActivity target ;-------//
activityBuilder.addField(activityClassName, "target", Modifier.PRIVATE);
//---------添加構(gòu)造方法 public xxx_ViewBinding(xxx target) -------------------//
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder();
constructorMethodBuilder.addParameter(activityClassName, "target").addModifiers(Modifier.PUBLIC);
constructorMethodBuilder.addStatement("this.target = target");
//-------------------添加實(shí)現(xiàn)接口UnBinder 要重寫的unbind方法----------//
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.\")");
//-------------------給類中帶有注解的屬性賦值----------------------//
List<Element> elementList = entry.getValue();
for (Element viewBindIdElement : elementList) {
String filedName = viewBindIdElement.getSimpleName().toString();
int id = viewBindIdElement.getAnnotation(BindView.class).value();
ClassName utilsClassName = ClassName.get("com.qingguoguo.butterknife",
"Utils");
constructorMethodBuilder.addStatement("this.target.$L = $T.findViewById(target,$L)", filedName, utilsClassName, id);
unbindMethodBuilder.addStatement("this.target.$L = null", filedName);
}
unbindMethodBuilder.addStatement("this.target = null");
activityBuilder.addMethod(unbindMethodBuilder.build());
activityBuilder.addMethod(constructorMethodBuilder.build());
//-----------------包名----------------------//
String packageName = mElementUtils.getPackageOf(element.getEnclosingElement()).getQualifiedName().toString();
//----------------生成Java文件----------------//
try {
JavaFile.builder(packageName,
activityBuilder.build()).addFileComment("仿ButterKnife自動(dòng)生成").build().writeTo(mFile);
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
}
斷點(diǎn)調(diào)試
剛開(kāi)始接觸APT不太熟悉的時(shí)候,肯定寫出來(lái)有很多問(wèn)題
如果你希望斷點(diǎn)調(diào)試,按照以往的經(jīng)驗(yàn)發(fā)現(xiàn)不起作用
因?yàn)榫幾g時(shí)在單獨(dú)的JVM里面執(zhí)行的,所以需要建立遠(yuǎn)程調(diào)試
- 在項(xiàng)目的根目錄下gradle.properties 文件中加入如下兩條語(yǔ)句
org.gradle.jvmargs= -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
org.gradle.parallel=true
-
run->Edit Config
編輯配置 -
創(chuàng)建遠(yuǎn)程Remote調(diào)試配置參數(shù)
Remote調(diào)試.png -
點(diǎn)擊剛剛創(chuàng)建的配置,debug運(yùn)行
debug運(yùn)行 -
rebuild
rebuild
參考鏈接:https://blog.csdn.net/hongxue8888/article/details/99710884



