学习目标:
Android编译时技术,仿照ButterKnife,实现自己的自动注入框架学习内容:
提示:这里可以添加要学的内容
1、 首先我们肯定要知道怎么使用ButterKnife
2、 了解ButterKnife
3、 打造自己的ButterKnife
学习开始:
ps:注释记得看
1.ButterKnife使用
想要了解ButterKinfe的详细使用方法,请绕道别的博客,嘿嘿。
我们这里只是简单介绍一下,我们既然想要写一个差不多的框架,那肯定要先来使用一下它,然后我们也要实现它的这种功能。
1.只需在app的 build.gradle 中添加如下代码:
dependencies {
//依赖核心库
implementation 'com.jakewharton:butterknife:8.8.1'
//注解处理器
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
然后在Activity中
@BindView(R.id.tv)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.tv)
public void onViewClicked() {
}
到这里,我们就简单的了解了ButterKnife的使用,感觉是不是非常方便,这是因为ButterKnife采用了编译时技术来实现的。
我们的代码是有生命周期的分别为源码期,编译期,运行期,下面看一张图
所以ButterKinfe就是在编译期通过编译期自动生成一些代码,这样就等于我们写了,所以这是关键。
我们在通过看类,我们使用ButterKnife在编译的时候会生成一些java文件,而这些文件就是实现它的功能主要java文件。我们来看一下生成的文件长啥样。
我的在这个文件目录下,你的也许跟我不一样。
我们来看一下这个ButterKnife帮我们生成的类(MainActivity$$ViewBinder)到底干了什么,代码如下
public class MainActivity$$ViewBinder<T extends MainActivity> implements ViewBinder<T> {
//target就是我们MainActivity
@Override
public Unbinder bind(final Finder finder, final T target, Object source) {
InnerUnbinder unbinder = createUnbinder(target);
View view;
view = finder.findRequiredView(source, 2131165325, "field 'tv' and method 'onViewClicked'");
//这里就是把找到的textView 赋值给target的tv,这里的tv就是MainActivity的变量名称
target.tv = finder.castView(view, 2131165325, "field 'tv'");
unbinder.view2131165325 = view;
//这里就是给这个view设置了监听事件,回调的就是我们MainActivity里的onViewClicked方法
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.onViewClicked();
}
});
return unbinder;
}
protected InnerUnbinder<T> createUnbinder(T target) {
return new InnerUnbinder(target);
}
protected static class InnerUnbinder<T extends MainActivity> implements Unbinder {
private T target;
View view2131165325;
protected InnerUnbinder(T target) {
this.target = target;
}
@Override
public final void unbind() {
if (target == null) throw new IllegalStateException("Bindings already cleared.");
unbind(target);
target = null;
}
protected void unbind(T target) {
view2131165325.setOnClickListener(null);
target.tv = null;
}
}
}
而上面的这个给我们自动生成的类,我们是通过ButterKnife.bind(this);
给MainActivity变量注册了事件,为什么这么说,我们再来看这个bind方法到底干了啥?
........
//看这个target就是MainActivity
public static Unbinder bind(@NonNull Activity target) {
return bind(target, target, Finder.ACTIVITY);
}
static Unbinder bind(@NonNull Object target, @NonNull Object source, @NonNull Finder finder) {
Class<?> targetClass = target.getClass();
try {
if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
//这个是去找相对应生成的类
ViewBinder<Object> viewBinder = findViewBinderForClass(targetClass);
//看这个,就是给我们绑定了事件
return viewBinder.bind(finder, target, source);
} catch (Exception e) {
throw new RuntimeException("Unable to bind views for " + targetClass.getName(), e);
}
}
//这个方法就是去找ButterKnife给我们生成的类
private static ViewBinder<Object> findViewBinderForClass(Class<?> cls)
throws IllegalAccessException, InstantiationException {
ViewBinder<Object> viewBinder = BINDERS.get(cls);
if (viewBinder != null) {
if (debug) Log.d(TAG, "HIT: Cached in view binder map.");
return viewBinder;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return NOP_VIEW_BINDER;
}
try {
//看到这$$ViewBinder后缀没,就是通过这个加载了生成的类。
Class<?> viewBindingClass = Class.forName(clsName + "$$ViewBinder");
//noinspection unchecked
//生成类对象
viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
if (debug) Log.d(TAG, "HIT: Loaded view binder class.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
viewBinder = findViewBinderForClass(cls.getSuperclass());
}
BINDERS.put(cls, viewBinder);
return viewBinder;
}
.........
所以大概流程就是这样,我们接下就来实现一个我们自己的简易ButterKnife,开干。
首先建两个java Library.
MyAnnotation
package com.suyong.myannotation;
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.CLASS)
public @interface MyBindView {
int value();
}
MyAnnotation-Complier
package com.suyong.myannotation_complier;
import com.google.auto.service.AutoService;
import com.suyong.myannotation.MyBindView;
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.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;
/**
* 注解解释器类
**/
@AutoService(Processor.class)
public class AnnotationComplier extends AbstractProcessor {
//处理文件
Filer filer;
//处理log日志
Messager messager;
//初始化
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
//声明这个注解处理器要处理的注解有哪些
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(MyBindView.class.getCanonicalName());
return types;
}
//申明注解处理器支持的java版本
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
//查找被我们注解标记的内容
//VariableElement 变量
//ExecutableElement 方法
//TypeElement 类
//PackageElement 包
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//查找所有被MyBindView注解标记的内容
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(MyBindView.class);
// 类名 这个类里的所有用MyBindView标记的变量
Map<String, List<VariableElement>> map = new HashMap<>();
for (Element element : elementsAnnotatedWith) {
VariableElement variableElement = (VariableElement) element;
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
String activityName = typeElement.getSimpleName().toString();
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);
String packageName = getPackageName(variableElements.get(0));
//生成类的名字
String newActivityName = activityName + "$$MyViewBinder";
try {
JavaFileObject sourceFile = filer.createSourceFile(packageName + "."+newActivityName);
writer = sourceFile.openWriter();
StringBuffer buffer = new StringBuffer();
buffer.append("package " + packageName + ";\n");
buffer.append("import android.view.View;\n");
buffer.append("public class " + newActivityName +"{\n");
buffer.append("public void bind("+packageName+"."+activityName+" target){\n");
for (VariableElement variableElement : variableElements) {
//获取成员变量的名字
String filedName = variableElement.getSimpleName().toString();
//获取成员变量上面的注解
int resId = variableElement.getAnnotation(MyBindView.class).value();
buffer.append("target."+variableElement+"=target.findViewById("+resId+");\n");
}
buffer.append("}\n");
buffer.append("}\n");
writer.write(buffer.toString());
} catch(IOException e){
e.printStackTrace();
}finally{
if(writer!=null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return false;
}
//获取包名
private String getPackageName (VariableElement variableElement){
Element enclosingElement = variableElement.getEnclosingElement();
PackageElement packageOf = processingEnv.getElementUtils().getPackageOf(enclosingElement);
return packageOf.getQualifiedName().toString();
}
}
然后我们需要在app build.gradle下添加如下两句
//依赖注解类
implementation project(path: ':MyAnnotation')
//依赖注解解释器类
annotationProcessor project(path: ':MyAnnotation-Complier')
还要在app模块下建一个MyButterKinfe类
package com.suyong.mybutterkinfe;
import java.lang.reflect.Method;
//这个是为了执行我们上面代码生成的类
public class MyButterKnife {
public static void bind(Object activity){
String name = activity.getClass().getName();
String binderClassName = name + "$$MyViewBinder";
try {
//利用反射
Class<?> aClass = Class.forName(binderClassName);
Object binder = aClass.newInstance();
Method bindMethod = aClass.getDeclaredMethod("bind", activity.getClass());
bindMethod.invoke(binder,activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后我们可以在我的MainActivity里优雅的使用了,如下:
package com.suyong.mybutterkinfe;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.suyong.myannotation.MyBindView;
public class MainActivity extends AppCompatActivity {
@MyBindView(R.id.tv)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyButterKnife.bind(this);
}
}
这里的话呢,我只实现了成员变量的初始化,并没有写那些绑定事件那些东东,其实BindString,Onclick都跟我这个原理、逻辑是一致的,相信聪明的你一定能悟出来。
代码链接