尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Flutter鸿蒙适配实战:json_rpc_2库双向通信改造全解析
最近把公司的 Flutter 应用向鸿蒙系统做迁移适配整个过程里json_rpc_2 这个三方库的鸿蒙化适配算是比较典型的一个案例。json_rpc_2 是 dart-lang 官方维护的一个 JSON-RPC 2.0 协议实现许多 Flutter 项目拿它来做长连接双向通信、组件间调用、设备间消息传递。虽然它是纯 Dart 实现没有原生代码依赖看起来就像“直接加进 pubspec 就能跑”但真正在鸿蒙工程里运行起来才发现协议层面、线程调度、网络权限、断线处理这些环节都需要重新审视和改造。这篇就基于我实际踩过坑的适配过程把怎么定义结构化通讯准则、怎么理解 JSON-RPC 2.0 在鸿蒙场景下的实战要点、怎么设计鸿蒙级的双向交互链路完整拆开讲一遍。内容面向正在做 Flutter 鸿蒙化、或者打算在三方库适配方向上动手的开发者也适合想深入理解 json_rpc_2 内部机制的朋友做参考。1. 项目背景与设计思路1.1 为什么偏偏选 json_rpc_2 做鸿蒙化适配先说背景。Flutter 要跑在鸿蒙上目前主流路线是使用支持 OpenHarmony 的 Flutter 分支配合 DevEco Studio 搭一套混合工程。这套链路里最麻烦的不是 Flutter 本身而是项目用到的各种三方库。有些库带原生依赖需要重编 so 或者桥接 Platform Channel有些库虽然是纯 Dart却在底层调用了 dart:io 的 Socket、HttpClient 或者 isolate 相关能力这些在鸿蒙运行时里不一定表现一致。json_rpc_2 属于比较特殊的一类它本身是协议层实现不关心底层传输管道是什么只要上层能给它一个符合 StreamChannel 规范的通道。所以在鸿蒙上适配 json_rpc_2核心任务不是“改库”而是“重新设计通道”。真正决定适配成败的是你给 json_rpc_2 喂进去的那个 Stream 流稳不稳定、序不序、通不通。我当时选它做鸿蒙化试点还有一个原因项目里很多模块之间的双向调用比如 Flutter 侧发控制指令给鸿蒙原生侧原生侧再把状态变化推回 Flutter 侧长期依赖自造的 MethodChannel 封装方法名满天飞、参数结构靠注释约定、错误处理各写各的。重构的时候就想找一个统一协议模型json_rpc_2 刚好是现成的答案——协议完备、官方维护、社区验证充分还带一套标准的错误码体系很适合当作“结构化通讯准则”的底座。1.2 结构化通讯准则到底在定义什么“结构化通讯准则”听起来很抽象但落到工程上就是四件事方法名怎么命名、参数怎么传、错误怎么报、时序怎么控制。没有这套准则双端联调用不了多久就会陷入混乱Flutter 侧写了一个sendCmd鸿蒙侧叫dispatchCommand参数一会儿传 Map 一会儿传 JSON 字符串出错以后返回的字段不统一联调全靠对着源码逐字猜。我在这次适配里定了一套 JSON-RPC 2.0 之上的项目级规范举几个具体规则方法名采用domain.action三段式比如device.setBrightness、system.getBatteryInfo、app.switchScene禁止缩写禁止下划线命名。参数统一为 JSON 对象键名采用 camelCase所有 bool 类型必须显式传true/false不允许用0/1代替。错误码在 JSON-RPC 2.0 标准错误码之上扩展一套业务错误码比如1001表示设备离线1002表示参数校验失败1003表示操作超时格式固定为{code: 1001, message: device offline, data: {...}}。超时控制统一在客户端侧设置不同方法允许配置不同的超时时长服务器端不承担超时判断职责。这套准则的好处是凡是走 JSON-RPC 通道的调用无论发生在 Flutter 和鸿蒙原生之间还是鸿蒙多设备之间都适用同一套语义。后面接入新的业务模块时不需要再纠结协议设计照着方法名分段表、参数规范表、错误码字典直接填充即可。1.3 技术选型对比为什么 JSON-RPC 2.0 比自定义通道更合适市面上常见的 Flutter 双端通信方案大致分三类MethodChannel 函数调用、事件总线EventBus / Stream、REST/WebSocket 自研协议。MethodChannel 适合低频、单次的调用鸿蒙原生和 Flutter 之间直接桥接没问题但方法多了以后格式难以统一也难做多端扩展。事件总线适合单向广播做“A 触发 B 处理”很方便但一问一答的强时序交互表达起来就很别扭。REST 适合跨公网的客户端-服务端模型天然不适合设备本地或局域网内的高频双向通信。JSON-RPC 2.0 在这个场景里正好补上空白。它定义了一个对称的调用模型请求方发出带id的请求应答方必须回带相同id的响应其中一方也可以只发不带id的通知用于单向事件广播对端不需要应答。这让同一套通道既能做 request-response又能做 fire-and-forget正好覆盖鸿蒙应用里“发指令、收状态、订阅事件”三类核心通信模式。下表是我在技术选型时做的一个对比对比维度MethodChannelEventBusRESTJSON-RPC 2.0调用语义单次函数调用单向广播请求响应请求响应通知协议格式无统一规定无统一规定HTTP 状态码标准 JSON 对象错误处理自造无HTTP 状态码标准错误码多端支持弱弱强强鸿蒙适配成本中等低中等先难后易调试友好性一般一般好非常好从表格也能看出来JSON-RPC 2.0 的鸿蒙化适配成本前期不低因为要先搭通道、定规范但一旦跑通后续扩展业务就非常顺。尤其是鸿蒙场景天然讲“多设备协同”JSON-RPC 的对称模型是几个方案里最适合将来做设备间一对一、一对多通信的。2. JSON-RPC 2.0 协议与 json_rpc_2 库的底层逻辑2.1 JSON-RPC 2.0 核心规范到底在说什么理解 JSON-RPC 2.0只需要抓住“两套对象、三类消息”。所谓两套对象指请求/通知和响应/错误它们在 JSON 结构上是不同的三类消息分别是请求Request、通知Notification和响应Response含错误响应。一个符合规范的请求长这样{ jsonrpc: 2.0, method: device.setBrightness, params: { level: 80 }, id: 1 }对应的正常响应{ jsonrpc: 2.0, result: { success: true }, id: 1 }如果处理过程中出错则响应里放的是error而不是result{ jsonrpc: 2.0, error: { code: 1002, message: invalid params }, id: 1 }通知则是省略id的请求接收方收到后执行操作但不返回任何内容{ jsonrpc: 2.0, method: system.heartbeat, params: { timestamp: 1713350000 } }协议里还定义了几个标准错误码从-32700到-32000分别对应解析错误、无效请求、方法不存在、无效参数、内部错误等。很多初用 json_rpc_2 的人容易忽略一点JSON-RPC 2.0 允许params是数组或者对象。json_rpc_2库两者都支持但我强烈建议统一用对象。数组参数一旦方法签名调整位置型参数很容易出现前后端错位对象参数加一个字段或者废弃一个字段的兼容成本要低得多。2.2 json_rpc_2 库的内部结构和工作原理json_rpc_2 包的核心抽象是Peer类。一个Peer代表通信双方中的一方它既负责发出请求也负责接收并响应对方发来的请求。通过registerMethod向对端暴露本地能力通过sendRequest、sendNotification向对端发起调用。Client和Server本质上都是Peer的封装变体——Client主要扮演调用方的角色很少注册方法Server主要扮演被调用方的角色很少主动发请求。实际鸿蒙适配中如果你需要两边互相调用直接用Peer就行。库的底层数据流是 Stream 驱动的。构造Peer时需要传入一个StreamChannelString这个通道负责把 Dart 侧的字符串消息传给远端同时把远端发来的字符串消息以 Stream 的形式喂给Peer。Peer内部对收到的 JSON 字符串做解析如果是请求则按方法名分发给对应的 handler如果是响应则按id匹配之前挂起的请求 Completer。翻源码的时候注意到一个关键细节Peer在处理请求时如果 handler 抛出了异常库会兜底返回一个ApplicationError错误响应并不会让整个流崩掉。这个设计对鸿蒙场景非常重要——原生侧上报的数据偶尔格式不完整只要 handler 内部做了捕获就不会影响后续请求的处理。我在适配时还重新阅读了json_rpc_2的事件循环处理方式。它基于StreamQueue逐条读取消息不依赖额外的队列库。也就是说只要底层通道不乱序、不丢包Peer自身不会字节级乱序。这一点在后续排查鸿蒙网络通道问题时帮了大忙——绝大多数乱序现象都出在 Socket 或 WebSocket 传输层而不是 json_rpc_2 协议层。2.3 为什么鸿蒙场景特别吃“协议规范化”这一套鸿蒙开发里有一个经常被提到的词叫“分布式能力”。传统意义上你写一个 App 只在手机上跑通信需求也就是 Flutter UI 和原生系统能力之间的桥接。但鸿蒙应用天然有跨设备调用的诉求手机上发起任务平板承接大屏展示传感器数据从周边设备回传。这种场景下通信双方不再局限于“一个 Flutter 引擎、一个鸿蒙原生壳”而可能演变成“多个设备、多种运行时、多种传输介质”的混合通信网络。如果还按 MethodChannel 思路每个设备、每个运行时都各自维护一套私有通信协议跨端联调会迅速失控。JSON-RPC 2.0 的通用性在这里体现出来协议只有一个 JSON 文件篇幅那么长任何语言都能实现方法名、参数、错误码全部是字符串和数字可以用统一文档管理断线重连、超时重试逻辑也可以做在通道层协议层保持稳定。所以我认为鸿蒙级双向交互的底层必须是一个“结构化准则”级别的协议模型而不是临时拼装的私有格式。json_rpc_2 正好提供了一套经过多年验证的实现。3. 鸿蒙化适配的完整实操过程3.1 环境准备与工程结构搭建本次适配基于 Flutter 的 OpenHarmony 分支 SDK配合 DevEco Studio 构建鸿蒙端壳工程。Flutter 工程作为核心业务层鸿蒙壳工程负责系统能力、权限管理和原生资源加载。依赖取消避照常规方式写入 pubspec.yamldependencies: flutter: sdk: flutter json_rpc_2: ^3.0.2 web_socket_channel: ^2.4.0 stream_channel: ^2.1.1web_socket_channel和stream_channel不是 json_rpc_2 运行的必要依赖但适配时大概率会用到。因为它们能帮你把 Socket、WebSocket 等实际传输链路包装成StreamChannel。鸿蒙侧需要重点检查的是权限声明。如果你打算走 TCP Socket 或 WebSocket 通信必须确保 module 的 module.json5 里加了ohos.permission.INTERNET否则连接阶段会静默失败或者直接抛SocketException。我最初适配时就漏了这步排查了很久才发现请求根本没出手机。工程结构上我做了分层lib/ core/ # json_rpc_2 适配层 channel.dart # 自定义 StreamChannel 实现桥接鸿蒙 Socket/WebSocket rpc_client.dart # JsonRPC 客户端封装统一超时、日志、错误映射 rpc_server.dart # JsonRPC 服务端封装注册鸿蒙能力 models/ # 请求响应数据模型 pages/ # 业务 UI services/ # 业务 Service调用 RPC 层这样的好处是上层业务完全不知道底层走的是鸿蒙 Socket、WebSocket 还是普通网络字节流所有通信都收敛到channel.dart里后续如果想换传输方式只需替换这一个文件。3.2 通信链路构建从鸿蒙原生收流到 json_rpc_2适配的核心难点在通信链路。鸿蒙原生侧有一个服务端能力Flutter 侧作为客户端去连接它。或者反过来由鸿蒙侧主动连接 Flutter 侧开放的端口。我们在实际项目中采用的是 Flutter 作为客户端、鸿蒙原生作为服务端通过 TCP Socket 承载 JSON-RPC 流量。鸿蒙原生侧用kit.NetworkKit的 Socket 能力创建了一个监听端口Flutter 侧用dart:io的Socket.connect建立连接。连接建立之后原始字节流不能直接丢给 json_rpc_2需要做两步处理第一步是按 UTF-8 解码为字符串第二步是处理消息边界。消息边界是这里最容易被忽略的技术点。TCP 是流式协议一次write的内容不一定在另一端一次性到达可能出现半包、粘包。json_rpc_2 本身不处理消息边界它只认完整的 JSON 文本。因此我采用了“每帧消息加一个四字节长度头”的做法发送方先写四个字节的大端整数表示后续 JSON 字符串长度再写入 JSON 字节接收方先读四字节拿到长度再按长度读取对应的 JSON 字符串。这套逻辑封装在自定义的LengthPrefixedStreamChannel里import dart:async; import dart:convert; import dart:io; import dart:typed_data; import package:stream_channel/stream_channel.dart; class LengthPrefixedChannel extends StreamChannelMixinString { override final StreamString stream; final StreamSinkString sink; LengthPrefixedChannel(this.stream, this.sink); }实现通道时整体思路是对 Socket 的字节流做长度前缀协议解析。为了保证发送时每条 JSON 字符串都正确附带长度头所有写入操作都走同一个StreamSink避免多个调用并发写导致字节交错。我在发送端还额外做了同步锁确保两个方法同时调用sink.add时不会阻塞或者交叉写入。链接完成后把LengthPrefixedChannel包装成StreamChannelString直接传入 json_rpc_2 的构造final channel LengthPrefixedChannel(stream, sink); final peer json_rpc.Peer(channel.castString());这一步跑通后意味着底层传输已经和协议层解耦。后面不管是换 WebSocket、换 Unix Domain Socket 还是走鸿蒙的软总线能力只需要再实现一个符合相同接口的StreamChannel即可。3.3 双向交互的具体实现请求、响应、通知一个都不能少通道建好以后接下来是注册方法和发起调用。双向交互的实践逻辑全都体现在这里。鸿蒙侧作为被调用方需要注册一批“鸿蒙能力”方法比如获取设备信息、控制传感器、切换系统设置等。Flutter 侧只需要直接调用这些方法名不用关心鸿蒙原生代码具体怎么实现peer.registerMethod(device.getInfo, (parameters) async { final deviceInfo await _harmonyService.getDeviceInfo(); return deviceInfo; }); peer.registerMethod(system.setWifiEnabled, (parameters) async { final enabled parameters[enabled] as bool; await _harmonyService.setWifiEnabled(enabled); return {success: true}; });返回值直接写普通 Map 即可json_rpc_2 会自动序列化为 JSON 响应。如果 handler 内部需要抛错库约定是抛json_rpc.RpcError并带自定义业务错误码throw json_rpc.RpcError( 1001, device offline, data: {detail: device cannot be reached}, );Flutter 侧作为调用方时使用sendRequest发起调用并拿到Future等待结果final result await peer.sendRequest( system.setWifiEnabled, {enabled: true}, ); print(result[success]);这里有个非常重要的体验点sendRequest如果没有指定超时理论上是无限等待的直到对端返回响应或者连接断开。在正常通信中这问题不大但鸿蒙场景里设备可能随时进入休眠、断开网络或切换 WiFi一旦对端消失这个Future会一直 pending调用方的 await 就卡死了。因此我在适配时把超时逻辑统一封装在RpcClient里Futuredynamic request(String method, Map params) async { final completer Completerdynamic(); final id peer.sendRequest(method, params).then(completer.complete).catchError(completer.completeError); Future.delayed(timeout).then((_) { if (!completer.isCompleted) { completer.completeError(request timeout); } }); return completer.future; }这里不是让你照抄而是强调“凡是网络调用必须有超时保护”这个原则。哪怕鸿蒙设备之间明明很近协议层也可能因为系统调度而延迟超时保护是保证 UI 响应体验的最后一道防线。通知的使用也很关键。类似“设备电量发生变化”“系统设置被其他应用修改”这种事件性信息不能每毫秒都让客户端轮询而是让鸿蒙原生侧在事件发生时主动推给 Flutter 侧。json_rpc_2 的做法是调用sendNotificationpeer.sendNotification(event.batteryChanged, {level: 30});Flutter 侧如果也要反向主动给鸿蒙侧推送消息同一个Peer上同样调用sendNotification即可。之所以强调用通知而不是请求是因为通知不给对端回任何消息省掉一次响应开销也避免对端需要维护一个没有用的请求 id。高频状态上报场景下这个差异非常明显。3.4 结构化通讯准则的落地命名、校验、错误码字典规范容易写落地才是难点。我在工程里做了三件事确保刚才定义的通讯准则真正做到“强制有效”。第一件是方法名注册表。在rpc_server.dart中定义本地方法时不允许直接用裸字符串注册而是通过一个静态常量类统一管理class RpcMethods { static const getDeviceInfo device.getInfo; static const setWifiEnabled system.setWifiEnabled; static const batteryChangedEvent event.batteryChanged; }业务层调用时强制引用常量否则编译期就报错。这个方法成本极低但可以避免散落字符串带来的拼写错误和维护噩梦。第二件是参数校验。JSON-RPC 2.0 本身只保证传输结构不保证参数内容合理。我建议在方法 handler 里统一先跑一遍校验工具比如检查类型、检查必填、检查范围。校验失败直接抛invalid params错误码业务层不参与。第三件是错误码字典。我把所有可能出现的错误码集中放在一个枚举类里并写了对应的RpcErrorSerializer辅助链路日志和联调工具统一解析。这样哪怕错误发生在鸿蒙原生侧、Flutter 侧的日志里也能立即看出问题归属。4. 常见问题与排查技巧实录4.1 鸿蒙适配中的经典坑权限、Socket、数据编码整个适配过程中我遇见的坑大致能分成三类每一个都在网上翻了很久才找到合适解法。第一个坑是权限。鸿蒙的权限声明不是简单的AndroidManifest.xml一行就能搞定的。如果 Flutter 侧的 TCP Socket 连接一直抛出连接失败但没有报权限拒绝的明显信息先检查 module.json5 里是否配置了ohos.permission.INTERNET。漏掉这一步的典型表现是连本机回环地址127.0.0.1都会失败因为鸿蒙的 Socket 创建直接受权限管控。第二个坑是 Socket 半包粘包。前面提到我做了长度前缀协议这个设计不是闲来无事的炫技而是实际遭遇过第一个 JSON-RPC 请求到达后鸿蒙侧拿到的字符串是“半个请求”第二个请求来的时候拼接后的 JSON 又解不出来。如果不处理消息边界你会看到 json_rpc_2 那边频繁弹出 JSON 解析异常。这种现象和字节流分片有关。解决办法就是我前面说的在发送和接收两层都严格按四字节长度头处理。第三个坑是数据编码。鸿蒙原生侧拿到字节流后如果按系统默认编码解析某些中文字符会直接乱码。协议层必须统一用 UTF-8两端都显式指定。另外注意 JSON 字符串不要携带 BOM 头。从鸿蒙原生某些 Socket API 里取出的字节流可能会带 BOM直接解析会让 json_rpc_2 报FormatException。4.2 稳定性设计断线重连、心跳守护、请求队列适配完成后真正上路的稳定性问题才浮现。鸿蒙设备尤其是手机系统资源紧张时会杀掉后台 Socket 连接或者系统切到省电模式后断开网络这会让 Flutter 侧持有的Peer直接失效。json_rpc_2 的Peer在底层通道关闭后不会自动重建所以断线重连必须由应用层实现。我实践的方案是封装了一个RpcConnectionManager主要职责有三个轮询检测连接状态、自动重连、连接恢复后重新注册方法。连接状态检测是用定时器定期向鸿蒙侧发送一个心跳通知比如每 3 秒发一次system.ping。如果连续三次都没有收到任何响应包括通知的转发和请求的响应就判定连接已断开。注意心跳本身用通知模式这样不会在积攒大量待响应请求时造成无谓的超时。重连策略采用指数退避第一次重连等待 1 秒第二次 2 秒第三次 4 秒上限 30 秒避免设备在信号不稳定期间频繁发起连接请求。请求队列也是容易忽略的一环。断线瞬间可能还有一批sendRequest正在等待结果如果不处理这些请求会一直悬空。我建议在RpcConnectionManager里挂一个pendingRequests表在连接断开时统一以业务错误码1001设备离线Complete 掉这些 Future让上层立刻感知而不是卡到超时。4.3 调试技巧日志链路、镜像抓包、单测模拟JSON-RPC 的调试友好性在鸿蒙工程里体现得很充分因为所有消息都是明文 JSON随便找一个本地代理工具就能抓到完整报文。我流程化地建了三条调试手段。第一在通道层打完整日志。每一条发出去的消息和每一条接收到的消息都打印到日志里并标注时间戳和收发方向。这样做看起来很笨但排查粘包问题、乱序问题、字段缺失问题非常高效。第二针对关键方法做单测。json_rpc_2 支持在测试环境里直接构造Peer两端都跑在同一进程内通过StreamController模拟通道这样能快速验证方法注册、参数匹配、错误码映射。第三压测时记录响应时间分布。比如统计 100 次device.getInfo调用的耗时平均耗时应低于几十毫秒如果出现明显波峰大概率是鸿蒙原生侧方法注册区的某个处理流程偶发耗时。这里我还想补充一个排查小工具给每个请求的data或params里加一个traceId字段。这个方法非常土但是效果极好。分布式调试时Flutter 侧日志和鸿蒙原生侧日志按traceId一拼就能定位整条调用链路不用再看时间靠猜。5. 个人实操心得与后续扩展5.1 这次适配里最深的三个体会回头看这次 json_rpc_2 鸿蒙化适配有几点体会特别值得分享。第一不要因为“纯 Dart 包”就低估鸿蒙化适配的工作量。json_rpc_2 虽然不涉及原生插件但作为三方库它的可用性高度依赖底层通道。通道不稳协议再好也白搭。适配一个三方库本质上适配的是它和你工程里所有基础设施之间的接口约定而不是单纯编译通过就完事。第二先写规范再写代码真的能省掉大量联调时间。项目早期我偷懒没定统一的错误码和方法名段结果 Flutter 侧和鸿蒙原生侧各写各的联调时发现同一功能两边命名对不上同一个错误返回了三种不同格式。后来花了小半天统一规范后续几乎没再因为通信格式问题返工。这个成本投入非常划算。第三双向交互设计里一定要搞清楚什么是强请求什么是弱事件。如果把所有事件都做成sendRequest对端不仅要做大量无谓响应还要维护一堆没人关心的状态如果把不该丢的操作做成通知又可能因为连接问题导致指令丢了也不知道。我的原则是指令类方法一律走请求-响应状态类事件一律走通知。项目日后再出现新方法时先问一句“调用方需不需要确认结果、显示失败”再决定往哪一类里放。5.2 后续还能往哪些方向扩展json_rpc_2 鸿蒙化适配完成之后其实有很多可以继续深挖的方向。一个是支持更多的传输通道。目前项目用 TCP Socket将来如果需要进一步贴近鸿蒙分布式软总线可以把StreamChannel的底层实现换成基于软总线能力的自定义通道让 Flutter 侧完全无感地接入分布式设备网络。一个方向是增加加密层。JSON-RPC 消息目前是明文传输如果设备跨公网或半可信网络建议在通道层先做 TLS 或简单加密让协议层只关心业务逻辑。另一个方向是把这套 RPC 能力开放给更多业务模块。当前只有部分系统能力注册进来了后续相机、音频、多屏协同等复杂能力都可以按同样的方法表、参数表、错误码字典扩展几乎不需要再动通道层和协议层。如果将来项目要移植到更多平台比如 Windows、macOS 的桌面端这套基于 json_rpc_2 的通讯层也可以直接复用因为StreamChannel的抽象足够通用两头只换底层 Channel上层协议代码一行不改。这个收益在最初做设计时可能看不明显但一旦触及多平台支持价值就会体现出来。最后再分享一个小技巧做鸿蒙化适配时务必先在模拟器或真机上把“断开连接、切换网络、锁屏恢复”几个场景完整测一遍。这些场景最容易暴露通道层的脆弱点也是最容易在常规开发中被漏掉的部分。我在实际测试中发现锁屏恢复后 Flutter 侧的 Socket 底层的顺牌状态可能变得很奇怪但上层代码却完全不知道。后来靠心跳检测加自动重连解决了这个问题也算这段适配工作给我留下的一个深刻教训。
RELATED

