某车厂面试记录

自我介绍省略

问:导航服务厂商的方式是提供apk还是sdk

答:两种方案都有

问:贵司的导航方案对比高德百度或国外方案有什么优势吗

答:国内高德百度方案不是很了解,我们是做海外的,数据用的here,引擎是自研的

复盘:没有说优势,比如引擎自研,不需要天天催引擎去修复问题,功能支持定制化,迭代速度快

问:你们导航apk被用于服务各家车厂厂商,每个应该有差异化定制的,你觉得各个厂商的最大差异在哪

答:一个是界面差异,另外是功能方面,比如某厂商有独有功能组队出行,另外厂商需要路口放大图功能

复盘:没有提与车厂自有硬件和生态的整合。比如各个车厂车辆信号,接口都是不同的

问:你们apk的整体架构方案

答:code基线一样,不同厂商不同flavor

问:apk整体架构

答:两代引擎架构不同,老引擎MVC,新引擎mvvm,新引擎使用新技术多,如dagger解耦,navigation界面跳转,livedata+databinding做界面动态刷新,老引擎用JAVA开发,新引擎使用Kotlin开发

复盘:没有说为什么有两代引擎,为什么要转框架,为什么使用新技术。比如MVC耦合性过高,代码维护困难,Navigation统一了跳转逻辑,并且自动处理和保存返回栈状态,不再使用原先那套晦涩难懂的flowmanager,老引擎MVC中,数据变化后需要手动findViewById然后setText;LiveData感知生命周期自动派发更新,DataBinding把布局和数据绑定声明式写在一起,减少很多UI模板代码

问:你们的老引擎用MVC架构,那么界面和业务逻辑会耦合,你们是如何解决的

答:xml界面也是按flavor切分出去

问:这样会不会工作量太大

答:项目初期是的

问:你们遇到的项目问题可能是项目共有的,你们如何去在各个厂商里面都解决

答:我们用jira管理问题,如果问题是各个平台都有,那么会有public标签,解决后,其他项目组要么cherry pick要么rebase

复盘:没有回答到点子上,公共问题我们只改基类。基类放在公共代码区,不分厂商。改一次,所有厂商自动生效,不需要每个厂商单独去改。

问:你们的项目分flavor,应该无法cherry pick吧?

答:通过继承解决,出问题的代码一般写在基类,基类不分flavor

复盘:这里回答其实有问题,之所以cherry pick是因为项目后期其实切出主分支了 在SOP上只能cherry pick其他项目共性问题

问:你们项目是只有一个activity多个fragment吗,如何管理fragment

答:是的,我们用自己开发的一套flowmanager管理fragment,fragment对应page,page也有page start,stop,create,destroy等生命周期,flowmanager内部维护一个stack,界面跳转就是入栈出栈

问:如果fragment已经显示了,你们的入栈效果是什么样的,会弹出其他页面吗

答:不会出现你这样的问题,如果界面已经显示,flowID判断界面一样就不会跳转

问:如果不做界面跳转,那么会做弹栈吗

答:有的方法会,有的不会

问:有用到Android原生fragmentmanager管理fragment

答:没有

复盘:这块需要再研究一下代码

问:你对反射的理解

答:通过找方法找属性违规地调用系统方法去实现一个功能,项目中只有一个地方用到反射,是替换系统字体的相关代码。不推荐使用,会影响性能,除非没有其他解决方案。例如用反射和annotation可以实现IOC注解,但是最新的dagger已经不使用反射实现注解了,已经改为使用编译时生成代码,不在运行时运行反射,可以提高性能。

问:说说Kotlin的协程

答:轻量级线程,对CPU资源开销小,一个线程消耗资源大概1m,协程大概只有十分之一或更少,语法糖很厉害,能够轻松实现多线程操作

复盘:回答不够精确

Kotlin协程本质上是一个轻量级的线程框架,它的核心是挂起与恢复机制。

和传统线程相比:

资源开销小:一个线程通常占用~1MB栈空间,而协程可以小到几十字节,所以一个进程可以轻松创建成千上万个协程。

切换成本低:协程的挂起是用户态操作,不涉及操作系统内核调度,比线程切换高效得多。

