先从SPI谈起
SPI: Service Provider Interfaces即Service提供者接口.
ServiceLoader类是在java上的SPI实现,使用时在java工程resources资源目录下创建META-INF/services文件夹,在services文件夹中创建以接口全名命名的文件,文件内容为该接口实现类全名,基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定,方便快捷。举个栗子:
//形状接口
public interface Shape {
String introduce(); //介绍
}
//实现类一
public class Circle implements Shape {
public String introduce() {
return "圆形"; //言简意赅的介绍
}
}
//实现类二
public class Sequare implements Shape {
static{
System.out.println("【Sequare】据说有延时加载,try it..");
}
public String introduce() {
return "方形";
}
}
文件位置
- src
-main
-resources
- META-INF
- services
- xxxpackage.Shape
文件名:包名.接口名
文件内容:包名.接口实现类,换行符分隔
xxxpackage.Circle
xxxpackage.Sequare
使用
ServiceLoader shapeLoader = ServiceLoader.load(Shape.class);
Iterator it = shapeLoader.iterator();
while(it.hasNext()){
System.out.println("Iterator next()方法调用..");
Shape shape = it.next();
System.out.printf("what's shape?%s\n",shape.introduce());
}
这不是动态代理吗???再想想,跟Spring框架的IOC控制反转是否一样?Spring容器通过xml配置方式实例化接口实现类对象,如出一辙.
AutoService
Android上的SPI实现.这是一个注解处理器,是Google开发的,用来生成META-INF/services/javax.annotation.processing.Processor文件的。
compile 'com.google.auto.service:auto-service:1.0-rc3'
AutoService会自动在META-INF文件夹下生成Processor配置信息文件,该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
APT技术
APT(Annotation Process Tool),是一种在代码编译时处理注解,按照一定的规则,生成相应的java文件,多用于对自定义注解的处理,目前比较流行的Dagger2, ButterKnife, EventBus3都是采用APT技术,对运行时的性能影响很小。
这里需要强调的是,ButterKnife之所以比xutils高效,是因为xutils使用了反射的方式去绑定View,而APT(编译时注解)比反射的性能要高
分析ButterKnife架构
共有三部分构成
- butterknife-annotations(java工程,注解)
- butterknife-compiler(java工程,用于生成代码)
- butterknife(Android工程,实例化XX_ViewBinding对象)
代码
- butterknife-annotations
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value();
}
- butterknife-compiler
//Android上的SPI实现.这是一个注解处理器,是Google开发的,用来生成META-INF/services/javax.annotation.processing.Processor文件的。
@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();
}
// 1. 指定处理的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 2. 给到需要处理的注解
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
types.add(BindView.class.getCanonicalName());
return types;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// System.out.println("------------------------>");
// process 方法代表的是,有注解就都会进来
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
/*for (Element element : elements) {
//有注解的属性所在的activity
Element enclosingElement = element.getEnclosingElement();
//有注解的属性名
System.out.println("------------------------>"+element.getSimpleName().toString()+" "+enclosingElement.getSimpleName().toString());
}*/
// 解析 属性 activity -> List
Map> elementsMap = new LinkedHashMap<>();
for (Element element : elements) {
//有注解的属性所在的activity
Element enclosingElement = element.getEnclosingElement();
//有 //activity中有注解的属性集合
List viewBindElements = elementsMap.get(enclosingElement);
if (viewBindElements == null) {
viewBindElements = new ArrayList<>();
elementsMap.put(enclosingElement, viewBindElements);
}
viewBindElements.add(element);
}
// 生成代码
for (Map.Entry> entry : elementsMap.entrySet()) {
Element enclosingElement = entry.getKey();
List viewBindElements = entry.getValue();
// public final class xxxActivity_ViewBinding implements Unbinder
String activityClassNameStr = enclosingElement.getSimpleName().toString();
ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);
ClassName unbinderClassName = ClassName.get("com.justin.butterknife","Unbinder");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr+"_ViewBinding")
.addModifiers(Modifier.FINAL,Modifier.PUBLIC).addSuperinterface(unbinderClassName)
.addField(activityClassName,"target",Modifier.PRIVATE);
// 实现 unbind 方法
// android.support.annotation.CallSuper
ClassName callSuperClassName = ClassName.get("android.support.annotation","CallSuper");
MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addAnnotation(callSuperClassName);
unbindMethodBuilder.addStatement("$T target = this.target",activityClassName);
unbindMethodBuilder.addStatement("if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");");
// 构造函数
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(activityClassName,"target");
// this.target = target;
constructorMethodBuilder.addStatement("this.target = target");
// findViewById 属性
for (Element viewBindElement : viewBindElements) {
// target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
// target.textView1 = Utils.findViewById(source, R.id.tv1);
String filedName = viewBindElement.getSimpleName().toString();
ClassName utilsClassName = ClassName.get("com.justin.butterknife","Utils");
int resId = viewBindElement.getAnnotation(BindView.class).value();
constructorMethodBuilder.addStatement("target.$L = $T.findViewById(target, $L)",filedName,utilsClassName,resId);
// target.textView1 = null;
unbindMethodBuilder.addStatement("target.$L = null",filedName);
}
classBuilder.addMethod(unbindMethodBuilder.build());
classBuilder.addMethod(constructorMethodBuilder.build());
// 生成类,看下效果
try {
//获取包名
String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
JavaFile.builder(packageName,classBuilder.build())
.addFileComment("butterknife 自动生成")
.build().writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
System.out.println("异常!");
}
}
return false;
}
}
apply plugin: 'java-library'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//Android上的SPI实现.这是一个注解处理器,是Google开发的,用来生成META-INF/services/javax.annotation.processing.Processor文件的。
implementation 'com.google.auto.service:auto-service:1.0-rc3'
//动态生成java代码
implementation 'com.squareup:javapoet:1.9.0'
implementation project(':butterknife-annotations')
}
tasks.withType(JavaCompile){
options.encoding='UTF-8'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
- butterknife
/**
* 实例化xxxActivity_ViewBinding
*/
public class ButterKnife {
public static Unbinder bind(Activity activity) {
// xxxActivity_ViewBinding viewBinding = new xxxActivity_ViewBinding(this);
try {
Class extends Unbinder> bindClassName = (Class extends Unbinder>)
Class.forName(activity.getClass().getName() + "_ViewBinding");
// 构造函数
Constructor extends Unbinder> bindConstructor = bindClassName.getDeclaredConstructor(activity.getClass());
Unbinder unbinder = bindConstructor.newInstance(activity);
// 返回 Unbinder
return unbinder;
} catch (Exception e) {
e.printStackTrace();
}
return Unbinder.EMPTY;
}
}
public interface Unbinder {
@UiThread
void unbind();
Unbinder EMPTY = new Unbinder() {
@Override
public void unbind() {
}
};
}
public class Utils {
public static T findViewById(Activity activity,int viewId){
return (T) activity.findViewById(viewId);
}
}
使用
implementation project(':butterknife-annotations')
annotationProcessor project(':butterknife-compiler')
implementation project(path: ':butterknife')
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
textView.setText("5555555555555555");
}
}
代码生成在主工程app\build\generated\source\apt\debug\com\justin\utils目录下
public final class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
public MainActivity_ViewBinding(MainActivity target) {
this.target = target;
target.textView = Utils.findViewById(target, 2131165325);
}
@Override
@CallSuper
public final void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");;
target.textView = null;
}
}
坑
在 classpath 'com.android.tools.build:gradle:3.4.0'下,始终无法生成代码, public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) 方法进不去,不知道为何?