Android常见面试题
1. Handler机制(Native层实现及延时消息流程)
Handler机制是Android的消息驱动核心,由Java层的Looper、MessageQueue、Handler和Native层的Looper、MessageQueue共同协作实现。
Native层实现原理
- 基于Linux epoll和eventfd: 从Android 2.3开始,Native层使用epoll(I/O多路复用)替代Java的wait/notify机制,以实现更高效的阻塞/唤醒。 初始化流程: Java层MessageQueue的构造函数中调用nativeInit(),在Native层创建NativeMessageQueue和Looper对象。 Native Looper构造函数会创建eventfd(用于计数通知的句柄)和epoll实例,并将eventfd注册到epoll池中监听可读事件。 阻塞与唤醒: 当消息队列为空时,线程阻塞在nativePollOnce()→ Looper::pollInner()→ epoll_wait(),等待事件发生。 当有新消息加入时,nativeWake()会向eventfd写入数据,触发epoll_wait返回,线程被唤醒。
延时5秒消息的源码流程
- Java层: Handler.sendMessageDelayed(msg, 5000)计算触发时间(当前时间+延时),调用MessageQueue.enqueueMessage()按时间顺序插入消息链表。 如果新消息是队列中最早触发的,则调用nativeWake()唤醒线程。
- Native层: nativePollOnce()的超时参数根据最近消息的触发时间动态调整(例如5秒后到期,则超时设为5000ms)。 超时到达或被唤醒后,epoll_wait返回,Looper从消息队列取出到期消息交Java层处理。
2. View绘制流程与MeasureSpec Mode
绘制流程三阶段
- Measure(测量): 从ViewRootImpl.performTraversals()开始,递归调用每个View的measure()方法。 最终调用onMeasure()确定View的宽高,其中使用MeasureSpec(32位整型,高2位为Mode,低30位为Size)约束尺寸。
- Layout(布局): 父View通过onLayout()确定子View在容器中的位置(左上右下坐标)。
- Draw(绘制): 依次绘制背景、内容(onDraw())、子View(dispatchDraw())、装饰(如滚动条)。
MeasureSpec的三种Mode
- EXACTLY:精确模式(对应match_parent或具体数值),View必须使用SpecSize作为最终尺寸。
- AT_MOST:最大模式(对应wrap_content),View尺寸不能超过SpecSize。
- UNSPECIFIED:不指定模式(如ScrollView测量子View),View可任意大小,一般用于系统内部。
3. 事件分发机制与ACTION_CANCEL触发条件
事件分发流程
事件从Activity.dispatchTouchEvent()开始,依次经过ViewGroup和View的onInterceptTouchEvent()、onTouchEvent()方法,采用责任链模式传递。
ACTION_CANCEL触发场景
当以下情况发生时,子View会收到ACTION_CANCEL事件:
- 父View拦截事件: 子View消耗了ACTION_DOWN事件,但后续ACTION_MOVE被父View的onInterceptTouchEvent()拦截,子View收到CANCEL。 例如:ScrollView在滑动时拦截事件,导致子Button触发CANCEL。
- 手势移出子View边界: 手指从子View内滑动到外部,子View收到CANCEL。
- 强制禁止拦截: 子View通过requestDisallowInterceptTouchEvent()设置FLAG_DISALLOW_INTERCEPT,可临时阻止父View拦截(但ACTION_DOWN无法禁止)。
4. OOM(Out Of Memory)问题
注:搜索结果未直接覆盖OOM,以下基于通用知识补充。
OOM指应用内存超过Java堆上限(通常由内存泄漏、大图片加载、多线程累积对象等引起)。
- 常见场景: 静态集合类持有Activity导致无法回收。 Bitmap未复用或未及时回收。
- 解决方向:使用内存分析工具(如Android Profiler)检测堆转储。
5. 内存泄漏检测与LeakCanary原理
注:搜索结果未包含此主题,以下基于通用知识说明。
检测方法
- 手动方式:通过Android Profiler观察堆内存是否持续增长。
- 自动化工具:如LeakCanary自动检测Activity/Fragment的泄漏。
LeakCanary原理简析
- 监听对象生命周期: 通过Application.registerActivityLifecycleCallbacks()监听Activity销毁。
- 触发检测: Activity销毁后,创建弱引用(WeakReference)并触发GC。
- 分析引用链: 若弱引用未被回收,说明存在泄漏,Dump堆内存并分析泄漏路径。
监听泄漏的方式
- LeakCanary内置ObjectWatcher,通过判断对象是否在GC后存活来确认泄漏。
6. 平行窗口与大屏兼容
注:搜索结果未涉及此主题。
平行窗口是折叠屏/大屏设备的多任务功能,允许同一应用分屏显示不同界面。
适配关键点:
- 使用限定符(如sw600dp)为不同屏幕提供布局。
- 支持多窗口模式:在AndroidManifest中设置android:resizeableActivity="true"。
- 避免硬编码尺寸,使用ConstraintLayout等自适应布局。
7. 进程间通信机制与Messenger实现
IPC方式概述
注:搜索结果未详细展开,以下为常见方式。
Android支持的IPC机制包括:Binder、AIDL、Messenger、ContentProvider、Socket、文件共享。
Messenger原理
- 基于Binder的封装: Messenger通过AIDL实现跨进程消息传递,内部持有IMessenger接口的Binder代理。
- 工作流程: 服务端实现Handler并创建Messenger绑定到Service。 客户端通过IBinder获取Messenger代理,发送Message(支持回调回复)。
- 特点:串行处理消息,无需自己处理线程同步。
8. View如何绘制到Window上
注:部分逻辑可参考的绘制流程。
- Window绑定: Activity启动时,Window(具体实现为PhoneWindow)通过setContentView()加载DecorView。
- 视图附加: ViewRootImpl通过addView()将DecorView添加到WMS(WindowManager Service),创建Surface用于绘制。
- 触发绘制: ViewRootImpl.performTraversals()统筹Measure、Layout、Draw三阶段,最终通过Surface将数据提交给底层渲染。
9. MVVM实现原理
注:搜索结果未覆盖,以下基于通用知识。
MVVM通过数据绑定实现View和Model的解耦:
- 核心组件: Model:数据层(如数据库、网络)。 View:界面(Activity/Fragment),通过DataBinding观察ViewModel数据。 ViewModel:保存UI相关数据,生命周期长于View,常用LiveData或Flow通知数据变化。
- 关键技巧: 使用Jetpack的ViewModel+LiveData自动处理配置变更(如旋转屏幕)。
10. AIDL实现与回调线程
AIDL实现步骤
- 定义.aidl接口,声明方法及回调接口(oneway关键字可支持异步调用)。
- 服务端实现Stub类,重写业务方法。
- 客户端绑定Service,将IBinder转换为接口对象调用远程方法。
回调线程问题
- 默认线程:AIDL回调在客户端的Binder线程池中执行,非主线程。
- 切换主线程:需在回调中通过Handler将逻辑抛到主线程(如Activity.runOnUiThread())。
11. Java锁:Lock与synchronized区别与原理
注:搜索结果未涉及,以下基于并发知识总结。
获取方式 | 自动加锁/释放锁 | 需手动 |
灵活性 | 代码块范围锁,不可中断 | 可尝试获取锁、设置超时、响应中断 |
公平性 | 非公平 | 可选公平或非公平模式 |
底层原理 | 基于JVM Monitor机制(对象头MarkWord) | 依赖AQS(AbstractQueuedSynchronizer)队列 |
原理简述
- synchronized:通过字节码指令monitorenter/monitorexit和对象监视器(Monitor)实现互斥。
- Lock:内部通过AQS维护等待队列,通过CAS操作竞争锁资源。