语法糖:用suspend关键字标记挂起函数,配合launch、async等作用域构建器,可以用同步的代码风格写出异步的逻辑,避免传统的回调地狱。

但要注意,协程并不是为了取代线程,而是更好地利用线程。它会把协程派发到有限的线程池上执行,实现高效的并发。

问:协程里面加的日志没有打印,有哪些可能情况

答:忘了

复盘:

  1. 协程根本没有被启动或执行

launch或async的作用域被取消了(比如在ViewModel中,页面销毁时viewModelScope自动取消)。

协程被延迟启动(start = CoroutineStart.LAZY)且没有被start()或await()触发。

  1. 日志所在的代码没有运行到

协程内部有个空判断或条件分支,日志所在的那条分支没进去。

协程在到达日志前抛出了异常(且异常被静默吞掉了,没有打印堆栈)。

  1. 日志所在的协程被取消或挂起未恢复

在日志打印前调用了yield()或delay(),而协程在此过程中被外部取消了。

协程被挂起后,一直没有恢复(比如等待一个永远不会完成的CompletableDeferred)。

  1. 日志本身的问题

日志的Tag或级别被过滤了(比如设置的是Log.d(VERBOSE),但手机当前日志级别是WARN)。

在非UI线程打印日志,但使用的日志工具只绑定了主线程的日志输出(较少见,但有可能)。

问:JAVA创建多线程的方式有哪两种,他们有什么区别

答:只记得new thread+runable

问:其实是一个是用runable一个是继承thread方式

复盘:基础知识忘了

继承 Thread 类:创建一个类继承 Thread,重写 run() 方法,然后创建该类的实例并调用 start()。

实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run() 方法,然后将该实例作为参数传给 Thread 对象,再调用 Thread 的 start()。

核心区别(3点):

扩展性:Java是单继承,继承Thread后就不能再继承其他类了;而实现Runnable还可以继承其他类,更灵活。

资源共享:多个Thread对象可以执行同一个Runnable实例,天然适合共享同一份资源(比如卖票系统);继承Thread方式则需要用static变量,更麻烦。

代码分离:Runnable方式把"任务本身"和"线程运行"分离,更符合面向对象设计。

问:说说你项目中遇到的困难

答:举一个oom的例子,截图投屏功能OOM。导航通过AIDL传输5Hz的图片给中间件,期间发生OOM。调查下来发现allocateDirect方法会申请连续内存,改用allocate可以申请非连续内存以避免OOM,但是后续仍有OOM,继续调查发现是因为我创建图片存储对象byte数组和Bitmap时,每次都会new对象,这个会造成内存抖动,如果GC来不及释放内存,那么最终会导致OOM。改法是做缓存,不是每次都创建对象,而是只有对象为空的时候才创建新的对象

问:你缓存的时候,用到特殊对象么?

答:没有,缓存的时候就是将变量的声明从方法内移动到类里面,比如创建的byte数组和bitmap对象的生命都是从方法内移动到类里面

复盘:这个回答没有理解面试官的意图。问"特殊对象"是在暗示:直接缓存Bitmap和byte数组,可能会持续占用大量内存,导致其他问题。你应该回答的是如何控制缓存的大小和生命周期(比如LRU缓存、软引用等)。

///TODO

问:你刚刚说的这个投屏功能和导航进程应该不是同一个进程吧,你们是如何通信的,为什么采用这个通信方式

答:不是同一个进程,但是我们没有直接和副屏进程进行通信,在系统框架中,我们是将图片通过AIDL传输给中间件的,这个方案是系统框架决定的,客户要求我们用这样的方式进行通信。

问:你刚刚提到了Aidl 你认为Aidl发消息,其内部是多线程还是单线程?

答:单线程

问:其实是多线程,Aidl内部会使用binder线程池进行消息的发送,所以你如果不做处理,消息可能乱序,比如你发送的消息是1234,收到的消息可能是3214,你有做相关处理么?

答:我们属于客户端,单线程阻塞调用AIDL的sendIamge接口,可以确保发送顺序和接收顺序

问:图片发送的时候需要进行序列化和反序列化,你直到这是为什么么

答:可能是因为序列化后图片尺寸会变小吧?

