P5初级聊完,进入P6中级。P6是阿里主力级别,面试难度跳一档。P5考"知不知道原理",P6考"能不能讲清源码"。设计模式要结合项目应用,ARouter和EventBus要能画源码级流程图。
P6面试最大坑:只说"是什么"不说"为什么"。问ARouter路由原理,"编译期APT生成路由表"是P5的回答。P6要答路由表怎么加载、首次加载怎么优化、拦截器怎么实现。今天8道题给到源码级深度。
Q1:Android常用设计模式?项目里怎么用?
单例模式:全局唯一实例。AppManager管理Activity栈、网络库getInstance。DCL+volatile防止指令重排。
观察者模式:一对多依赖。LiveData、EventBus、RxJava本质都是观察者。网络状态变化通知多页面更新。
建造者模式:复杂对象分步构建。Dialog.Builder、OkHttpClient.Builder。链式调用可读性好,参数校验集中在build()。
适配器模式:接口转换。RecyclerView.Adapter适配数据到ViewHolder。HeaderViewAdapter在原始Adapter外包一层添加Header。
追问:MVP/MVVM属于什么模式?不是GoF的23种之一。MVVM结合了观察者模式(数据绑定)+ 中介者模式(ViewModel协调View和Model)。架构模式是多种设计模式的组合应用。
Q2:ARouter路由原理?编译期和运行时分别做了什么?
编译期:APT扫描所有@Route注解的类,为每个模块生成路由表文件(ARouterGroupGroupxxx)。文件里是HashMap结构,key是路由路径,value是目标Class。
运行时:
- ARouter.init()扫描所有dex中的路由表文件(反射加载Group类)
- 加载到内存的路由树(按Group分组懒加载,首次访问某Group才加载)
- ARouter.build("/app/main").navigation()从路由树查找目标Class,反射实例化后startActivity
性能优化:首次全量扫描dex耗时长(大项目3-5秒)。ARouter用dex分组加载+懒加载分摊。也可以在编译期生成索引文件(arouter-gradle-plugin)跳过运行时扫描。
Q3:ARouter的拦截器(Interceptor)怎么实现?
拦截器链:多个Interceptor按priority排序,依次执行。每个Interceptor的process(postcard, callback)里决定是否放行。callback.onContinue(postcard)继续,callback.onInterrupt()中断导航。
典型场景:登录拦截——未登录时onInterrupt跳登录页,登录成功后重新导航到目标页。权限检查、AB实验分流也用拦截器。
实现原理:InterceptorChain内部维护index计数器,每次onContinue index+1取下一个Interceptor执行。全部执行完才真正导航。
追问:拦截器是同步还是异步的?process方法本身同步执行,但可以在里面做异步操作(弹Dialog等用户操作)。异步操作完再调onContinue/onInterrupt——这就是为什么Interceptor的callback是异步设计。
Q4:ButterKnife原理?编译期做了什么?
编译期:APT扫描所有@BindView/@OnClick注解,为每个Activity/Fragment/View生成一个XXX_ViewBinding类。这个类里有findViewById的赋值代码和onClick的设置代码。
运行时:ButterKnife.bind(this)反射找到XXX_ViewBinding类,调构造函数执行里面的findViewById。
为什么用ButterKnife而不是手写findViewById:代码简洁(注解代替重复的findViewById)、编译期检查(注解的id不存在编译就报错)。
现在还用吗:新项目推荐ViewBinding(Google官方)——纯编译期无反射,性能比ButterKnife好。ButterKnife作者Jake Wharton也推荐迁移到ViewBinding了。
Q5:EventBus的三种线程模式?底层怎么分发事件?
POSTING(默认):事件在发送线程处理。MAIN:事件在主线程处理,子线程发送则post到Handler。BACKGROUND:事件在子线程处理。ASYNC:总在独立子线程处理,适合耗时操作。
底层分发:post(event)放入ThreadLocal队列 → postSingleEvent查找所有@Subscribe方法 → 根据ThreadMode决定直接invoke还是post到Handler/线程池。
Q6:EventBus的索引加速(Index)是什么?
问题:EventBus默认在运行时用反射扫描类的所有方法找@Subscribe注解。大项目类多时首次注册要几百毫秒。
EventBusIndex:编译期APT扫描所有@Subscribe方法,生成一个索引类(MyEventBusIndex),里面是类→订阅方法列表的映射。运行时直接查索引不走反射。
使用:EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus()。首次注册从几百毫秒降到几毫秒。
追问:EventBus和LiveData/Flow怎么选?EventBus是全局事件总线(跨组件通信),LiveData/Flow是局部数据流(ViewModel→View)。跨模块通信用EventBus(配合Index),同模块内数据流用LiveData/Flow。
Q7:Android中的责任链模式?实际应用?
责任链:请求沿链传递,每个处理者决定处理或传给下一个。
OkHttp拦截器链:经典实现。请求从RetryAndFollowUpInterceptor开始,经过BridgeInterceptor→CacheInterceptor→ConnectInterceptor→CallServerInterceptor,每个拦截器可以修改请求/响应或短路返回。
View事件分发:dispatchTouchEvent从Activity→Window→ViewGroup→View逐层传递,每层的onInterceptTouchEvent决定是否拦截。
自定义场景:表单校验链——每个Validator校验一个字段,不通过就中断不往下传。权限请求链——依次请求多个权限,一个拒绝就中断。
Q8:Android中的策略模式?实际应用?
策略模式:定义一系列算法,运行时选择一个。各策略实现同一接口,可互相替换。
实际应用:图片加载策略——Glide/Fresco实现统一ImageLoader接口,方便切换和Mock测试。缓存策略——LruCache/DiskLruCache/NetworkCache实现统一CacheStrategy,组合使用。登录策略——手机号/微信/QQ各自实现LoginStrategy接口。
追问:策略模式和状态模式区别?结构相似但意图不同——策略模式是"选择一种算法"(主动选择),状态模式是"根据状态自动切换行为"(被动切换)。Context在策略模式里不知道当前用的哪个策略,在状态模式里状态会自动转换。
面试Tips:P6面试设计模式要能结合项目说出实际应用——不是背定义而是"我在XX场景用了XX模式解决了XX问题"。ARouter考编译期APT生成+运行时加载的完整流程。EventBus考线程模式和Index加速。ButterKnife现在可以不深究但要能对比ViewBinding的优劣。P6还考代码review能力——面试官给一段有问题的代码你能指出哪里设计不合理。
下一篇是阿里P7高级Android面经。Java并发编程深度题、JVM调优、volatile底层原理——P7考的是Java功底。
项目里用过设计模式的同学评论区聊聊,你用得最多的是哪种模式?
本系列连载中,关注不迷路,下一篇:阿里P7高级Android工程师面试真题
系列简介:Android大厂面经连载,覆盖字节跳动、腾讯、阿里、美团等40+企业,从初级到架构师全岗位覆盖。每篇文章包含真实面试题+详细答案+代码示例,帮你拿到大厂Offer。