Android 车载系统软件开发?助你面试一把过!
Q1. 自定义 View 的流程
通常是父布局通过递归先测量所有子视图的大小,再计算位置,然后从上到下依次绘制。
GPU会将这些绘制指令渲染到图形缓冲区,再传递 (BufferQueue) 给 SurfaceFlinger 进行合成, 最后交给显示硬件(HAL层)刷新屏幕。
Q2. View的事件传递机制
事件的起点通常是 Activity 的 DecorView(根布局), 从这里依次向下分发,如果没有被视图组 (ViewGroup) 拦截的话,事件会一直走到 View, 如果 View 都没有消耗这个事件, 就会向上“回溯”, 最终回到 Activity 处理。
核心方法论: 事件分发依赖于三个核心方法:
- dispatchTouchEvent():分发。
- 返回 true 当前视图是责任人,事件将不再向上回溯
- 返回 false 事件回溯给父级,不触发自己的 onTouchEvent
- 当调用 super 方法时,如果是 ViewGroup 就会调用子视图的 dispatchTouchEvent 询问它是否消耗了事件
- onInterceptTouchEvent():拦截(仅属于 ViewGroup)。
- onTouchEvent():消费。
- 滑动冲突可使用 parent.requestDisallowInterceptTouchEvent 通知父容器不再拦截事件。
事件没有被消耗,最终回到 Activity onTouchEvent:
事件被 view3 处理,不再回溯:
Q3. ANR 出现的原因
对于 APP 来说 ANR 出现的原因无非就是三大类:
- 主线程压力过大,正在高负荷处理各种任务。比如:CPU计算、GPU高频渲染等
- 内存占用过高,APP的运行内存不够。比如:内存泄露、加载大图等
- 主线程在等待别的任务。比如:死锁、同步 Binder 调用
如何通过日志分析?
首先搜索包名锁定 main 线程,若状态为 Blocked 则通过 held by 寻找持锁线程,若为 Runnable
则分析堆栈定位耗时的业务代码;其次关注 Native 状态下的 Binder 调用,确认是否因对端进程无响应导致跨进程通信卡死;最后通过日志顶部的 CPU usage 确认 io wait 占比,从而快速判定是代码逻辑过载、后台抢锁冲突还是磁盘 IO 瓶颈。
PS: 跨进程通信(IPC)卡死通常由 Binder 同步调用 机制引发,例如:跨进程死锁、对端进程(服务端)忙碌或阻塞、系统服务(SystemServer)全局锁竞争
Q4.CAN 协议应用层处理建议
协议层的实现通常是由系统完成的,应用端通过反射去拿实例,通过这个实例,传入 handle 接收信号,通过系统提供给我们的 write 去写入指令, 写入后系统通过 handle 下发值,之后根据 handle传过来的值进行解析与封装,解析这一块的逻辑比较长,有很多二进制和十进制的判断,解析后调用相关的接口分发,封装是通过观察者模式,使用并发的 Map 集合(ConcurrentHashMap)存取 指令和接口的集合,暴露出订阅(observe)、解绑(remove)方法,外部调用这个订阅方法,传入需要的指令,例如:车门、车灯等,就可以处理相关的业务了。
Q5. 请简短说明 MQTT 的三种模式 和 WebSocket 接口有哪几个回调方法作用
MQTT 的“模式”通常指其服务质量等级,决定了消息传递的可靠性:
- QoS 0 (At most once): 最多一次。消息发送后不确认,可能会丢失。适用于对实时性要求高但允许丢包的场景(如传感器频率数据)。
- QoS 1 (At least once): 至少一次。确保消息到达,但由于确认机制,接收方可能会收到重复消息。
- QoS 2 (Exactly once): 只有一次。通过四步握手确保消息不丢不重,开销最大,适用于金融支付等严谨场景。
WebSocket 协议主要通过以下四个事件回调进行生命周期管理:
- onOpen:
作用: 连接建立成功时触发。
场景: 此时可以开始向服务端发送数据,通常用于初始化握手或发送心跳。 - onMessage:
作用: 接收到服务端推送到客户端的数据时触发。
场景: 处理业务逻辑,如解析 JSON 数据、更新 UI。 - onError:
作用: 通信过程中发生错误(如网络中断、协议非法)时触发。
场景: 用于异常捕获和日志记录。 - onClose:
作用: 连接关闭时触发。
场景: 执行清理操作,或根据业务需求启动断线重连机制。
