1、冷启动流程中ActivityThread和AMS如何交互?
Launcher通过Binder通知AMS启动目标ActivityAMS检查目标进程是否存在,若不存在则通过Zygote fork新进程- 新进程入口为
ActivityThread.main(),创建主线程Looper后调用attach()向AMS注册 AMS通过ApplicationThread代理对象调度生命周期(核心IPC调用为scheduleLaunchActivity())
2、如何实现布局加载的异步化?
- 初级方案:使用
AsyncLayoutInflater,但需处理线程同步问题 - 进阶方案:
Hook LayoutInflater#inflate(),结合预编译的ViewStub池 - 终极方案:在编译期通过
APT生成布局的Java代码,完全跳过XML解析
3、Binder mmap如何保证数据一致性?
参考简答:
物理页锁定:Binder驱动通过binder_alloc_buf锁定物理页,防止换页机制导致数据丢失。内存屏障:内核使用mb()/rmb()保证多核CPU下的内存可见性。引用计数:通过binder_node的强/弱引用计数管理,确保数据生命周期与进程绑定。
4、mmap的共享缓冲区大小有限制吗?
参考简答:
默认限制:Android进程启动时通过ProcessState初始化Binder Buffer,默认大小为1MB~8MB。扩容机制:驱动动态分配物理页,但需注意传输数据超过阈值(如1MB)可能导致事务失败。
5、Binder mmap如何实现“一次拷贝”?
-
-
传统IPC的两次拷贝瓶颈
- 传统IPC(如Socket、管道)的数据传输需要两次拷贝:
- 用户态→内核态缓冲区(第一次拷贝);
- 内核态→目标用户态(第二次拷贝)。
- 这种机制导致CPU和内存开销翻倍,尤其在大数据量场景下性能骤降。
-
-
-
Binder的“一次拷贝”核心机制
-
Binder的优化关键在于共享内存映射(mmap)和Binder Buffer设计:
mmap共享缓冲区:Client与Server进程通过mmap将同一块物理内存映射到各自的用户空间和内核空间。内核驱动中转:Binder驱动仅需将数据从Client用户空间直接拷贝到Server的内核映射区,而Server可直接读取该区域,无需二次拷贝。
-
代码剖析
// binder_mmap函数核心逻辑(Linux内核源码)
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) {
// 1. 计算用户空间与内核空间的地址偏移量
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
// 2. 分配物理页并更新页表
ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);
// 3. 建立用户态与内核态共享内存
vma->vm_ops = &binder_vm_ops;
}
6、如何解决硬件加速下属性动画的闪烁问题?
参考简答:
-
1. 问题诊断:
- 使用
dumpsys gfxinfo确认是否存在Missed VSync - 检查动画View的
LayerType是否设置为LAYER_TYPE_HARDWARE
- 使用
-
2. 源码级修复:
// 自定义ObjectAnimator解决双缓冲问题
publicclassStableAnimatorextendsValueAnimator {
@Override
publicvoidanimateValue(float fraction) {
// 在UI线程执行前同步RenderThread状态
if (isHardwareAccelerated()) {
((HardwareCanvas) getCanvas()).insertSyncBarrier();
}
super.animateValue(fraction);
}
}
7、多图层叠加时Overdraw暴增如何解决?
参考简答:
-
1. 诊断工具链:
- 开启开发者选项中的"显示GPU过度绘制"
- 使用
hwui.print=true抓取详细绘制日志
-
2. Framework层优化:
// 修改LayerRenderer.cpp的混合逻辑
void LayerRenderer::drawLayer(...) {
if (layer->getAlpha() == 1.0f &&
!layer->hasBlendMode()) {
// 跳过不必要的混合计算
glDisable(GL_BLEND);
actualDraw();
glEnable(GL_BLEND);
} else {
actualDraw();
}
}
- 3. 业务层配合:对全屏遮罩层启用
setWillNotDraw(true)使用Canvas.clipRect()限制绘制区域
8、如何检测Handler内存泄漏?
参考简答:
LeakCanary监控:在Application中集成LeakCanary,观察泄漏链。MAT/Android Profiler:分析内存快照,查找残留的Activity和Handler实例。弱引用+回调判空:在Handler内部判断Activity是否已销毁。
9、主线程Looper的MessageQueue会引发泄漏吗?
参考简答:
主线程Loper生命周期与进程绑定:只要进程存活,主线程的MessageQueue始终存在。关键点在于Message的target:若Message未移除且持有Activity引用,则必然泄漏。
10、为什么Kotlin的Lambda更危险?
原因:Lambda默认持有外部类引用,等同于匿名内部类
handler.postDelayed({ doSomething() }, 10_000)
解决方案:使用WeakReference包裹外部类或手动移除Callback。