Android 面试题大全(进阶补充版)
本文档为《Android面试题大全》的补充篇,覆盖 **Gradle 工程化、NDK/JNI、音视频、图形渲染、热修复/插件化、安全逆向、跨平台、Framework 深入、Java 并发进阶、大厂场景设计题、HR 面** 等第一份未深入的方向。
目录
-
Java 并发编程进阶(#一java-并发编程进阶)
-
JVM 深入与调优(#二jvm-深入与调优)
-
Kotlin 进阶(#三kotlin-进阶)
-
Gradle 构建与工程化(#四gradle-构建与工程化)
-
NDK / JNI / C++ 基础(#五ndk--jni--c-基础)
-
音视频开发(#六音视频开发)
-
图形渲染系统(#七图形渲染系统)
-
热修复 / 插件化 / ClassLoader(#八热修复--插件化--classloader)
-
安全与逆向(#九安全与逆向)
-
动画体系(#十动画体系)
-
WebView 深入(#十一webview-深入)
-
跨平台技术(#十二跨平台技术)
-
Framework 深入(#十三framework-深入)
-
蓝牙 / 传感器 / 定位(#十四蓝牙--传感器--定位)
-
测试与 CI/CD(#十五测试与-ci-cd)
-
操作系统与网络进阶(#十六操作系统与网络进阶)
-
Compose 进阶(#十七compose-进阶)
-
大厂场景设计题(#十八大厂场景设计题)
-
HR 面与软技能(#十九hr-面与软技能)
一、Java 并发编程进阶
- 什么是 AQS(AbstractQueuedSynchronizer)?
-
JUC 的核心框架,用一个 volatile int state + CLH 双向队列实现同步。
-
获取失败封装为 Node 入队,park 阻塞;释放时 unpark 唤醒后继节点。
-
ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock 都基于 AQS。
-
模板方法模式:子类只需实现 tryAcquire/tryRelease。
- CAS 原理与问题?
-
Compare And Swap,CPU 原子指令(cmpxchg),比较内存值与预期值,相同则更新。
-
Java 通过 Unsafe.compareAndSwapInt 实现,AtomicXXX 全家桶基础。
-
三大问题:
-
ABA 问题 → AtomicStampedReference(版本号)。
-
自旋开销 → LongAdder 分段累加。
-
只能保证单个变量 → AtomicReference 包对象。
- ThreadLocal 原理与内存泄漏?
-
每个 Thread 持有 ThreadLocalMap,key 是 ThreadLocal 弱引用,value 是强引用。
-
ThreadLocal 被回收后 key 变 null,但 value 仍被 Entry 持有 → 泄漏。
-
解决:用完必须 remove()(finally 中)。
-
Android 中的场景:Looper 的 ThreadLocal 存储,Handler 拿当前线程 Looper。
- ReentrantReadWriteLock 如何实现读写锁?
-
AQS 的 state 拆成两部分:高 16 位读锁计数,低 16 位写锁重入计数。
-
读锁共享(多个读线程),写锁独占。
-
锁降级:写锁 → 读锁可以(写后读);读锁 → 写锁不可以。
-
适用:读多写少,如缓存。
- CountDownLatch、CyclicBarrier、Semaphore 区别?
| 工具 | 作用 | 可复用 |
|------|------|--------|
| CountDownLatch | 等待 N 个任务完成 | 否 |
| CyclicBarrier | N 个线程互相等待到同一屏障 | 是(reset) |
| Semaphore | 限制同时访问的线程数 | 是 |
-
ConcurrentHashMap 1.8 的 put 流程?
-
计算 hash(spread 抑制碰撞)。
-
table 为空则 initTable(CAS + sizeCtl 控制)。
-
桶为空 → CAS 放入。
-
hash == MOVED(-1)→ helpTransfer 协助扩容。
-
否则 synchronized 锁桶头节点,链表尾插或红黑树插入。
-
binCount 判断是否 treeify。
-
addCount 统计(CounterCell 分段计数)。
7. 线程死锁的四个必要条件与排查?
-
条件:互斥、持有并等待、不可剥夺、循环等待。
-
排查:`jstack <pid>` 看 "Found one Java-level deadlock";Android 可用 `debug.dumpHprofData` + MAT。
-
预防:固定加锁顺序、tryLock 超时、缩小锁粒度。
8. ForkJoinPool 与 Work-Stealing 算法?
-
每个线程有独立双端队列,自己从头取任务,空闲线程从别人队尾偷任务。
-
减少锁竞争,适合分治任务(并行流、CompletableFuture 底层)。
9. CompletableFuture 常用 API?
```java
CompletableFuture.supplyAsync(() -> fetch()) // 异步执行
.thenApply(d -> d.transform()) // 转换
.thenCombine(other, (a, b) -> merge(a, b)) // 合并两个
.exceptionally(e -> fallback()) // 异常兜底
.allOf(f1, f2, f3) // 全部完成
```
- 适合编排多个异步任务(并行请求多个接口聚合)。
10. happens-before 规则有哪些?
-
程序顺序规则、监视器锁规则、volatile 规则、线程 start/join 规则、传递性。
-
是 JMM 判断数据是否存在竞争、是否可见的依据。
二、JVM 深入与调优
1. 类加载的完整过程?
加载(生成 Class 对象)→ 验证 → 准备(静态变量分配默认值)→ 解析(符号引用转直接引用)→ 初始化(执行 `<clinit>`,静态变量赋真实值)。
2. 对象的创建过程?
-
类加载检查。
-
分配内存(指针碰撞 / 空闲列表)。
-
初始化零值。
-
设置对象头(MarkWord、类型指针)。
-
执行 `<init>` 构造。
3. 对象的内存布局?
-
对象头:MarkWord(哈希码、GC 分代年龄、锁标志)+ 类型指针 + 数组长度(数组才有)。
-
实例数据:字段(long/double 优先对齐)。
-
对齐填充:补齐 8 字节倍数。
4. Android 的 ART 与标准 JVM 的差异?
-
ART 无永久代,dex 走自己的结构(DexFile)。
-
AOT/JIT/PGO 混合编译,dex2oat 生成 oat/vdex。
-
GC 以并发复制收集器(Concurrent Copying, CC)为主,不分永久代、代际弱化(年轻代用 bump-pointer)。
-
GC Root 包含 JNI 全局引用、正在加载的类等 Android 特有项。
5. 常见 GC 收集器对比?
| 收集器 | 代 | 算法 | 特点 |
|--------|-----|------|------|
| Serial | 新生代 | 复制 | 单线程,客户端 |
| Parallel Scavenge | 新生代 | 复制 | 吞吐量优先 |
| CMS | 老年代 | 标记清除 | 并发低停顿,已废弃 |
| G1 | 全堆 | Region 化 | 可预测停顿,>4G 堆 |
| ZGC | 全堆 | Region/染色指针 | 停顿 <1ms |
6. 内存抖动(Memory Churn)是什么?怎么排查?
-
频繁创建短生命周期对象 → 频繁触发 Young GC → GC 时暂停所有线程(STW)→ 卡顿。
-
典型场景:onDraw 中 new Paint/Path、循环中拼接字符串。
-
排查:Android Profiler 看内存锯齿;Memory Profiler 记录分配栈定位。
7. 如何分析 hprof 文件?
-
Profiler / `am dumpheap` 导出。
-
MAT 打开 → Dominator Tree(支配树)看最大对象。
-
Path to GC Roots(排除弱/软引用)找持有链。
-
LeakCanary 自动化日常监控。
8. 强软弱虚引用在 Android 中的实际应用?
-
软引用:早期图片缓存(现已用 LruCache)。
-
弱引用:Handler 修复、Glide 的 ActiveResources(正在使用的资源弱引用池)。
-
虚引用:NativeAllocationRegistry 跟踪 Native 内存释放(Bitmap 像素数据)。
三、Kotlin 进阶
1. inline、noinline、crossinline 的区别?
-
inline:内联函数,lambda 直接复制到调用处,减少对象创建,是 reified 的前提。
-
noinline:被 inline 函数中不需要内联的 lambda 参数。
-
crossinline:禁止 lambda 中非局部返回(因为内联后 return 语义变化)。
2. reified 关键字的作用?
```kotlin
inline fun <reified T : Activity> Context.startActivity() {
startActivity(Intent(this, T::class.java))
}
```
-
内联后泛型类型在编译期真实存在,可 `T::class.java`,避免传 Class 参数。
-
Gson、Retrofit 的扩展封装常用。
3. 协程取消的原理?
-
cancel 只是设置状态位,挂起点(suspend 函数)检查 isActive 抛 CancellationException。
-
CPU 密集循环需手动 `ensureActive()` 或 `yield()` 检查。
-
finally 中清理资源,若需挂起操作用 `withContext(NonCancellable)`。
4. 协程异常处理机制?
-
launch:异常立即向上传播,由 CoroutineExceptionHandler 捕获。
-
async:异常存在 Deferred,直到 await 才抛出;根作用域中 async 也会立即取消兄弟。
-
SupervisorJob:子协程异常不波及其他子(supervisorScope)。
-
结构化并发:异常会导致父 Job 取消所有子。
5. Flow 与 LiveData 如何选择?
| 对比 | LiveData | Flow |
|------|----------|------|
| 线程 | 主线程 setValue | 任意,可切线程 |
| 操作符 | 无 | map/filter/combine 等 |
| 背压 | 无 | buffer/conflate/collectLatest |
| 生命周期 | 自感知 | repeatOnLifecycle 辅助 |
| 冷热 | 热 | 冷(StateFlow/SharedFlow 热) |
新项目推荐:UI 层 StateFlow + collectAsStateWithLifecycle。
6. StateFlow 与 SharedFlow 区别?
-
StateFlow:必须有初始值、值去重(conflate+distinctUntilChanged)、永远有最新值,适合 UI 状态。
-
SharedFlow:可配置 replay/extraBufferCapacity、无初始值、支持事件(用 replay=0),适合一次性事件。
-
事件流不要用 StateFlow(会有丢失和粘性问题)。
7. Kotlin 密封接口(sealed interface)与密封类区别?
-
interface 支持一个类同时实现多个密封接口,类只能单继承。
-
1.6 后密封子类只需在同模块即可(跨文件)。
8. by 委托的原理?
-
属性委托:`by lazy` 编译为 Lazy 对象 + getValue 调用。
-
类委托:`class A(b: B) : B by b`,编译器生成转发方法。
-
自定义委托需实现 `getValue/setValue`(ReadWriteProperty)。
9. Kotlin 与 Java 混用的坑?
-
平台类型(Platform Type):Java 返回值可空性未知,需显式检查。
-
@JvmOverloads 生成重载,@JvmStatic 生成静态,@JvmField 暴露字段。
-
协程 suspend 无法直接被 Java 调用(需 Continuation 参数)。
-
Kotlin 属性 x 编译为 getX/setX,is 开头特殊。
10. Kotlin 编译产物与 Java 有什么不同?
-
支持 null 检查(Intrinsics.checkNotNull)插入。
-
lambda 编译为 Function 对象(非内联时)。
-
协程经 CPS 变换, suspend 状态存于 Continuation。
-
默认参数生成合成方法;data class 生成 copy/componentN。
四、Gradle 构建与工程化
1. Gradle 构建生命周期?
-
Initialization:执行 settings.gradle,确定参与构建的模块,创建 Project 对象。
-
Configuration:执行各模块 build.gradle,生成 Task DAG。
-
Execution:按依赖顺序执行 Task。
2. buildSrc 与 Version Catalog 的区别?
-
buildSrc:单独模块,代码写依赖版本(Groovy/Kotlin),参与编译、影响构建缓存。
-
Version Catalog(libs.versions.toml):Gradle 7.4+ 官方依赖版本管理,配置即用、不影响构建速度、IDE 支持好,推荐。
3. 常见提升构建速度的手段?
-
org.gradle.parallel=true(模块并行)。
-
org.gradle.caching=true(构建缓存)。
-
kapt 改 KSP(快 2 倍以上)。
-
模块化拆分 + 按需构建(-am 只构建依赖)。
-
Gradle Remote Build Cache / CI 缓存。
-
R8 优化、禁用不必要的 Transform。
4. AGP 的 Transform API 与 ASM?
-
Transform:class 转 dex 前的插桩入口(已废弃,新版用 AsmClassVisitorFactory / Instrumentation API)。
-
场景:无埋点(自动插桩)、性能监控、方法耗时统计。
-
ASM:Core API(事件流)与 Tree API(树形结构),常用于字节码注入。
5. ProGuard / R8 的作用与常用规则?
-
作用:压缩(shrink)、优化(optimize)、混淆(obfuscate)、预校验。
-
R8 是 AGP 默认,替换 ProGuard 并做 dex 合并。
-
常用规则:
```
-keep class com.x.Model { *; } // 保留类与成员
-keepclassmembers class * { @Keep *; } // 配合 @Keep 注解
-dontwarn okhttp3.** // 忽略警告
```
- 反射、JNI、序列化、枚举 values 需 keep。
6. APK 签名 v1/v2/v3/v4 的区别?
| 方案 | 校验粒度 | 版本 |
|------|----------|------|
| v1 (JAR) | 单个文件条目 | 全部 |
| v2 (APK) | 整个 APK | 7.0+ |
| v3 | v2 + 密钥轮换证明 | 9.0+ |
| v4 | 增量安装 fs-verity | 11+ |
- v2 解决 v1 的 ZIP 条目篡改(META-INF 不参与校验)问题。
7. 什么是 AAB(Android App Bundle)?
-
Google Play 上传格式,按需分发 split APK(base + config)。
-
Play 自动按设备 ABI/密度/语言生成最小 APK。
-
Feature Delivery:on-demand / conditional 模块动态下发。
-
Play Asset Delivery 适合游戏大资源。
8. 自定义 Gradle 插件的步骤?
-
实现 `Plugin<Project>` 接口的 apply 方法。
-
定义 Extension(DSL 参数)与 Task。
-
发布方式:buildSrc / 独立模块(maven-publish)/ composite build。
9. Maven 仓库与依赖冲突处理?
-
传递依赖冲突:默认取最高版本;`resolutionStrategy.force` 强制指定。
-
exclude 剔除传递依赖;`api` 与 `implementation` 区别:
-
api:依赖会传递给使用方(编译期可见)。
-
implementation:不传递(编译隔离,提升构建速度与封装)。
10. Baseline Profiles 是什么?
-
提前记录关键代码路径(启动、滑动),AGP 编译成 dex 的高优先级机器码。
-
首次启动即可享受接近 AOT 的性能,提速 20%~30%。
-
Macrobenchmark 自动生成,Jetpack 库自带 profile。
五、NDK / JNI / C++ 基础
1. JNI 的基本用法与签名规则?
```c
extern "C" JNIEXPORT jstring JNICALL
Java_com_x_Foo_nativeMethod(JNIEnv *env, jobject thiz) {
return env->NewStringUTF("hello");
}
```
-
命名:Java_包名_类名_方法名(下划线转义 _1)。
-
方法签名:`(I[Ljava/lang/String;)V`,javap -s 可查。
2. JNI 局部引用与全局引用?
-
局部引用:方法返回后自动释放,但循环中大量创建会溢出(DeleteLocalRef)。
-
全局引用:手动 NewGlobalRef / DeleteGlobalRef,跨方法持有(如回调 Java)。
-
弱全局引用:不阻止 GC,用前需 IsSameObject 检查。
3. JNIEnv 是否线程安全?
-
JNIEnv 与线程绑定,不能跨线程缓存使用。
-
可缓存 JavaVM(AttachCurrentThread / DetachCurrentThread)。
-
后台线程回调 Java:Attach → GetMethodID → CallVoidMethod → Detach。
4. so 库与 ABI 是什么?
-
ABI(Application Binary Interface):arm64-v8a、armeabi-v7a、x86、x86_64。
-
abiFilters 指定打包哪些;只打 v8a 会显著减小体积(模拟器/旧设备需兼容)。
-
64 位要求:Play 强制提供 arm64 版本。
5. C++ 智能指针?
-
unique_ptr:独占所有权,不可拷贝可移动。
-
shared_ptr:引用计数共享,循环引用需 weak_ptr 打破。
-
make_shared 一次分配对象与计数块,更高效。
6. C++ 虚函数与虚表(vtable)?
-
虚函数实现运行时多态:对象头部存 vptr 指向虚函数表。
-
子类覆写后表项替换,调用时查表。
-
构造函数不能是虚函数(对象未建好无 vptr);析构函数基类必须是虚函数(否则 delete 父指针不调用子析构)。
7. Native 崩溃如何排查?
-
tombstone 文件(/data/tombstones/):addr2line / ndk-stack 符号化。
-
ndk-stack -sym 路径 直接解析崩溃堆栈。
-
Breakpad / xCrash 跨平台捕获 minidump。
-
发布版本保留 so 的 unstripped 符号表用于还原。
8. Native 内存泄漏如何排查?
-
ASan(AddressSanitizer):编译期插入检测,查越界/UAF。
-
malloc_debug:运行时 hook malloc。
-
Android Profiler 的 Native Memory Record(记录调用栈分配)。
9. ANR / 崩溃信号机制?
-
Native 崩溃本质是信号(SIGSEGV/SIGABRT/SIGBUS)。
-
注册 sigaction 捕获 → 保存上下文 → fork 子进程 dump(避免死锁)。
-
Java 崩溃走 Thread.setDefaultUncaughtExceptionHandler。
10. CMake 与 CMakeLists 常用配置?
```cmake
cmake_minimum_required(VERSION 3.22)
add_library(native SHARED native.cpp)
target_link_libraries(native android log)
```
- externalNativeBuild 接入 AGP;find_library 引用 NDK 库(log、jnigraphics)。
六、音视频开发
1. 音视频播放的完整链路?
协议层(RTMP/HLS/HTTP)→ 封装层(FLV/MP4/TS)解封装 → 编码层(H264/AAC)解编码 → 像素/PCM → 渲染(OpenGL/AudioTrack)。
2. I 帧、P 帧、B 帧的区别?
-
I 帧:关键帧,完整图像,可独立解码。
-
P 帧:参考前面的帧,存差值。
-
B 帧:参考前后帧,压缩率最高但需要缓冲、增加延迟。
-
直播为低延迟通常关 B 帧;seek 只能定位到 I 帧(GOP)。
3. 视频硬编与软编的区别?
-
硬编:MediaCodec 调用 GPU/DSP,功耗低速度快,但机型兼容性差。
-
软编:FFmpeg/x264 CPU 编码,兼容好但发热、性能差。
-
生产实践:优先硬编,失败回退软编。
4. MediaCodec 的工作流程?
-
生产者-消费者模型:InputBuffer 队列 + OutputBuffer 队列。
-
dequeueInputBuffer → fill → queueInputBuffer → dequeueOutputBuffer → render/release。
-
异步模式 setCallback(API 21+)。
5. 如何实现一个美颜/滤镜相机?
-
CameraX 拿 YUV → 转 RGBA(或直接 ImageFormat)→ OpenGL ES 纹理(FBO)→ shader 处理(磨皮/滤镜)→ 显示到 SurfaceView 或编码 MediaCodec 输入 Surface。
-
磨皮:双边滤波保边缘;大眼瘦脸:网格顶点形变(局部位移 shader)。
6. FFmpeg 常用命令行与 API?
-
命令:`ffmpeg -i in.mp4 -vf scale=640:-1 -c:v libx264 out.mp4`
-
API:avformat_open_input → avformat_find_stream_info → avcodec 找解码器 → av_read_frame 循环 → avcodec_send/receive_packet → sws_scale 转格式。
7. 直播架构与常见协议对比?
| 协议 | 延迟 | 特点 |
|------|------|------|
| RTMP | 1-3s | 推流主流,TCP |
| HTTP-FLV | 2-4s | 拉流,防火墙友好 |
| HLS | 5-20s | 切片 m3u8,弱网稳 |
| WebRTC | <500ms | UDP,实时连麦 |
8. 音频焦点(AudioFocus)如何处理?
-
请求焦点 requestAudioFocus,根据 LOSS_TRANSIENT/LOSS 暂停或降音量。
-
播放前申请,停止时 abandonAudioFocus。
-
AudioAttributes 标注用途(音乐/通话/提示音)影响混音策略。
9. AudioTrack 播放的四种模式?
-
MODE_STATIC:一次性写入缓冲,适合短提示音(延迟低)。
-
MODE_STREAM:流式写,适合长音频/语音实时。
-
采样率需匹配设备(48kHz 常见),格式 ENCODING_PCM_16BIT。
10. 如何做音视频同步?
-
基准选择:音频为主(人耳敏感)或系统时钟。
-
PTS 换算:`pts * time_base` 秒。
-
视频慢了 → 丢帧/加快渲染;快了 → 等待。
-
参考实现:ffplay 的 sync 类型(audio/video/external clock)。
七、图形渲染系统
1. VSync 信号与 Choreographer?
-
显示器每 16.6ms(60Hz)发 VSync。
-
Choreographer 通过 FrameDisplayEventReceiver(native 回调)接收 VSync。
-
postCallback 分发 CALLBACK_INPUT → ANIMATION → TRAVERSAL(measure/layout/draw)。
-
doFrame 计算掉帧(时间差 vs 16.6ms)。
2. 为什么用双缓冲/三缓冲?
-
双缓冲:前台缓冲显示、后台缓冲绘制,VSync 时交换,防撕裂。
-
三缓冲:CPU/GPU/Display 各占一个,减轻 jank(代价是延迟一帧)。
-
Android 4.1 Project Butter 引入 VSync 同步 + 三缓冲。
3. SurfaceFlinger 的工作流程?
-
收集所有 Layer(App 的 Surface)。
-
VSync 到来时计算脏区,调用 OpenGL/合成器合成(HWC 硬件合成优先)。
-
合成结果送 framebuffer 显示。
-
App 端 draw 只是往 Surface(BufferQueue)生产图形缓冲,SF 消费合成。
4. BufferQueue 生产者-消费者模型?
-
Surface(IGraphicBufferProducer):App 生产。
-
SurfaceFlinger(Consumer):消费合成。
-
acquire/dequeue + release/queue 一套异步流程,是 App 与显示系统解耦核心。
5. OpenGL ES 渲染管线?
顶点着色器 → 图元装配 → 光栅化 → 片元着色器 → 测试混合(深度/模板/Alpha)→ 帧缓冲。
-
GLSL 编写着色器;VBO/EBO 存顶点数据;FBO 离屏渲染(美颜中间处理)。
-
EGL 创建上下文与 Surface(GLSurfaceView 封装了这些)。
6. GLSurfaceView 与 TextureView 的区别?
| 项 | GLSurfaceView | TextureView |
|----|---------------|-------------|
| 实现 | 独立 Surface(View 层级之外) | 普通View,硬件加速层 |
| 性能 | 高 | 略低(额外合成) |
| 变换/动画 | 不支持(单独Window) | 支持平移缩放旋转 |
| 嵌套列表 | 有坑(与列表滑动冲突) | 可正常使用 |
7. RenderThread 与 HWUI?
-
5.0 后 UI 绘制不再在主线程,draw 只是记录 DisplayList。
-
RenderThread 真正执行 GPU 命令(RenderProperties + DisplayListOp)。
-
硬件加速默认开启;关闭则回退软件绘制(Skia 软栅格化)。
8. 硬件加速原理与禁用场景?
-
View 绘制指令录制为 DisplayList → RenderThread 通过 Skia (HWUI) 转 GPU 命令。
-
优点:动画流畅(仅重录脏区)、减轻主线程。
-
禁用场景:某些自定义 View 兼容问题(setLayerType SOFTWARE)、截图(缓存 bitmap)。
9. 自定义 View 如何做 GPU 友好?
-
避免频繁创建 Paint/Path(对象池)。
-
用硬件层 setLayerType(LAYER_TYPE_HARDWARE) 做复杂动画。
-
shader 尽量复用,避免每帧编译。
-
invalidate 最小化(脏矩形)。
八、热修复 / 插件化 / ClassLoader
1. Android 中的 ClassLoader 体系?
```
BootClassLoader(系统类)
↑
PathClassLoader / DexClassLoader(应用类)
```
-
PathClassLoader:加载已安装 APK(/data/app)。
-
DexClassLoader:可加载任意路径 dex/apk/jar(旧版需 optimizedDirectory,8.0 后已废弃该参数)。
-
二者 8.0 后实现一致(都走 DexPathList)。
2. 热修复的三大方案对比?
| 方案 | 原理 | 修复范围 | 代表 |
|------|------|----------|------|
| 类加载 | dex 插到 dexElements 前面 | 代码(需重启) | Tinker、Nuwa |
| 底层替换 | ArtMethod 替换入口点 | 方法(即时生效) | AndFix、Sophix |
| Instant Run | 类代理/分发 | 代码(即时) | Robust |
-
Tinker 补丁包最小(差量 dex),但需重启。
-
Sophix 综合两种:优先底层替换,失败退回类加载。
3. Tinker 的基本流程?
-
编译期:old dex 与 new dex 做 diff 生成 patch.dex。
-
运行时:下载补丁 → 合成全量 dex(合成在子进程)。
-
反射插入 dexElements0 → 重启生效。
-
类查找顺序改变,加载补丁中的新类。
4. 插件化如何加载插件的资源?
-
反射构造 Resources:`ResourcesManager` 或 new AssetManager + addAssetPath(pluginPath)。
-
构造插件自己的 Resources 实例(资源 id 冲突用动态 id 或资源分段)。
-
四大组件插件化难点:
-
Activity:Hook IActivityManager 预占坑(stub)+ 还原。
-
Service:代理分发。
-
Provider:ContentProviderClient 包装。
-
Receiver:动态注册静态化。
5. Instant Run 的三类更新?
-
热插拔:方法体修改,运行时替换(方法入口插桩)。
-
温插拔:字段/签名变化需重启 Activity。
-
冷插拔:结构变化,整个 APK 重启加载。
6. odex / vdex / art / oat 文件?
-
odex:优化后的 dex(系统预提取)。
-
oat:ART 编译产物(含机器码),ELF 格式。
-
vdex:验证过的 dex 副本(避免重复验证),9.0+。
-
art:方法入口地址/Profiling 信息。
7. Hook AMS 启动插件 Activity 的流程?
-
在 Instrumentation.execStartActivity 前替换 Intent:真实目标存 Bundle,Component 换成 Manifest 中预注册的占坑 Activity。
-
AMS 校验通过后,ActivityThread 收到 LaunchActivityItem。
-
在 mInstrumentation.newActivity 前把 Component 还原为真实类。
-
ClassLoader 用插件的 DexClassLoader 加载并实例化。
8. 如何检测插件 APK 的签名一致性?
-
PackageManager.GET_SIGNATURES / GET_SIGNING_CERTIFICATES 获取签名。
-
对比字节码哈希,不一致拒绝加载(防替换攻击)。
九、安全与逆向
1. 常见加密算法分类与应用?
-
对称:AES(加密数据)、DES(已不安全)、ChaCha20。
-
非对称:RSA(交换密钥/签名)、ECC(更短密钥同强度)。
-
摘要:MD5(已不安全)、SHA-256、HMAC(带密钥摘要)。
-
典型:HTTPS 用 RSA/ECDHE 交换对称密钥,数据 AES-GCM 加密。
2. Android 应用加固方案与原理?
-
dex 加密:原 dex 加密藏在 so / 资源中,运行时解密后用 InMemoryDexClassLoader 加载。
-
so 加壳:OLLVM 混淆、VMP 虚拟机保护。
-
资源混淆:AndResGuard 改短资源路径。
-
防调试:ptrace 自附加、检测 frida/xposed。
3. 常见反编译工具与防护?
-
工具:jadx、apktool、JEB、IDA、Frida(动态 hook)。
-
防护:代码混淆 + 资源混淆、字符串加密、关键逻辑 native 化、完整性校验(签名校验)、反调试检测。
4. 如何防止网络被抓包?
-
SSL Pinning(证书锁定):OkHttp CertificatePatcher/sslSocketFactory 校验本地证书。
-
双向认证(客户端证书)。
-
关键参数签名 + 时间戳防重放。
-
防止用户证书:Android 7+ 默认不信任用户 CA(networkSecurityConfig 控制)。
5. 数据存储加密方案?
-
密钥存 AndroidKeyStore(硬件支持的 TEE/StrongBox),不可导出。
-
EncryptedSharedPreferences / EncryptedFile(Jetpack Security)。
-
大数据:AES-GCM + Keystore 生成密钥加密随机数据密钥(信封加密)。
6. 防截屏与 FLAG_SECURE?
```java
getWindow().setFlags(FLAG_SECURE, FLAG_SECURE);
```
-
截屏黑屏、不可见于最近任务预览(SurfaceFlinger 拒绝截取)。
-
适用:支付、隐私页面。
7. AES 的几种模式与填充?
-
ECB:同明文同密文(不安全,暴露模式)。
-
CBC:需 IV,常见但并行性差。
-
GCM:带认证(AEAD),推荐,防篡改。
-
填充:PKCS5Padding;密钥长度 128/192/256。
8. 什么是重打包攻击与防护?
-
攻击:反编译 → 植入恶意代码/去广告 → 重签名 → 分发。
-
防护:运行时校验自身签名哈希(native 校验更安全)、完整性自校验(关键文件哈希)、服务端二次校验。
9. 防自动化点击 / 群控?
-
设备指纹(传感器噪声、GPU 渲染差异)。
-
行为分析(滑动轨迹正态分布检测)。
-
SafetyNet/Play Integrity API 检测设备完整性。
十、动画体系
1. 补间动画(Tween)与属性动画(Property)区别?
-
补间:只改显示效果(View 绘制层矩阵变换),不改变真实位置,点击事件仍在原地。
-
属性:通过反射/Setter 真实改变属性值,作用于任何对象。
-
属性动画核心类:ValueAnimator(值变化)与 ObjectAnimator(属性赋值)。
2. ValueAnimator 原理?
-
内部 AnimationHandler 挂到 Choreographer 的 CALLBACK_ANIMATION。
-
每帧根据 TimeInterpolator 与 TypeEvaluator 计算当前值,回调 onAnimationUpdate。
-
与 UI 绘制同节奏,不额外开线程。
3. 插值器(Interpolator)与估值器(Evaluator)?
-
Interpolator:时间 → 进度比(如 AccelerateDecelerate)。
-
Evaluator:进度比 → 具体值(IntEvaluator、ArgbEvaluator 颜色渐变、自定义路径)。
4. Activity 切换动画的几种方式?
-
overridePendingTransition(旧版,5.0 前主流)。
-
ActivityOptions.makeCustomAnimation / makeSceneTransitionAnimation(转场动画)。
-
windowAnimationStyle 主题全局配置。
-
共享元素转场:SharedElement + Transition(supportPostponeEnterTransition)。
5. Lottie 动画原理?
-
AE 导出 JSON(图层、关键帧、变换)。
-
Lottie 库解析为图层树,ValueAnimator 驱动进度,Canvas 绘制。
-
优势:设计师直接导出、跨端一致、体积小;劣势:复杂效果性能不如原生/视频。
6. 如何优化动画卡顿?
-
硬件层:动画期间 setLayerType(LAYER_TYPE_HARDWARE)(复杂视图才用,否则费显存)。
-
用属性动画避免 invalidate 整棵树(如 translationX 走 RenderNode)。
-
帧率监控:Choreographer/postOnFrameCallback 计算丢帧率。
-
帧动画(AnimationDrawable)大图易 OOM → 用 SurfaceView 或 Lottie。
十一、WebView 深入
1. WebView 的加载流程?
`loadUrl` → WebChromeClient(进度/JS Dialog)→ WebViewClient(shouldOverrideUrlLoading / onPageFinished)→ 渲染。
2. JS 与 Native 通信方式?
-
JSBridge:`addJavascriptInterface`(4.2 以下有漏洞,需 @JavascriptInterface 注解)。
-
URL Scheme 拦截:shouldOverrideUrlLoading 解析自定义协议。
-
evaluateJavascript:Native 调 JS(4.4+,可拿返回值)。
-
WebMessagePort / postMessage(双向通道)。
3. WebView 内存泄漏怎么解决?
-
WebView 内部持有 Activity Context → 独立进程 + 杀进程。
-
或 xml 动态创建(传 ApplicationContext)+ 从父容器 remove + destroy。
-
onDestory 中 destroy 的顺序:先 removeView,再 webview.destroy()。
4. WebView 白屏原因与排查?
-
内核初始化未完成就加载(首次慢)→ 预热 WebView 池。
-
JS 错误阻塞渲染 → vConsole 调试。
-
证书错误 → onReceivedSslError 处理(勿盲目 proceed)。
-
资源跨域/CSP → onReceivedError 分类处理。
5. 如何调试 H5?
-
Chrome inspect(chrome://inspect)远程调试。
-
setWebContentsDebuggingEnabled(true)。
-
vConsole / eruda 注入到 H5。
6. WebView 缓存策略?
-
CACHE_DEFAULT:HTTP 缓存头控制。
-
LOAD_CACHE_ELSE_NETWORK:优先缓存,适合静态页。
-
离线包:H5 资源本地化(zip 下发 + shouldInterceptRequest 拦截返回本地流)。
十二、跨平台技术
1. Flutter 与 RN 的架构区别?
| 项 | Flutter | React Native |
|----|---------|--------------|
| 渲染 | 自绘 Skia/Impeller,一致性强 | 原生组件映射(JSC/Hermes 桥接) |
| 语言 | Dart | JS/TS |
| 桥接 | 无桥(Platform Channel) | 旧架构 JS Bridge,新架构 JSI/TurboModule |
| 性能 | 接近原生(每帧直接 GPU) | 新架构已大幅改善 |
| 包体 | 较大(引擎 ~4MB) | 较小 |
2. Flutter 的渲染管线?
Widget(配置,不可变)→ Element(树,diff)→ RenderObject(布局/绘制)→ Layer → Scene → GPU。
- setState 触发 markNeedsBuild → 下帧重建 subtree → 三棵树 diff(增量)。
3. Flutter Platform Channel 三种类型?
-
MethodChannel:方法调用(常用)。
-
BasicMessageChannel:双向消息。
-
EventChannel:事件流(如原生传感器数据流)。
-
数据走 BinaryCodec,仅支持基本类型(需 JSON/protobuf 序列化复杂对象)。
4. 如何做 Flutter 与 Native 混编?
-
Add-to-App:Flutter Module 作为 AAR 嵌入原生。
-
FlutterFragment / FlutterView 嵌入页面。
-
路由统一:FlutterBoost 栈管理(多引擎/单引擎+栈路由)。
5. KMP(Kotlin Multiplatform)适合什么场景?
-
业务逻辑、网络层、数据层共享 Kotlin 代码。
-
UI 各端原生(或 Compose Multiplatform)。
-
相比 Flutter:复用现有 Android 技术栈、无引擎开销,但生态较新。
6. 鸿蒙(HarmonyOS)与 Android 的区别?
-
分布式架构(跨设备流转、软总线)。
-
UI:ArkTS/ArkUI 声明式(类似 Compose/SwiftUI)。
-
应用形态:Ability(UI Ability / Data Ability)与 Android 四大组件不同。
-
开发语言 ArkTS(TypeScript 扩展)。
十三、Framework 深入
1. SystemServer 启动了哪些核心服务?
-
AMS(ActivityManagerService)、PMS(PackageManagerService)、WMS(WindowManagerService)。
-
IMS(InputManagerService)、DMS(DisplayManagerService)、PowerManagerService。
-
引导服务(BOOT_CLASS)→ 核心服务 → 其他服务三段启动。
2. 输入事件从硬件到 View 的流程?
-
Linux input 驱动 → /dev/input/eventX。
-
InputReader(EventHub 读)→ InputDispatcher。
-
InputDispatcher 找到焦点窗口 → socketpair 发给 App 进程。
-
ViewRootImpl 的 InputEventReceiver 收到 → InputStage 处理链 → DecorView dispatchTouchEvent → Activity。
3. 窗口的类型与 Z 序?
-
Application(Activity 窗口)、Sub(Dialog)、System(状态栏、Toast)。
-
type 值分三段:1-99 应用、1000-1999 子窗口、2000+ 系统窗口。
-
WMS 用 token 校验权限;SYSTEM_ALERT_WINDOW 需要 Settings.canDrawOverlays 授权。
4. Toast 的实现原理?
-
TN(ITransientNotification)是 Binder 回调。
-
NotificationManagerService 排队(每包最多 5 个),通过 TN.show 通知 App 端。
-
App 端 Handler 切主线程创建/显示(所以子线程 Looper.prepare 也能用)。
-
Toast 队列由系统控制,自定义时长只能 hack(反射 cancel 再 show)。
5. Dialog 为什么不能用 Application Context?
-
创建 Dialog 时会通过 WindowManager 添加窗口。
-
Application 类型 token 不是应用窗口 token,type 校验失败抛 BadTokenException。
-
例外:SYSTEM_ALERT_WINDOW 类型的 Dialog 可以用 Application Context(悬浮窗权限)。
6. Context 一共有多少个?
-
Application:每进程 1 个。
-
Activity:每个实例 1 个。
-
Service:每个实例 1 个。
-
BroadcastReceiver:onReceive 每次广播一个新的(生命周期几秒,不宜做耗时/弹窗)。
-
ContentProvider:与应用共享 Application Context。
7. Parcel 与 Serializable 的区别?
-
Serializable:Java 标准反射实现,产生大量临时对象、慢。
-
Parcelable:手写 writeToParcel/Creator,基于 Parcel(共享内存读写),快 10 倍。
-
Bundle 内部 ArrayMap 查找;跨进程传大对象有 1MB Binder 限制。
8. 什么是 System UI Flag(沉浸式)?
-
setSystemUiVisibility:LOW_PROFILE、FULLSCREEN、HIDE_NAVIGATION。
-
WindowInsetsController(新 API):hide(Type.systemBars())、setAppearanceLightStatusBars。
-
fitsSystemWindows 与 OnApplyWindowInsetsListener 处理布局避让。
9. Choreographer 掉帧监控如何实现?
```kotlin
Choreographer.getInstance().postFrameCallback { time ->
// 计算 lastTime 与 time 差值,超过阈值上报
Choreographer.getInstance().postFrameCallback(this)
}
```
- 环形记录帧时间 → 慢帧/卡顿堆栈聚合上报(如 Matrix 的 EvilMethodTracer)。
10. 后台执行限制(Doze & App Standby)?
-
Doze:息屏+静止,延迟 Job/网络/唤醒,维护窗口批量执行。
-
App Standby:未使用 App 限制网络与 Job。
-
豁免:前台、白名单、高优先级 FCM。
-
适配:WorkManager setExpedited 加急任务。
十四、蓝牙 / 传感器 / 定位
1. BLE(低功耗蓝牙)开发流程?
-
权限:BLUETOOTH_SCAN/CONNECT(12+ 运行时)+ ACCESS_FINE_LOCATION(部分扫描场景)。
-
BluetoothLeScanner.startScan 扫描。
-
connectGatt 连接 GATT。
-
discoverServices 发现服务 → getCharacteristic 读/写 → setCharacteristicNotification 订阅 notify。
-
onCharacteristicChanged 收数据。
2. 经典蓝牙 vs BLE?
-
经典:连续流(音频 A2DP、SPP),功耗高。
-
BLE:小数据包、极低功耗(手环、IoT)。
-
Android 12 新 API(BluetoothLeAdvertiser/BLE 批量扫描)。
3. 常见传感器与采样策略?
-
加速度/陀螺仪(连续高频)、心率/温度(按需)、计步(硬件步数传感器,无需持续采样)。
-
SensorManager.registerListener 选 SamplingPeriodUs:游戏用 SENSOR_DELAY_GAME,日常 UI 用 UI。
-
省电:onPause unregisterListener,或显著动作检测(SIGNIFICANT_MOTION)唤醒。
4. 定位方案对比?
| 方案 | 精度 | 耗电 |
|------|------|------|
| GPS | 高(10m) | 高 |
| 网络(基站/WiFi) | 中(百米) | 低 |
| 融合(FusedLocationProvider) | 自适应 | 最优 |
-
后台定位需 ACCESS_BACKGROUND_LOCATION 单独授权。
-
Geofencing(地理围栏)由系统托管,App 被杀仍能唤醒。
5. NFC 的几种模式?
-
读卡器模式(读 NFC 标签 NDEF)。
-
卡模拟模式(HCE,Host Card Emulation 模拟门禁/公交卡)。
-
P2P 模式(Beam,已移除)。
-
Android Beam 8.0 废弃,改用 HCE。
6. USB / 串口通信?
-
UsbManager 枚举设备,请求权限(BroadcastReceiver 接收 ACTION_USB_PERMISSION)。
-
UsbInterface/UsbEndpoint 找到 bulk 端点,bulkTransfer 读写。
-
串口:usb-serial-for-android 库封装 CDC/FTDI 等协议。
十五、测试与 CI/CD
1. 单元测试框架选择?
-
JUnit4 + Mockito(Mock 依赖)+ Robolectric(Android 环境模拟,跑 JVM)。
-
纯逻辑用 JUnit,涉及 Framework 用 Robolectric,UI 用 Espresso/Compose Test。
2. 如何测试 ViewModel?
-
注入假的 Repository(接口 + FakeImpl)。
-
kotlinx-coroutines-test 的 runTest + StandardTestDispatcher 控制虚拟时间。
-
Turbine 测试 Flow(`flow.test { awaitItem() }`)。
3. Espresso 与 UiAutomator 区别?
-
Espresso:同进程 UI 测试(白盒,快,可访问 ViewAction/IdlingResource 同步)。
-
UiAutomator:跨进程黑盒测试(可测系统 UI、第三方 App、解锁屏幕)。
4. 什么是依赖注入带来的可测试性?
-
构造器注入替代内部 new,测试时传 Mock/Fake。
-
Hilt 提供 @BindValue + HiltAndroidTest 快速替换绑定。
5. CI/CD 流水线一般包含哪些阶段?
提交 → Lint/ktlint 静态检查 → 单元测试 → 构建 AAB/APK → 灰度/内测分发 → Play/Fir 发布 → 崩溃监控(Crashlytics/Sentry)闭环。
6. 如何做 A/B 测试?
-
Firebase Remote Config / GrowthBook 分流(按设备哈希分组)。
-
指标埋点上报 → 统计显著性检验(t-test/Bayesian)。
-
注意:分组一致性、样本量预估、防刷(真实用户过滤)。
7. Monkey 与稳定性测试?
-
adb shell monkey -p com.x --throttle 300 -v 5000。
-
崩溃/ANR 日志抓取 → 分类归因。
-
Boost STF/Maxim 定向 Monkey(遍历业务关键路径)。
十六、操作系统与网络进阶
1. 进程与线程的区别?
-
进程是资源分配单位(独立地址空间),线程是调度单位(共享进程资源)。
-
上下文切换:进程切换需换页表/TLB,线程只需换寄存器和栈。
-
Android 每个 App 默认独立进程(Linux 进程 + Binder IPC)。
2. 用户态与内核态?
-
CPU 特权级区分,系统调用时陷入内核(trap)。
-
Binder 一次拷贝:用户态→内核共享区→对方用户态直接映射读取。
-
epoll/select 是内核态监听 fd 的机制。
3. 虚拟内存与页表?
-
每进程有独立虚拟地址空间,MMU 通过页表映射物理内存。
-
缺页中断:页不在物理内存时从磁盘/swap 加载。
-
mmap:文件映射到虚拟内存,读写文件无需 read/write 系统调用(MMKV 用)。
4. epoll 的优势?
-
select/poll:每次传入全部 fd,O(n) 遍历,上限受限。
-
epoll:红黑树管理 fd + 就绪链表回调,O(1) 获取就绪事件。
-
Android MessageQueue 的 nativePollOnce 即基于 epoll。
5. HTTP/2 的核心改进?
-
二进制分帧(Header 压缩 HPACK)。
-
多路复用(一个 TCP 并行多请求,无队头阻塞到应用层)。
-
服务端推送(Chrome 已移除支持)。
-
TCP 层仍有队头阻塞 → HTTP/3 改用 QUIC(UDP)。
6. CDN 原理?
-
DNS 调度(GSLB)到边缘节点。
-
边缘节点未命中回源拉取并缓存(Cache-Control 策略)。
-
图片 CDN 可动态裁剪、WebP 转换、鉴权防盗链。
7. TCP 长连接与心跳(移动端)?
-
NAT 超时:运营商 NAT 表 5~10 分钟回收,需心跳(IM 常用 4~5 分钟智能心跳)。
-
心跳包极小(仅协议头),前台/后台不同间隔。
-
前后台切换、网络切换(WiFi→蜂窝)需重建连接。
8. 断点续传如何实现?
-
Range: bytes=start-end 请求头。
-
响应 206 Partial Content + Content-Range。
-
客户端记录已下载偏移;文件完整性用 ETag/If-Range 校验(避免文件变更后拼接错误)。
十七、Compose 进阶
1. 重组(Recomposition)的规则?
-
状态读取(State<T>.value)发生在哪个 Composable,哪个就会重组。
-
重组可能频繁且无序执行:Composable 必须无副作用。
-
@Stable/@Immutable 帮助编译器跳过不必要的重组(参数未变则跳过)。
2. remember 与 rememberSaveable?
-
remember:重组间保留(配置变更丢失)。
-
rememberSaveable:跨配置变更/进程死亡恢复(Bundle 机制,自动保存可 Bundle 化类型)。
3. 副作用 API 对比?
| API | 场景 |
|-----|------|
| LaunchedEffect(key) | 首次进入/ key 变化执行协程 |
| DisposableEffect | 需成对清理(注册/注销) |
| SideEffect | 每次重组后执行(同步外部状态) |
| rememberCoroutineScope | 事件回调中启协程 |
| snapshotFlow | 将 Compose 状态转 Flow |
4. Compose 性能优化?
-
列表:LazyColumn + key(稳定 id,提升复用 diff 效率)。
-
derivedStateOf:减少由 state 计算的中间状态触发重组。
-
避免不稳定参数(List 用 kotlinx.collections.immutable 或 @Immutable)。
-
重组范围最小化:lambda 读状态而非整个函数读。
-
R8 + Compose Compiler Metrics 报告分析可跳过率。
5. Compose 与 View 互操作?
```kotlin
// Compose 中嵌 View
AndroidView(factory = { ctx -> MapView(ctx) }, update = { map -> ... })
// View 中嵌 Compose
ComposeView(context).apply { setContent { MyScreen() } }
```
- 需要正确的 ViewTreeLifecycleOwner/ViewModelStoreOwner(ComponentActivity/Fragment 自动提供)。
6. Navigation Compose 的状态保存?
-
navController 导航(rememberNavController)。
-
Bundle 传参与 restore(rememberSaveable + NavBackStackEntry)。
-
ViewModel 作用域绑定 NavBackStackEntry(hiltViewModel()),返回栈弹出自动销毁。
十八大厂场景设计题
1. 设计一个埋点 SDK(无埋点 + 代码埋点)?
-
代码埋点:注解 + APT 生成收集类,或 API 直接上报。
-
全埋点:Gradle 插桩(Transform/ASM)在 onClick/onPageResume 注入。
-
缓存:内存队列 → 批量落盘(mmap 防丢)→ 定时/定量上报。
-
可靠性:失败重试、本地保留(数据库)、压缩(protobuf+gzip)。
-
动态配置:远端开关控制采样率。
2. 设计一个 Crash 捕获与上报系统?
-
Java:Thread.setDefaultUncaughtExceptionHandler 链式接管,先缓存再交回原 handler。
-
Native:信号量捕获 fork 子进程 dump tombstone。
-
ANR:Watchdog 线程 + traces.txt 监听。
-
上报:补充设备信息、页面栈、最近操作日志 → 压缩上传 → 符号表还原(mapping.txt 管理)。
-
平台:Crashlytics / Sentry / Bugly 或自研聚合(聚类归因 + 版本趋势)。
3. 设计抖音的无限滑动信息流?
-
预加载:前后各 1 个视频(PagerAdapter/Compose Pager)。
-
播放策略:可见 >50% 播放,离开立即停止释放解码器。
-
缓存:磁盘 LRU(视频分片 Range 加载),滑太快时先显示首帧。
-
内存:同屏最多 2-3 个解码实例,TextureView 池化。
-
指标:卡顿率(播放起播时间)、秒开率监控上报。
4. 设计微信朋友圈的图片九宫格上传?
-
压缩策略:长边 1280、质量 80、WebP 兼容回退 JPEG。
-
并发控制:同时上传 3 个,失败重试指数退避。
-
断点续传:分片 + Range。
-
一致性:本地先占位(灰块),上传成功后替换 URL,失败标记重传。
-
弱网:切 WiFi 提示、蜂窝默认原图不传仅传缩略图。
5. 设计一个灰度发布系统?
-
维度:设备(哈希取模)、用户 ID 白名单、地区、版本。
-
配置下发:长连接推送 + 定时拉取兜底。
-
熔断:崩溃率/ANR 率超阈值自动停发。
-
监控大盘:新版本 vs 旧版本核心指标对比。
6. 设计一个 App 内更新(热更新 + 强更)?
-
版本策略:热修(Tinker patch)小改动;大功能走商店/自建 APK 下载。
-
下载:前台服务通知进度、MD5 校验、断点续传。
-
安装:FileProvider + ACTION_INSTALL_PACKAGE(8.0 需 REQUEST_INSTALL_PACKAGES 权限)。
-
强更逻辑:服务端下发 minVersion,低于则弹不可关闭对话框。
7. 设计千万级日活 App 的配置中心?
-
存储:SQLite(版本号 diff 增量更新)。
-
下发:长连接推送即时生效 + 启动拉取(版本协商)。
-
合并策略:多维度(渠道、地区、版本)交集合并。
-
原子性:写临时文件 rename,防止配置损坏。
8. 如何设计页面路由表支持 DeepLink?
-
scheme/host/path 注册到路由表(ARouter @Route)。
-
安全:白名单校验、防导出劫持(exported=false + 自定义校验)。
-
延迟加载:PendingIntent 拉起时区分冷/热启动。
-
兜底:目标页面不存在时跳转 Web 版或首页。
十九、HR 面与软技能
1. 自我介绍怎么说?
结构:基本信息(年限+方向)→ 技术亮点(2-3 个与岗位匹配的关键词)→ 代表项目(一句话量化成果)→ 求职动机。
控制在 1-2 分钟,重点前置。
2. 离职原因怎么回答?
-
公式:客观发展诉求 + 向前看(不贬低前司)。
-
示例:业务方向调整后技术成长受限,希望在 XX 方向更深的团队发展。
-
避免:吐槽领导/加班/薪资(除非对方主动且策略表达)。
3. 你有什么缺点?
-
说真实但不致命的缺点 + 正在改进的行动。
-
示例:以前过度追求细节导致排期紧张,现在会用四象限拆分任务优先级。
4. 为什么选择我们公司?
-
提前研究:产品、技术栈、团队(官网/招聘 JD/技术博客)。
-
结合自身:方向匹配 + 价值贡献 + 成长空间。
5. 职业规划?
-
短期(1-2 年):深耕某方向成为团队骨干。
-
中期(3-5 年):技术专家 or 管理两条路(按 JD 倾向表述)。
-
展现稳定性和自驱力。
6. 期望薪资如何谈?
-
提前调研市场(脉脉/Offershow/拉勾薪酬报告)。
-
给区间不给死数:"期望 XX-XX,具体结合职责与整体包综合评估。"
-
谈总包(base+奖金+期权),不是只谈月薪。
7. 反问环节问什么?
-
这个岗位当前最需要解决的三个问题?
-
团队的技术栈与协作方式(敏捷节奏/代码评审文化)?
-
入职后前三个月的核心目标?
-
避免一上来问薪资福利加班(终面 HR 环节再问)。
8. 如何应对压力面?
-
保持冷静,对方质疑时先认同合理部分再陈述事实。
-
不懂的问题坦诚 + 给出思考路径(比胡编更加分)。
-
把对抗转化为讨论:"您说的角度我之前没考虑,如果按这个思路我会......"
9. 加班怎么看?
- 表达结果导向:"我更关注交付效率,必要时为项目目标全力以赴;平时会通过自动化工具和流程优化减少无谓加班。"
10. Offer 选择逻辑?
-
权重打分:成长空间 > 业务前景 > 技术栈匹配 > 薪资 > 通勤。
-
问清:汇报线、团队规模、KPI 考核方式、试用期标准。
附录:进阶学习路线建议
-
**源码阅读顺序**:Handler → View 三大流程 → RecyclerView 缓存 → OkHttp → AMS/WMS → Binder。
-
**动手项目**:自己实现一个简化版图片框架 / 热修复 / 路由框架,面试远超八股。
-
**关注趋势**:Compose、KMP、Baseline Profiles、Macrobenchmark、AI 端侧(TFLite/MNN/NCNN)。
-
**AOSP 环境**:能编译刷机 AOSP,阅读 frameworks/base 关键类加分极大。
-
**输出习惯**:技术博客 / 内部分享,倒逼深度理解。