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

627 阅读5分钟

一、Handler机制

1.1 Looper.loop()为什么不会阻塞主线程?

原理: 通过epoll_wait实现消息队列的空闲等待,配合Native层唤醒机制实现高效调度

本质解析:

  • 事件驱动模型:Native层的epoll_wait机制
  • 同步屏障:MessageQueue#postSyncBarrier()

关键源码路径以及代码示例:

// 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%)

代码示例:

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实现,关键代码如下:
// 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事件自动移除观察者

核心代码:

// LifecycleBoundObserver.onStateChanged()    
if (state == DESTROYED) {    
    removeObserver(mObserver);    
}

四、Kotlin协程挂起函数的线程调度

4.1 协程上下文切换原理

  • Dispatchers.IO实际使用LimitingDispatcher控制并发量
  • 与线程池关系:共享DefaultScheduler底层线程池

4.2 挂起函数状态机如何实现的?

  • 编译器生成ContinuationImpl子类管理执行状态

反编译后关键代码:

// 编译后的挂起函数    
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 DartNative通信性能瓶颈?

  • MethodChannel使用二进制序列化(优于JSON)
  • 大数据传输推荐BasicMessageChannel

6.2 线程模型陷阱?

  • Android端回调默认在 UI线程 执行,耗时操作需切换线程

关键代码:

// 在子线程执行耗时操作    
new Handler(Looper.getMainLooper()).post(() -> {    
    result.success(data);    
});

七、情景案例题分析及解答

7.1 场景一:

某头部电商APP启动时出现持续3秒的黑屏/白屏,技术方案评审会上出现两派争论:

  • 客户端组认为服务端接口响应慢
  • 服务端组指责客户端冗余初始化任务太多

面试时可能忽略的细节:

  1. ContentProvider初始化耗时(平均每个CP增加80ms)
  2. MultiDex加载时间在Android 5.0以下设备呈指数级增长
  3. 经典误区: 盲目使用IntentService预加载数据,反而加剧CPU竞争

冷启动优化方案:

优化阶段传统方案进阶方案
任务调度异步初始化基于Startup库的拓扑排序
页面渲染减少层级使用ConstraintLayout布局,减少绘制时间
IO 优化合并SP文件迁移到MMKV提升读写性能
监控体系手动打点字节码插桩+火焰图定位瓶颈

7.2 场景二:

某千万级DAU的IM软件,消息列表在快速滑动时出现明显卡顿,即使使用RecyclerView + ViewHolder模式仍无法解决。

常见回答:

  • 布局绘制:仅关注LinearLayout层级,未发现Canvas.saveLayer()导致的离屏渲染
  • 内存抖动:在onBindViewHolder中频繁创建SimpleDateFormat实例
  • 线程管理:在滑动过程中触发网络请求,造成MainLooper阻塞

性能调优组合拳:

渲染层优化:

<!-- 优化前 -->  
<ImageView android:src="@drawable/msg_bg" />  
<!-- 优化后 -->  
<View android:background="@color/msg_bg_color" /> <!-- 用纯色替代图片 -->

内存治理三板斧:

  • 采用PoolingObjectPool复用MessageItem对象
  • 使用StrictMode检测主线程IO操作
  • 通过LeakCanary监控ViewHolder泄漏

滑动优化:

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内存抖动

代码改造示例:

// 优化前:同步解码+渲染 
mVideoDecoder.decodeFrame(buffer);
mSurfaceView.renderFrame(frame);
// 优化后:异步双缓冲队列
ExecutorService decoderExecutor = Executors.newSingleThreadExecutor();
// 防止OOM
BlockingQueue<Frame> frameQueue = new ArrayBlockingQueue<>(2); 

接入大厂开源SDK(终极武器):

接入字节跳动开源的直播性能监控SDK ,实现帧率、卡顿率、CPU占用率的三维实时埋点

更多分享

  1. Android 互联网大厂,高频重点面试题集分享(二)
  2. Android 常规基础面试题分享
  3. Android Kotlin协程相关面试题分享
  4. Android 架构以及优化相关面试题分享