相关推荐

运筹优化算法岗笔试全解析:从建模到工程实战

运筹优化算法岗笔试全解析:从建模到工程实战

2017年阿里内推的算法工程师(运筹优化)笔试题,放到今天来看依然很能说明问题。那几年正是互联网公司开始认真对待运筹优化方向的时候,阿里在电商、物流、调度、定价这些场景里积累了大量的业务需求,急需能把数学建模和…

📅 2026/10/6 3:49:48
ApolloAuto自动驾驶平台入门:从环境搭建到仿真Demo跑通全攻略

ApolloAuto自动驾驶平台入门:从环境搭建到仿真Demo跑通全攻略

百度ApolloAuto这名字,在自动驾驶圈子里算是绕不开的存在。不管你是准备参加全国大学生智能汽车竞赛的百度智慧交通创意组,还是单纯想研究一套开源无人车系统到底怎么跑起来,ApolloAuto都是目前能接触到的最完整的开源自动驾驶平台之一。这篇…

📅 2026/10/6 3:49:48
Android自定义LayoutManager实现卡片堆叠滑动效果

Android自定义LayoutManager实现卡片堆叠滑动效果

上个月接了个需求,产品经理从工位上探出头,给我发了张gif:一叠卡片像扑克牌一样摞在一起,第一张完整露出,后面的卡片只显示一截头部,整叠卡片可以上下滑动翻看,滑动过程里卡片还有轻微的缩放变化…

