Android 互联网大厂,高频重点面试题集分享(五)

206 阅读4分钟

1、冷启动流程中ActivityThread和AMS如何交互?

  1. Launcher通过Binder通知AMS启动目标Activity
  2. AMS检查目标进程是否存在,若不存在则通过Zygote fork新进程
  3. 新进程入口为ActivityThread.main(),创建主线程Looper后调用attach()向AMS注册
  4. AMS通过ApplicationThread代理对象调度生命周期(核心IPC调用为scheduleLaunchActivity()

2、如何实现布局加载的异步化?

  1. 初级方案:使用AsyncLayoutInflater,但需处理线程同步问题
  2. 进阶方案Hook LayoutInflater#inflate(),结合预编译的ViewStub池
  3. 终极方案:在编译期通过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如何实现“一次拷贝”?

    1. 传统IPC的两次拷贝瓶颈

    • 传统IPC(如Socket、管道)的数据传输需要两次拷贝:
      • 用户态→内核态缓冲区(第一次拷贝);
      • 内核态→目标用户态(第二次拷贝)。
    • 这种机制导致CPU和内存开销翻倍,尤其在大数据量场景下性能骤降。
    1. 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。

更多分享

  1. Android 互联网大厂,高频重点面试题集分享(一)
  2. Android 互联网大厂,高频重点面试题集分享(二)
  3. Android 互联网大厂,高频重点面试题集分享(三)
  4. Android 互联网大厂,高频重点面试题集分享(四)