一、Handler机制
1.1 Looper.loop()
为什么不会阻塞主线程?
原理: 通过epoll_wait
实现消息队列的空闲等待,配合Native层唤醒机制
实现高效调度
本质解析:
- 事件驱动模型:
Native
层的epoll_wait
机制 - 同步屏障:
MessageQueue#postSyncBarrier()
关键源码路径以及代码示例:
java 代码解读复制代码// MessageQueue.next()
nativePollOnce(ptr, nextPollTimeoutMillis);
// Native层通过epoll监听文件描述符事件
// 同步屏障核心逻辑
public int postSyncBarrier() {
Message msg = Message.obtain();
msg.when = SystemClock.uptimeMillis();
msg.setAsynchronous(true);
return enqueueMessage(queue, msg, uptimeMillis);
}
1.2 子线程创建Handler
为什么会崩溃?
根本原因: 未初始化当前线程的Looper
(需先执行Looper.prepare()
)
解决方案: 使用HandlerThread
自动管理Looper
生命周期。
1.3 如何实现消息优先级?
腾讯方案:
- 自定义MessageQueue的enqueueMessage()
- 使用红黑树代替链表(消息量>1000时效率提升30%)
代码示例:
java 代码解读复制代码void enqueueMessage(Message msg, long when) {
if (mMessages == null || when < mMessages.when) {
msg.next = mMessages;
mMessages = msg;
} else {
Messageprev= mMessages;
Messagecur= prev.next;
while (cur != null && cur.when <= when) {
prev = cur;
cur = cur.next;
}
prev.next = msg;
msg.next = cur;
}
}
二、Binder机制
2.1 Binder
相比Socket
的优势?
- 内存映射技术(
mmap
)减少数据拷贝次数 - 引用计数机制实现自动资源回收
2.2 AIDL
生成Java类的工作原理?
- 自动生成
Stub
(服务端)和Proxy
(客户端)类 - 数据序列化通过
Parcel
实现,关键代码如下:
java 代码解读复制代码// Book.aidl自动生成的方法
public void readFromParcel(Parcel parcel) {
this.name = parcel.readString();
}
2.3 Binder
线程池管理?
- 默认最大线程数16(可通过
Process.setThreadPriority()
调整) - 使用
IPCThreadState
维护线程状态
2.4 为什么Zygote
不用Binder
?
- 安全性考量:
Socket
支持SELinux
策略(SEAndroid)的精细控制。 - 效率对比:
fork进程
时Socket
通信耗时比Binder少。 - 生命周期:
Zygote
进程存活周期与SystemServer
解耦。
2.5 Binder
数据量传输极限?
技术本质:
- 内核限制:
mmap
内存映射区大小(默认1M-8K) - 协议限制:
Binder
事务缓冲区大小(通过BINDER_SET_MAX_THREADS
设置)
避坑指南:
- 跨进程传递
Bitmap
时使用Ashmem
(实测2MB图片传输速度提升4倍) - 大文件传输改用
Socket+ContentProvider
方案(微信方案)
三、Jetpack
架构组件
3.1 ViewModel
配置变更后为什么能存活?
- 通过
HolderFragment+NonConfigurationInstances
机制保留引用 - 源码关键类:
ViewModelStoreOwner
3.2 LiveData
防止内存泄漏的原理?
- 基于Lifecycle的ON_DESTROY事件自动移除观察者
核心代码:
java 代码解读复制代码// LifecycleBoundObserver.onStateChanged()
if (state == DESTROYED) {
removeObserver(mObserver);
}
四、Kotlin
协程挂起函数的线程调度
4.1 协程上下文切换原理
Dispatchers.IO
实际使用LimitingDispatcher
控制并发量- 与线程池关系:共享
DefaultScheduler
底层线程池
4.2 挂起函数状态机如何实现的?
- 编译器生成
ContinuationImpl
子类管理执行状态
反编译后关键代码:
kotlin 代码解读复制代码// 编译后的挂起函数
label = when (this.label) {
0 -> { /* 初始状态处理 */ }
1 -> { /* 恢复点处理 */ }
}
五、性能优化之内存抖动检测和ANR分析实战
5.1 内存抖动如何定位?
- 使用
Allocation Tracker
抓取连续内存分配 - 典型案例:避免在
onDraw()
中创建对象
5.2 ANR日志解读?
- 分析
/data/anr/traces.txt
中的堆栈信息 - 重点关注
Binder
调用阻塞(如ContentProvider
操作)
六、Flutter
混合开发中通信(Platform Channel)原理相关
6.1 Dart
与Native
通信性能瓶颈?
MethodChannel
使用二进制序列化(优于JSON)- 大数据传输推荐
BasicMessageChannel
6.2 线程模型陷阱?
- Android端回调默认在
UI线程
执行,耗时操作需切换线程
关键代码:
java 代码解读复制代码// 在子线程执行耗时操作
new Handler(Looper.getMainLooper()).post(() -> {
result.success(data);
});
七、情景案例题分析及解答
7.1 场景一:
某头部电商APP启动时出现持续3秒的黑屏/白屏,技术方案评审会上出现两派争论:
- 客户端组认为服务端接口响应慢
- 服务端组指责客户端冗余初始化任务太多
面试时可能忽略的细节:
ContentProvider
初始化耗时(平均每个CP增加80ms)- MultiDex加载时间在Android 5.0以下设备呈指数级增长
- 经典误区: 盲目使用
IntentService
预加载数据,反而加剧CPU竞争
冷启动优化方案:
优化阶段 | 传统方案 | 进阶方案 |
---|---|---|
任务调度 | 异步初始化 | 基于Startup 库的拓扑排序 |
页面渲染 | 减少层级 | 使用ConstraintLayout 布局,减少绘制时间 |
IO 优化 | 合并SP文件 | 迁移到MMKV 提升读写性能 |
监控体系 | 手动打点 | 字节码插桩+火焰图定位瓶颈 |
7.2 场景二:
某千万级DAU的IM软件,消息列表在快速滑动时出现明显卡顿,即使使用RecyclerView
+ ViewHolder
模式仍无法解决。
常见回答:
- 布局绘制:仅关注
LinearLayout
层级,未发现Canvas.saveLayer()
导致的离屏渲染 - 内存抖动:在
onBindViewHolder
中频繁创建SimpleDateFormat
实例 - 线程管理:在滑动过程中触发网络请求,造成
MainLooper
阻塞
性能调优组合拳:
渲染层优化:
xml 代码解读复制代码
<ImageView android:src="@drawable/msg_bg" />
<View android:background="@color/msg_bg_color" />
内存治理三板斧:
- 采用
PoolingObjectPool
复用MessageItem
对象 - 使用
StrictMode
检测主线程IO操作 - 通过
LeakCanary
监控ViewHolder
泄漏
滑动优化:
java 代码解读复制代码recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
when (newState) {
SCROLL_STATE_DRAGGING -> pauseImageLoading() // 滑动时暂停图片加载
SCROLL_STATE_IDLE -> resumeImageLoading()
}
}
})
7.3 场景三:
某音视频社交APP在直播连麦场景下,当观众数超过500人时,主播端出现帧率从60FPS
暴跌至15FPS
的现象。候选人需要定位性能瓶颈并提供优化方案。
错误回答和容易忽略的细节:
- 锁定GPU过度绘制,却忽略了三重关键指标(CPU线程调度、内存抖动、I/O阻塞)
- 暴力解法:建议禁用动画/降低分辨率(破坏核心用户体验)
- 测试盲区:无法复现线下测试场景与线上真实设备差异
优化方案:
分层排查法:
- 使用
Android GPU Inspector
抓取渲染管线数据 - 结合
Systrace
分析主线程阻塞点(如Choreographer#doFrame
耗时) - 通过
Memory Profiler
检测ByteBuffer
内存抖动
代码改造示例:
java 代码解读复制代码// 优化前:同步解码+渲染
mVideoDecoder.decodeFrame(buffer);
mSurfaceView.renderFrame(frame);
// 优化后:异步双缓冲队列
ExecutorService decoderExecutor = Executors.newSingleThreadExecutor();
// 防止OOM
BlockingQueue frameQueue = new ArrayBlockingQueue<>(2);
接入大厂开源SDK(终极武器):
接入字节跳动开源的直播性能监控SDK ,实现帧率、卡顿率、CPU占用率的三维实时埋点
评论记录:
回复评论: