Android编译期插桩,让程序自己写代码(一)

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}

@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnv.getElementUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
typeUtils = processingEnv.getTypeUtils();
}

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}

auto-service为我们简化了定义注解处理器的流程。@AutoService是就是由auto-service提供的,其作用是用来告诉编译器我们定义的ButterKnifeProcessor是一个编译期注解处理器。这样在编译时ButterKnifeProcessor才会被调用。

我们还重写了AbstractProcessor提供的四个方法:getSupportedAnnotationTypesgetSupportedSourceVersioninitprocess

  • getSupportedAnnotationTypes表示处理器可以处理哪些注解。这里返回的是我们之前定义的BindView。除了重写方法之外,还可用通过注解来实现。

@SupportedAnnotationTypes(value = {“me.zhangkuo.apt.annotation.BindView”})

  • getSupportedSourceVersion表示处理器可以处理的Java版本。这里我们采用最新的JDK版本就可以了。同样,我们也可以通过注解来实现。

@SupportedSourceVersion(value = SourceVersion.latestSupported())

  • init方法主要用来做一些准备工作。我们一般在这里初始化几个工具类。上述代码我们初始了与元素相关的工具类elementUtils、与日志相关的工具类messager、与文件相关的filer以及与类型相关工具类typeUtils。我们接下来会看到process主要就是通过这几个类来生成代码的。

  • process用来完成具体的程序写代码功能。在具体介绍process之前,请允许我先推荐一个库:javapoetjavapoet是由神奇的square公司开源的,它提供了非常人性化的api,来帮助开发者生成.java源文件。它的README.md文件为我们提供了丰富的例子,是我们学习的主要工具。

private Map<TypeElement, List> elementPackage = new HashMap<>();
private static final String VIEW_TYPE = “android.view.View”;
private static final String VIEW_BINDER = “me.zhangkuo.apt.ViewBinding”;

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set == null || set.isEmpty()) {
return false;
}
elementPackage.clear();
Set<? extends Element> bindViewElement = roundEnvironment.getElementsAnnotatedWith(BindView.class);
//收集数据放入elementPackage中
collectData(bindViewElement);
//根据elementPackage中的数据生成.java代码
generateCode();
return true;
}

private void collectData(Set<? extends Element> elements){
Iterator<? extends Element> iterable = elements.iterator();
while (iterable.hasNext()) {
Element element = iterable.next();
TypeMirror elementTypeMirror = element.asType();
//判断元素的类型是否是View或者是View的子类型。
TypeMirror viewTypeMirror = elementUtils.getTypeElement(VIEW_TYPE).asType();
if (typeUtils.isSubtype(elementTypeMirror, viewTypeMirror) || typeUtils.isSameType(elementTypeMirror, viewTypeMirror)) {
//找到父元素,这里认为是@BindView标记字段所在的类。
TypeElement parent = (TypeElement) element.getEnclosingElement();
//根据parent不同存储的List中
List parentElements = elementPackage.get(parent);
if (parentElements == null) {
parentElements = new ArrayList<>();
elementPackage.put(parent, parentElements);
}
parentElements.add(element);
}else{
throw new RuntimeException(“错误处理,BindView应该标注在类型是View的字段上”);
}
}
}

private void generateCode(){
Set<Map.Entry<TypeElement,List>> entries = elementPackage.entrySet();
Iterator<Map.Entry<TypeElement,List>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<TypeElement,List> entry = iterator.next();
//类元素
TypeElement parent = entry.getKey();
//当前类元素下,注解了BindView的元素
List elements = entry.getValue();
//通过JavaPoet生成bindView的MethodSpec
MethodSpec methodSpec = generateBindViewMethod(parent,elements);

String packageName = getPackage(parent).getQualifiedName().toString();
ClassName viewBinderInterface = ClassName.get(elementUtils.getTypeElement(VIEW_BINDER));
String className = parent.getQualifiedName().toString().substring(
packageName.length() + 1).replace(‘.’, ‘$’);
ClassName bindingClassName = ClassName.get(packageName, className + “_ViewBinding”);

try {
//生成 className_ViewBinding.java文件
JavaFile.builder(packageName, TypeSpec.classBuilder(bindingClassName)
.addModifiers(PUBLIC)
.addSuperinterface(viewBinderInterface)
.addMethod(methodSpec)
.build()
).build().writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}

private MethodSpec generateBindViewMethod(TypeElement parent,List elementList) {
ParameterSpec.Builder parameter = ParameterSpec.builder(TypeName.OBJECT, “target”);
MethodSpec.Builder bindViewMethod = MethodSpec.methodBuilder(“bindView”);
bindViewMethod.addParameter(parameter.build());
bindViewMethod.addModifiers(Modifier.PUBLIC);
bindViewMethod.addStatement(“ T t e m p = ( T temp = ( Ttemp=(T)target”,parent,parent);
for (Element element :
elementList) {
int id = element.getAnnotation(BindView.class).value();
bindViewMethod.addStatement(“temp. N = t e m p . f i n d V i e w B y I d ( N = temp.findViewById( N=temp.findViewById(L)”, element.getSimpleName().toString(), id);
}

return bindViewMethod.build();
}

process的代码比较长,但是它的逻辑非常简单看,主要分为收集数据和生成代码两部分。我为关键的地方都加了注释,就不再详细解释了。到这里我们基本上完成了注解器的编写工作。

  1. 使用注解

在build.gradle中引入我们定义的注解和注解处理器。

implementation project(‘:apt_api’)
annotationProcessor project(“:apt_processor”)

应用注解

public class MainActivity extends AppCompatActivity {

@BindView(R.id.tv_content)
TextView tvContent;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);

tvContent.setText(“这就是ButterKnife的原理”);
}
}

到这里,这篇文件就结束了。什么?你还没说ButterKnife这个类呢。好吧,这个真的很简单,直接贴代码吧。

public class ButterKnife {
static final Map<Class<?>, Constructor<? extends ViewBinding>> BINDINGS = new LinkedHashMap<>();

public static void inject(Object object) {
if (object == null) {
return;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

上述【高清技术脑图】以及【配套的架构技术PDF】点击:Android架构视频+BAT面试专题PDF+学习笔记,或者私信回复【技能提升】即可获取!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!

水,赶快领取吧!

上述【高清技术脑图】以及【配套的架构技术PDF】点击:Android架构视频+BAT面试专题PDF+学习笔记,或者私信回复【技能提升】即可获取!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!

Android架构师之路很漫长,一起共勉吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值