📅 2026/10/6 3:49:48
MORE NEWS

更多资讯

📰

微信小程序护肤购物系统实践:数据建模与2MB主包优化

1. 项目概述与设计思路1.1 这个选题解决了什么问题先聊点实在的。做毕业设计或者个人项目选型,最难的不是实现本身,而是“这个题目最后能不能作为一个完整的故事讲出来”。护肤购物系统这个题目,名字里三个关键词缺一不可:微信小程…

📰

嵌入式Linux入门:从裸机到命令行,开发者必须掌握的实用命令与调试技巧

从单片机裸机开发转向嵌入式Linux,第一道坎往往不是C语言,也不是中断、寄存器这些老熟人,而是那个黑乎乎的终端界面。串口工具连上开发板,光标停在#符号前面,你突然发现自己连“看看目录里有什么”都做不到&#xff0c…

📰

莫以skill小而不为:AI Agent技能虽小却有大能量

大概两年前,我第一次在AI工具里看到"skill"这个词的时候,心里想的是:这不就是一段提示词打包成文件吗,能有什么技术含量。直到后来一个几十KB的小skill,让我在项目里少写了两百行逻辑,我才意识到…

📰

多智能体协作触达监控框架Agent-Reach:设计、指标与踩坑实践

最近我把自己搭的一个多智能体协作框架翻出来做了一次大的重构,顺手把所有"触达"相关的问题收敛成了一个独立模块,项目代号暂时就叫Agent-Reach。可能有人一听这个名字会以为是个网络探测或者渠道触达的工具,但其实不是&#xff0c…

📰

AI编程超级能力:本地化开发工具链的范式迁移

1. “Superpowers”不是功能,是开发者工具链的范式迁移最近在几个技术社区和内部分享里,反复听到一个词——“superpowers”。它既不是某个新发布的开源库,也不是某家大厂刚推出的SaaS服务,更不是什么玄学概念。它本质上是一类以A…

📰

基于Hadoop的智能图书推荐系统:从用户行为日志到协同过滤的完整实践

简介:基于Hadoop框架与用户行为特征感知的智能图书推荐系统设计的学士学位毕业论文,原为西南财经大学毕业论文,主要面向计算机科学与技术、软件工程等专业的本科、专科毕业生,也适合对大数据处理与个性化推荐感兴趣的学习者。论文…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