别一字不差念。卡住就停一下,用自己的话说。面试官提「别用 AI」,别解释,接着聊你做过的事。
面试官好,我叫宋向荣,在深圳。做移动端快九年了,前面六年写 iOS,这三年多主要写 Flutter。
现在在 Bitbase,做 KAI 这个交易 App 的 Flutter 组长,行情和下单这条链路是我在盯。入职四个月升的组长。再往前,安克那边我一个人把 soundcore 耳机 App 双端做完上架了。更早做过游戏 SDK,也做过混编,那次把列表从三十五帧拉到五十八。
我比较爱把工程搭顺,最近把自动化回归从两天压到两小时。想找个业务重一点、能待久一点的 Flutter 团队。
不提远程、不提钱、开场别提 AI。
当时交易页挂着几十个币,高峰一秒过来 N 条。最早来一条就刷一次,主线程又解析又整页重建,滑着就卡。
我用 Profile 看了一下,是 UI 线程 build 超时,不是画不动。解析扔到 Isolate,同一帧里同一个币只留最新那条,界面只刷变了的格子。
帧率从 X 到 Y。说白了不是某个 Widget 写坏了,是数据冲太猛。
主工程是成熟的 iOS,钱包要用 Flutter 复用。刚嵌进去,进页白屏一秒多,两套返回手势还打架。
后来引擎提前热好,首帧压到两百毫秒。协议带版本号。栈还是原生说了算,私钥只放钥匙串,Flutter 只拿签名结果。钱包两端一套代码。
安卓各家蓝牙脾气不一样。OTA 要写几千个包,中途一断就得从头来,用户骂得最多就是这个。
我做成状态机,指令一个一个发,断了再连。包写上了记位置,下次接着传。最后成功率到 99%,二十多款机,就我一个人双端上架。
我最满意的是现在这个 KAI。第一次从零定 Flutter 架构,还带人,工程也真正跑起来了。东南亚交易 App,双端,八种语言,四个月升的组长。
我定了分层和模块边界,新人按模板加页不会把结构拆散。行情和下单签名是我盯的。后来把自动化回归从两天压到两小时。如果重来,状态管理我会更早统一成一套。
最满意的是安克那个耳机 App。就我一个人,五个月从需求做到双商店上架。
用 GetX 把原来的 MVC 收干净,启动快了三成。蓝牙到 OTA 我打通了。国密和音频加解密也是我接的。语音增强的算法不是我的,我负责接到耳机上。两个项目挑一个讲就行。
Flutter 自己画,不用系统按钮那些,所以两端长得一样。上面写 Dart,底下 C++ 引擎在画像素,再往下才对接安卓 iOS。
Widget 就是一张说明书,扔了再建也没事。真正活着的是 Element,状态在它身上。布局绘制是 RenderObject。所以界面在刷,不代表底下全拆了。
详解Widget 是说明书,每次都可能是新的。State 才是还活着的人,计数、输入框、动画都记在它身上,挂在 Element 上,说明书换了人还在。
它就干这几下:initState 建好控制器,build 画界面,setState 说这块脏了下一帧再画,dispose 把监听拆掉。没状态就 Stateless。
你点按钮,setState 先把数字改掉,比如 0 变成 1。它不会立刻重画屏幕。
它只是跟框架说:这个组件脏了,记下来。术语叫 markNeedsBuild,被记下来的那个活节点叫 Element。
等屏幕下一次刷新(Vsync),框架才从「脏了的这块」往下重新 build。类型一样、Key 一样就复用老节点,这步叫 canUpdate。然后量尺寸 layout,再画出来 paint。
底下写了 const 的不重跑。所以是局部刷新,不是整页拆掉重来。
详解列表会插会删,item 又有自己的状态,不加 Key 就会跟错人。我一般用 id 当 ValueKey。
用 index 当 Key 等于没加。GlobalKey 能拿到 State,但贵,表单校验才用。
详解说白了就是数据变了谁跟着刷。本页我用 setState,跨页才上框架。
soundcore 就我一个人,GetX 写得快。KAI 行情太碎,我用 Riverpod,界面 watch,按钮 read,一个币一份 family,价格格子只盯价格。单测能单独跑,不用起一整页。
我踩过的:watch 和 read 用反了会乱刷;family 的 key 不能每次 new;登录态和长连接别 autoDispose,不然一切走 WebSocket 就断。
详解Dart 其实就一条线在转。await 不会新开线程,只是先去干别的,回来再接着跑。
所以网络一般不卡界面。卡的是算得重的,大 JSON、压图,我丢 Isolate。小活别起 Isolate,光启动它就够慢。
详解先看再改。Profile 里看是 build 超时,还是画超时。build 太重就拆开、能 const 就 const、列表用 builder。画太重就圈一块单独重绘,少用透明和裁剪,图按显示大小解。
启动我拆开看:原生壳、引擎、第一帧。安卓启动别塞一堆 SDK,iOS 的 AppDelegate 也别干重活。Dart 这边 main 里少等,蓝牙、埋点放到首页出来再做。
详解我平时用得最多的是 MethodChannel,跟原生说一声,等它回。电量、传感器这种一直推的,用 EventChannel。还有个 BasicMessageChannel,两边互发消息,编解码能自己换。底层就一条管子,三种只是用法不一样。
都是异步的,原生那边回消息要回到主线程。要类型安全我会上 Pigeon。调 C 库用 FFI。地图、WebView 才走 PlatformView。往原生里嵌 Flutter,引擎提前热好,别让用户盯着白屏。
详解早先就是 push、pop,好懂。后来官方弄了声明式那套,太重,项目里我一般用 go_router。
go 是按地址把栈摆好,push 就是再压一页。没登录就 redirect。返回拦截用 PopScope。
详解父告诉子你最高多高、最宽多宽,子报自己要多大,父再摆位置。一遍就完,所以快。
Column 里直接丢 ListView 会炸,因为高度没封顶。包个 Expanded,或者给死高度。Row 里文字溢出去,也是同一回事。
详解Debug 才能热重载,改界面马上能看,状态还在。改 initState、改全局初值,热重载救不了,得热重启。
线上包是提前编成机器码的,快,但不能热重载。测卡不卡,只用 Profile,Debug 看了不算。
闪卡交易签名?
参数排好序,加上时间戳和随机数。钥匙放系统钥匙串,不写文件。token 过期只刷一次,别的请求先排队。
合约?
我没写合约。客户端只调接口,链上确认了页面再变状态。
8 语言?
官方那套翻译文件。长句子包一下,别撑破布局。
自动化?
登录到下单这条主路径自动跑,接到 CI。不写死等几秒。
组长干啥?
大部分时间还是写核心。剩下拆任务、看代码、跟对面聊。
为什么走?
业务方向 / 想待久一点。不说前司差、不说钱。
双 IM?
上面套一层自己的接口,底下融云和自研各写一份,换的时候只换工厂。
已读离线?
服务端给序号。上线把缺的拉回来。两边对不上听服务端。
列表卡吗?
布局放到后台算,高度先算好缓存着。
和 Bitbase 重叠?
同集团另一条线 / 项目合作,跟 HR 说一样的话。
蓝牙库?
flutter_blue_plus / 自研 Channel。坑最多的是安卓 12 权限,还有各家蓝牙脾气不一样。
国密?
换证书用 SM2,传数据用 SM4。音频那块走原生库,别一次读进内存。
为什么 GetX?
就我一个人,求快。Controller 跟页走,走了就放掉,不跨页乱拿。
启动快三成?
启动别扫蓝牙。等进设备页再扫。
语音增强是你做的?
算法是算法组的,我接到耳机上。
为什么 5 个月?
项目做完上架,后面节奏慢了,正好有带 Flutter 团队的机会。
Unity、Cocos?
Unity 走 C# 桥,Cocos 走 JS 桥,两边说话用同一份 JSON。
丢单?
听苹果那条交易队列。服务端验。启动时把没做完的单补上。
35 到 58 帧?
当时是 build 太重。拆开、图按显示大小解、解析别放在 build 里。
京东组件化?
一块业务一个 pod,跳转走路由,跟 H5 用 JS Bridge。
崩溃降下来?
先看崩得最多的那几个。循环引用、通知没卸,这两类最多。
为什么换得勤?
转 Flutter 之后,混编、硬件、交易所带团队这几块我想都摸一遍。安克是项目做完就结束了。现在想找个能待三五年的。
期望薪资?
现在 XK,希望 25 到 30,看我具体管哪块。
简历写远程?
那是以前接私活写的,这次就是深圳坐班。
iOS 还是 Flutter?
Flutter 为主。iOS 是底子,混编两边我都能上手。
学历?
兴华是 专科 / 自考,我十六岁就自己做上架了。深大是后来在职补的设计。作品都在网上。
29 岁 9 年?
十六岁那个是兴趣项目,正经上班从 2017 算。
规划?
还是想把 Flutter 和性能做深。带五到十个人可以,但我不想脱离写代码。
缺点?
以前 Review 太细,新人会觉得烦。后来我改成先给规范,再盯关键路径。
加班?
上线那几天没问题。平时我更在意别靠加人加班。
反问?
你们 Flutter 用哪一版?客户端几个人,我进来主要盯哪块?发版怎么走?