2024年Android最全Android编译期插桩,让程序自己写代码(一),2024年最新android工程师面试题目和答案

本文介绍了Android程序员面试常考知识点,包括Java应用、APP框架、性能优化等内容,提供了一份详细的系统学习资料,强调了知识体系和深度学习的重要性。同时,作者还推荐了ButterKnife和APT框架的使用,并鼓励读者加入技术交流社群以共同成长。
摘要由CSDN通过智能技术生成

结尾

我还总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料分享给大家。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

//TypeElement
public class Test {

//VariableElement
private String name;

//ExecutableElement
private Test(){
}

//ExecutableElement
public void setName(/* TypeParameterElement */ String name) {
this.name = name;
}
}

Test.java用Element树结构描述如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到 setName(String name)ExecutableElement中并没有子节点TypeParameterElement。这是因为TypeParameterElement没有被纳入到Element树中。不过我们可以通过ExecutableElementgetTypeParameters()方法来获取。

此外,再给大家介绍两个Element中十分有用的方法。

public interface Element extends AnnotatedConstruct {
//获取父Element
Element getEnclosingElement();
//获取子Element的集合
List<? extends Element> getEnclosedElements();
}

二、TypeMirror

Element有一个asType()方法用来返回TypeMirrorTypeMirror表示 Java 编程语言中的类型。这些类型包括基本类型、声明类型(类和接口类型)、数组类型、类型变量和 null 类型。还可以表示通配符类型参数、executable 的签名和返回类型,以及对应于包和关键字 void 的伪类型。我们一般用TypeMirror进行类型判断。如下段代码,用来比较元素所描述的类型是否是Activity的子类。

/**

  • 类型相关工具类
    /
    private Types typeUtils;
    /
    *
  • 元素相关的工具类
    */
    private Elements elementUtils;
    private static final String ACTIVITY_TYPE = “android.app.Activity”;

private boolean isSubActivity(Element element){
//获取当前元素的TypeMirror
TypeMirror elementTypeMirror = element.asType();
//通过工具类Elements获取Activity的Element,并转换为TypeMirror
TypeMirror viewTypeMirror = elementUtils.getTypeElement(ACTIVITY_TYPE).asType();
//用工具类typeUtils判断两者间的关系
return typeUtils.isSubtype(elementTypeMirror,viewTypeMirror)
}

三、一个简单的ButterKnife

这一节我们通过编写一个简单的ButterKnife来介绍一下如何编写一个APT框架。APT应该是编译期插桩最简单的一种技术,通过三步就可以完成。

  1. 定义编译期注解。

我们新增一个Java Library Module命名为apt_api,编写注解类BindView。

@Retention(RetentionPolicy.Class)
@Target(ElementType.FIELD)
public @interface BindView {
}

这里简单介绍一下RetentionPolicyRetentionPolicy是一个枚举,它的值有三种:SOURCE、CLASS、RUNTIME。

  • SOURCE:不参与编译,让开发者使用。
  • CLASS:参与编译,运行时不可见。给编译器使用。
  • RUNTIME:参与编译,运行时可见。给编译器和JVM使用。
  1. 定义注解处理器。

同样,我们需要新增一个Java Library Module命名为apt_processor

我们需要引入两个必要的依赖:一个是我们新增的module apt_annotation,另一个是google的com.google.auto.service:auto-service:1.0-rc3(以下简称auto-service)。

implementation project(‘:apt_api’)
api ‘com.google.auto.service:auto-service:1.0-rc3’

新增一个类 ButterKnifeProcessor,继承 AbstractProcessor

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
/**

  • 元素相关的工具类
    /
    private Elements elementUtils;
    /
    *
  • 文件相关的工具类
    /
    private Filer filer;
    /
    *
  • 日志相关的工具类
    /
    private Messager messager;
    /
    *
  • 类型相关工具类
    */
    private Types typeUtils;

@Override
public Set getSupportedAnnotationTypes() {
return Collections.singleton(BindView.class.getCanonicalName());
}

@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();
//类元素

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,也可以分享给身边好友一起学习。

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值