上篇聊完中级架构选型,这篇进入滴滴高级面经。高级岗要理解源码——RxJava底层原理、Dagger2代码生成、R8/ProGuard混淆、多渠道构建。
今天8道题覆盖滴滴高级面试核心考点。
Q1:RxJava的Observable和Observer怎么配合工作?
核心流程:Observable.create创建Observable(内部持有ObservableOnSubscribe)→ subscribe时调用ObservableOnSubscribe.subscribe,传入Observer → ObservableOnSubscribe内部调用emitter.onNext/onComplete → emitter把事件传递给Observer的对应方法。
操作符链:map操作符创建ObservableMap,subscribe时把上游Observer包装成MapObserver(onNext里调用function转换后传给下游)。flatMap类似但返回Observable,内部合并多个流。
背压:上游发送速度>下游消费速度时的处理策略。Flowable支持背压(BUFFER/DROP/LATEST),Observable不支持(适合UI事件)。
追问:RxJava和Flow怎么选?新项目推荐Flow(协程原生支持、语法更简洁)。老项目有RxJava不需要迁移。Flow的背压通过buffer/conflate/collectLatest处理。
Q2:RxJava的Scheduler线程切换原理?
observeOn:切换下游Observer执行的线程。内部用Worker.schedule把事件post到目标线程队列。多次observeOn可多次切换。
subscribeOn:指定subscribe(上游数据发射)的线程。只有第一次有效(最内层subscribeOn决定实际线程)。
常见Scheduler:Schedulers.io(IO线程池,适合网络/文件IO)、Schedulers.computation(CPU密集,线程数=CPU核数)、AndroidSchedulers.mainThread(主线程)、Schedulers.single(单线程串行)。
追问:subscribeOn(io).observeOn(mainThread)流程?subscribe在io线程执行,数据经操作符链处理后,observeOn把onNext/onComplete切换到主线程。
Q3:Dagger2的代码生成原理是什么?
APT:编译时扫描注解(@Inject、@Module、@Component),生成Java源代码(用JavaPoet)。
生成内容:每个@Component生成DaggerXxxComponent类,包含@Provides方法的Provider包装、注入工厂方法、@Inject构造函数的Factory类。
注入过程:DaggerAppComponent.create().inject(activity) → Component找到Activity的MembersInjector → 调用@Provides获取依赖 → 赋值给@Inject字段。
追问:@Binds和@Provides区别?@Provides需要方法体创建实例。@Binds是抽象方法,用于接口绑定(告诉Dagger用哪个实现类实现接口),生成代码更简洁。
Q4:R8和ProGuard有什么区别?
ProGuard:只做代码压缩(移除未使用的类/方法/字段)和混淆(重命名类/方法/字段为短名称a/b/c)。不做D8的dex转换。
R8:Google开发,替代ProGuard+D8。一个工具完成:压缩+混淆+dex转换+内联优化+死代码消除。比ProGuard优化更激进。
混淆规则:-keep保留类/方法。常见需保留的:反射调用的类、JNI方法、Parcelable实现、Serializable类。-dontwarn忽略找不到的类引用。
追问:怎么调试混淆问题?-printmapping mapping.txt输出映射文件,崩溃堆栈用mapping.txt还原(ReTrace)。开发阶段minifyEnabled false关闭混淆。
Q5:多渠道构建(Product Flavors)怎么配置?
Product Flavors:build.gradle中定义不同渠道(如free/paid、dev/staging/production)。每个flavor有独立的applicationId、版本号、资源、源代码。
Build Variants:Flavor × BuildType = BuildVariant。如free×debug、paid×release。每个BuildVariant生成独立APK。
差异化配置:每个flavor有独立的src/flavorName/java(源代码)、src/flavorName/res(资源)。相同代码放src/main,差异代码放各自flavor目录。
签名配置:signingConfigs定义签名信息,release BuildType引用signingConfig。不同flavor可用不同签名。
追问:怎么批量打渠道包?Walle(美团开源,通过写入APK的Channel ID区分渠道,不需要重新编译)、友盟多渠道打包(在APK的META-INF写入渠道标识)。比Gradle Product Flavors快得多(适合上百个渠道)。
Q6:Gradle构建优化怎么做?
构建速度优化:Gradle Daemon(org.gradle.daemon=true,常驻进程避免JVM启动开销)、并行构建(org.gradle.parallel=true)、构建缓存(org.gradle.caching=true)。
依赖管理:implementation替代compile(限制依赖传递范围)。使用BOM统一版本。避免动态版本号(+导致每次检查更新)。
模块化:大项目拆成多个module,利用增量编译(只重编修改的module)。每个module独立编译,CI可并行。
追问:Gradle Kotlin DSL和Groovy DSL区别?Kotlin DSL(build.gradle.kts)有IDE自动补全和类型检查,语法更严格。Groovy DSL(build.gradle)语法灵活但IDE支持差。新项目推荐Kotlin DSL。
Q7:AGP(Android Gradle Plugin)的Transform API是什么?
Transform API:在编译流程中拦截class文件,在dex之前对class进行字节码操作。常用于AOP、方法插桩、性能监控。
工作流程:编译Java/Kotlin → Transform处理class → D8/R8转换dex → 打包APK。Transform可读取和修改所有class文件。
常见应用:方法耗时插桩、自动埋点、防重复点击。
注意:AGP 7.0+ Transform API已废弃,替代方案是ASM Instrumentation API(registerAsmClassVisitorFactory注册ASM Visitor,更轻量)。
追问:ASM和Javassist区别?ASM直接操作字节码(更底层、性能更好、但API复杂)。Javassist操作Java源代码级别(API简单、但性能差)。Android字节码操作推荐ASM。
Q8:滴滴高级Android面怎么准备?
重点复习:RxJava源码(操作符链+线程切换+背压)、Dagger2源码(APT代码生成)、R8/ProGuard(混淆规则)、Gradle构建(多渠道+优化)、Transform/ASM字节码操作。
面试技巧:高级岗考源码理解——能讲清底层实现原理。比如"RxJava操作符链怎么传递事件"、"Dagger2编译时生成了什么代码"。准备时读源码关键路径。
面试Tips:滴滴高级面考源码深度——RxJava源码和Dagger2源码是必考题。R8混淆和Gradle构建优化也高频。字节码操作(Transform/ASM)经常问。能讲清源码关键路径加分明显。
下一篇进入滴滴资深Android——组件化实践、热修复、路由框架、监控体系。
读过RxJava源码的同学扣1,你觉得最难理解的操作符是哪个?
本系列连载中,关注不迷路,下一篇:滴滴资深Android工程师面试真题
系列简介:Android大厂面经连载,覆盖字节跳动、腾讯、阿里、美团等40+企业,从初级到架构师全岗位覆盖。每篇文章包含真实面试题+详细答案+代码示例,帮你拿到大厂Offer。