问:你对平台化的理解?

答:底层通用,比如地图引擎,搜索,求路等,上层界面分flavor,通过继承关系,各个厂商特殊的定制部分为子类部分,公共逻辑为基类。另外有特殊接口也可以通过adapter适配特殊接口

问:如何分析ANR

答:主要看ANR的日志在哪,如果logcat日志没有,就去anr下的trace文件看看,实在没有就尝试复现,混淆代码打印的堆栈信息可能会被加密,这需要找到版本号对应的mapping文件,去解析加密的方法具体是哪个方法。

问:ANR有哪一类?这个ANR发生的条件是什么?

答:activity fragment 应该是5s超时,service是不是更长 10s或20s?记不清了,我们比较关注这个ANR如何产生和修复,不是很关系具体需要花多久会造成ANR

问:你在ANR日志看到的main线程 最多的堆栈对应的哪一行日志对应到哪个方法?

答:没有理解问题

问:单例如何写

答:Kotlin有object来写单例,Java的话一般使用双重校验的单例,使用前判断一次instance是否为空,如果不为空,那么返回实例,如果为空,那么加锁,再判断一次然后创建实例

问:这种实现,如果我用反射能不能绕开这个单例

答:不是很清楚

问:如果我不想外部用反射绕过这个单例,应该怎么做?

答:我们不是给外部提供sdk的,所以这样的场景考虑地比较少

问:导航相当于一个服务,可能连接很多客户端,这时需要创建一个注册服务和一个反注册服务的接口,这样的接口应该如何实现?

答:不清楚

问:很多client端需要用到你的导航服务,现在有一个request,客户端注册了一个监听,我们希望request不要阻塞原先的请求,我希望拿到注册的监听随时给他一个返回

答:没有懂

问:正常的内存抖动不会造成OOM,为什么你的之前AIDL传图片的例子会造成OOM

答:因为我创建的对象是图片,图片没有压缩时,尺寸比较大,压缩后也有0.1M左右,帧率又有5Hz,如果内存紧张,资源来不及释放,就可能OOM

问:针对图片这种对象,JVM是如何做回收的?

答:在方法里面定义的对象,在方法体结束后,就相当于图里一个孤立的节点,JVM在检测到对象没有可达路径时就会将对象回收

问:如何实现生产者消费者模式?你会考虑什么方面

答:我不清楚生产者消费者如何实现,但是我知道handler有用到生产者消费者模式,例如hander的sendmsg相当于生产了一个对象,looper在message队列中loop到msg然后执行handle msg时就是消费对象

问:你如何使用AI辅助工具

答:简单的基础知识,有遗忘的问网页的AI,这样回答比较迅速;复杂的问题,用GitHub copilot,他能读取源码,分析地不够到位,但是AI不够智能,可以AI和人工结对编程,一起分析问题,印证AI说的对不对

问:你有用AI工具做需求么,如何做的?

答:用提示词的方式做,但是AI做可能有问题,需要后期代码review

问:有什么问题么 略

相关推荐
程序员书虫2 小时前
Spring 依赖注入一次讲透:`@Autowired`、`@Resource`、`@Qualifier`、`@Primary` 到底怎么选
java·后端·面试
SamDeepThinking2 小时前
Spring Bean作用域的设计与使用
java·后端·面试
穿条秋裤到处跑3 小时前
每日一道leetcode(2026.04.16):距离最小相等元素查询
算法·leetcode·职场和发展
算法即正义4 小时前
知识竞赛软件的数据存储与备份方案
职场和发展·学习方法
programhelp_4 小时前
ZipRecruiter CodeSignal OA 2026|最新真题分享 + 速通攻略
数据结构·经验分享·算法·面试
C_fashionCat5 小时前
【2026面试题】前端实际场景去考察原理
前端·vue.js·面试
Mr_Xuhhh6 小时前
深入理解单链表的递归反转:从原理到实现
算法·leetcode·职场和发展
野生技术架构师6 小时前
2026年Java面试题集锦(含答案)
java·开发语言·面试
Ruihong6 小时前
你的 Vue 3 TS 类型声明,VuReact 会处理成什么样的 React?
vue.js·react.js·面试