Android Binder机制深度解析:从概念到AIDL实践
先说个结论Binder 是 Android 里最绕不开、也最容易被人顺手跳过的一块基石。平时我们用 Intent 跳页面、通过 ContentProvider 读联系人、用 LocationManager 拿定位背后全是一套 Binder 机制在做跨进程传输。这个系列的第一篇我打算先把概念树立起来不讲太深的内核源码重点回答“为什么需要它”“它比传统方案好在哪”“一次调用到底怎么传的”最后给一套能直接跑起来的最小示例。适合刚开始准备看 AIDL 源码、想搞懂 Framework 层原理、或者被“Binder 机制”面试题反复拷打过的同学。Binder 这个概念说难也难说简单也简单。难是因为它涉及进程、内核驱动、线程模型、对象引用这些底层知识简单是因为它对外提供的编程模型就是一个“像调用本地方法一样调用远程进程”的抽象。只要把几个关键角色的职责理清了后面再去看 AIDL 生成的代码、系统服务的调用链路都会顺畅很多。1. 进程隔离是 Android 绕不开的起点1.1 隔离带来的安全与稳定为什么还要通信Android 沿用了 Linux 的进程隔离模型每个应用默认跑在独立进程中拥有独立地址空间、独立的 UID。应用 A 想直接读应用 B 的内存内核不给权限这是系统层面的安全边界。如果所有应用都住在同一个大房间里一个崩溃就可能把别人带崩数据也谈不上隐私。所以 Android 从一开始就把“进程隔离”当成默认策略。四大组件里的 Service即使是你自己应用里的 Service也可以用android:process:remote单独拉一个进程后台任务、WebView 渲染、插件化方案很多场景都要主动多开进程。但隔离之后问题来了进程之间没法直接调方法、读变量。你总得有一种途径让两个进程交换数据。比如系统需要把你的应用进程里一个 Activity 的生命周期状态同步给 AMSActivityManagerService这里 AMS 跑在 system_server 进程你的应用跑在自己的进程中间必须有一座桥。这座桥就是跨进程通信IPCInter-Process Communication。Binder就是 Android 为这座桥专门定制的传输协议和通道。1.2 跨进程通信必须回答的四个问题任何一套跨进程通信方案本质上都要解决四个问题数据怎么传地址空间隔离后A 进程的数据怎么安全地进入 B 进程并且不能破坏双方内存。怎么同步如果两边同时读写一份资源如何避免数据错乱调用方发完请求后是阻塞等待结果还是异步通知。怎么找到对方进程那么多A 怎么知道该连哪个进程、调用哪个对象、哪个服务。怎么保证身份可信怎么确认对面真是你要连的进程而不是伪装者敏感服务如何做权限校验。不同方案对这四个问题的取舍天差地别。理解了这四个问题再回头看 Binder 的设计会发现它的每一个“奇怪设计”都有明确目的。2. 谈了那么多 IPCAndroid 为什么把票投给 Binder2.1 先看看其他 IPC 选手的优缺点面试聊 IPC 时很多人会把 Linux 传统的管道、消息队列、共享内存、Socket 都背一遍但真正要回答的是“Android 为什么不用它们”。我整理了一个对比表方便你对着看方案数据模型性能安全性易用性主要短板管道 / FIFO字节流中中低单向、无对象语义不适合复杂接口System V 消息队列消息包中中低数据边界处理麻烦依赖内核对象生命周期管理复杂共享内存内存映射高低低需要自己实现同步与权限校验开发成本高Socket字节流低中低传输效率低编程模型偏网络不适合高频系统调用Binder面向对象方法调用高高高数据结构受缓冲区限制不宜传超大对象坐标上 Binder 不是每一项都最优秀。纯看吞吐共享内存绝对碾压纯看通用性Socket 什么都能干。但 Android 需要的是一个“高频、低延迟、安全可控、同时能表达服务接口”的方案。系统服务调用有多高频对 Activity 生命周期的一次操作、对定位服务的一次获取、对通知栏的一次更新全都要走 IPC。如果用 Socket一次调用要经过多次用户态和内核态的切换加上数据编解码系统早就卡成幻灯片了。2.2 Binder 的几张底牌第一张底牌是性能。Binder 在一次数据传输中大概只需要一次内存拷贝。常规流程是调用方把数据写进内核空间Binder 驱动再通过 mmap 映射技术把同一块物理内存映射到接收方进程空间接收方不用再复制一遍。对比传统管道或者 Socket 的两次拷贝省下的开销在高频小数据量场景下非常可观。第二张底牌是安全。Binder 驱动在做进程间数据传输时内核会带上调用方的 UID/PID服务端拿到请求后可以直接做身份校验。这点很关键因为内核是可信的传入的 UID/PID 伪造不了。传统 IPC 比如共享内存和管道很难干净地解决“怎么确认对面是谁”的问题。第三张底牌是编程模型。Binder 把整个交互抽象成类似 Java 接口调用的形式客户端拿到一个代理对象调它的方法方法参数和返回值会自动序列化、传输、反序列化。开发者不需要处理 Socket 的字节流边界不需要管道的一读一写心理负担小很多。也正是因为 Binder 面向“接口”而不是面向“字节流”Android 才能把 AMS、WMS、PMS 这些庞大系统服务组织得像本地对象一样使用。2.3 硬币的另一面Binder 不适合什么Binder 不是万能的它最大的软肋是传输缓冲区有限。经典限制是单个 Binder 调用可用的缓冲区大约在 1MB 以内而且这个缓冲区还是服务端进程共享给多个并发事务用的不是一次调用独享 1MB。传大文件、传大 Bitmap甚至 Bundle 塞太多数据都会抛TransactionTooLargeException。我早期项目里就踩过这个坑当时图省事把一张压缩后的 Bitmap 直接放进 Intent 通过 Binder 传结果在性能差一点的机型上频繁崩溃。后来改成先写文件共享或者用 ContentProvider 存数据、跨进程只传 Uri问题就消失了。所以经验是Binder 适合传控制指令和小尺寸业务数据不适合做“搬运工”。真要大块数据搬运正确姿势是共享内存、文件、或者只传引用。3. 新手必看Binder 的四个角色与两级代理3.1 四个角色谁负责干什么理解 Binder 架构先记住四个角色Client发起调用的进程也就是使用服务的消费者。Server提供服务的进程比如系统服务中的 system_server。ServiceManager服务注册中心相当于 DNS 或电话簿负责把服务名字解析成 Binder 引用。Binder 驱动内核模块负责实际的数据传输、线程管理、引用计数、身份校验。用生活里的例子套一下你要给某个公司打电话先得查电话簿ServiceManager找到号码后用电话线拨号Binder 驱动对面接电话的是客服Server你这边拿着话筒等着听回复的就是 Client。电话线负责把声音传过去但它不关心你说什么内容只管传输。3.2 ServiceManager系统级电话簿Server 启动后第一步是向 ServiceManager 注册通常注册一个字符串名字比如activity、package这些大家眼熟的关键字对应的其实是 ActivityManagerService、PackageManagerService 的 Binder 对象。Client 要使用服务时先向 ServiceManager 查询这个名字拿到一个 Binder 代理对象再通过代理去和服务端通信。这样设计的好处很明显Client 不需要预先知道服务端进程地址、端口或者句柄只要知道一个全局唯一的名字服务端的重启、迁移、位置变化对 Client 完全透明。Android 8.0 之后为了让系统和设备厂商实现解耦还做了变体Framework 层走/dev/binderVendor 层走/dev/hwbinder后来又引入/dev/vndbinder把高低通信用途分开。你现在先不用纠结细节只需要知道 Binder 不是一个“单根线”而是按系统分层拆成了多套通道。3.3 Proxy 与 Stub你调的不是对象是替身一个最容易让人懵的点是Client 进程里拿到的服务对象本质上是假的。服务端进程里有一个真实 Binder 对象负责干活叫 Stub客户端进程里拿到的叫 Proxy。Proxy 和 Stub 之间没有直接内存关联Proxy 只是把“我要调哪个方法、参数是什么”打包通过驱动传到对端Stub 收到后解包、调用真实方法再把返回值打包传回来。对应到代码里AIDL 编译后会自动生成两个关键类名xxx.Stub服务端的抽象基类你继承并实现里面的真实业务逻辑。xxx.Stub.Proxy客户端的代理实现藏在getService或者bindService的返回值里。这两个概念一定要分清。很多人在项目里见过asInterface但不知道它为什么存在。它干的事很简单如果调用方和服务端在同一个进程直接用 Stub 本尊如果跨进程了就包一层 Proxy。这也是 Binder 面向对象设计最典型的体现同一个接口同进程是直接调用跨进程自动走代理。3.4 Binder 对象的生命周期和死亡通知跨进程场景里双方进程是独立的一个进程挂了另一个进程可能还活着。如果服务端进程被系统杀掉客户端还握着 Proxy继续调用就会抛DeadObjectException。为了解决这个问题Binder 提供了死亡通知机制客户端可以在 Proxy 上注册linkToDeath回调一旦服务端进程死亡会收到binderDied()通知然后做恢复逻辑比如重新绑定、提示用户重试。对象引用计数也是控制器自己处理的Binder 驱动会维护每个 Binder 实体的强弱引用关系。这背后细节比较多第一遍学不用背先记住“Binder 是有生命周期的跨进程引用需要主动监控”就够了。4. 一次 Binder 调用数据到底怎么传4.1 从注册服务到拿到方法返回值的完整过程把四个角色串起来看一次典型的 Binder 调用会发生什么。假设服务端已经注册进 ServiceManager客户端要调它的getMessage()方法Client 调用ServiceManager.getService(my_service)拿到一个 Proxy。Client 调用 Proxy 的getMessage()内部触发transact()把方法标识符code和参数打包成 Parcel。Binder 驱动接管把这段数据拷贝到内核空间找到目标服务端进程的内存映射区唤醒服务端 Binder 线程。服务端线程池里某个线程收到请求调用 Stub 的onTransact()解包根据code找到对应方法。Stub 调用真实实现类的getMessage()得到结果字符串。返回值打成 Parcel沿着同样的路径回到 ClientClient 的transact()返回Proxy 把结果解包后交还调用方。整个过程看起来像本地方法调用但实际跨了两次进程边界。因为 Binder 传输是同步的这个链路里 Client 发起调用的线程在结果返回前会阻塞住所以 Binder 调用天然是“带有 RPC 味道的同步调用”。4.2 Parcel 包怎么打包同步调用为什么会有感觉Parcel 是 Binder 传输的数据容器类似一个可序列化缓冲流。AIDL 生成的 Proxy 代码里你会频繁看到这类操作android.os.Parcel _data android.os.Parcel.obtain(); _data.writeString(msg); boolean _status mRemote.transact(TRANSACTION_getMessage, _data, _reply, 0); _reply.readException();写参数、发起事务、读返回值包装和解析都在 Parcel 上完成。系统四大组件传 Bundle 时底层也是把 Bundle 里的数据写入 Parcel再通过 Binder 送去对端。因为 Binder 调用默认是同步的你在主线程去调一个耗时服务比如跨进程查数据库时可能引发卡顿甚至 ANR。客户端 UI 线程在等待期间不能处理触摸事件。所以见到的“Binder 调用要在子线程执行”并不是规范强迫症而是同步阻塞模型的必然要求。反过来如果一个调用只是想通知对端“你该刷新了”不需要等它处理完可以给 AIDL 方法加oneway。加了之后调用方发完请求立即返回不再阻塞等待对端结果。这适合回调、事件通知、日志上报这类不需要返回值的场景。4.3 AIDL 在通信流程里扮演的角色AIDL 全称 Android Interface Definition Language也就是 Android 接口定义语言。它最大的价值是把上面这套繁琐的 Parcel 打包、解包、事务分发代码自动生成出来。比如你定义一个接口// IRemoteService.aidl package com.example.ipc; interface IRemoteService { String getMessage(); }构建时 Android Gradle 插件会生成IRemoteService.java里面默认带Stub类服务端继承它并实现真实逻辑。Stub.Proxy类客户端内部使用负责打包和 transact。asInterface(IBinder)根据是否同进程决定返回 Stub 还是 Proxy。你不再需要手写 Parcel也不需要关心 transact 的协议编号只要在服务端实现接口方法、在客户端调用接口方法就行。这也是为什么很多实际项目里大多数人写 AIDL 时全程没有碰过 Binder 底层代码却一样能完成跨进程服务调用。但如果你止步于“会用 AIDL”遇到复杂项目还是会翻车。比如 AIDL 传对象要 implements Parcelable传大文件会爆缓冲区服务端崩了客户端怎么自动重连这些都需要理解 Binder 的机制后才能游刃有余。5. 初学 Binder 的高频坑与学习路线5.1 常踩的坑与排查速查表我整理了一份高频问题速查表都是实际开发里经常遇到的现象常见原因处理建议TransactionTooLargeException单次 Binder 事务数据量超过缓冲区限制常见于传大 Bitmap、长列表传文件/共享内存/ContentProvider 的 Uri不要直接塞大对象DeadObjectException服务端进程已退出或被杀客户端继续调用实现 linkToDeath 监听收到死亡通知后重连SecurityException: Permission Denial服务端对调用方 UID/PID 做了权限校验客户端无权限核对 Manifest 权限声明或者服务端自定义校验逻辑bindService 返回 false 或 onServiceConnected 一直不来服务没有注册到 Manifest或exported、process配置不对检查 Manifest 里的 service 配置与 action 匹配主线程调 Binder 导致 UI 卡顿或 ANRBinder 调用同步阻塞主线程等待服务端返回挪到子线程或者把回调接口设计成 oneway服务端并发高时客户端偶发无响应Binder 线程池耗尽服务端处理请求排队精简耗时逻辑必要时加managedQuery式的分层缓存每个问题背后都有对应的机制解释这也是我更喜欢“先懂机制再解决问题”的原因。不懂底层时遇到异常只能百度复制粘贴懂了底层后基本看一眼堆栈就能猜到问题出在哪个角色。5.2 从日志和进程状态定位问题Binder 出问题时系统会留下不少线索。最常见的几类日志特征getService NAME returned null说明 ServiceManager 里没有找到对应服务通常是服务端进程还没注册成功或者注册名写错。binder thread pool (N threads) starved说明服务端 Binder 线程都在忙请求排队时间过长性能瓶颈在服务端。Failure to find service查询服务名失败常见于系统服务没有起来或者访问的是 vendor 区的 hwbinder 服务。定位时可以先看adb shell dumpsys activity services列出当前注册的 Service再通过adb shell ps -A | grep 包名确认进程是否存活。对系统服务adb shell service list可以快速查看 ServiceManager 里注册了哪些服务。这套排查流程是纯经验积累第一次看可能会觉得字段太多坚持用几次就会有“医生看化验单”的感觉。5.3 最小可运行的 AIDL 示例与配置要点第一遍学 Binder不建议直接啃 AOSP 源码建议先在 Android Studio 里跑通最小示例。这里给一个精简版骨架。接口文件IRemoteService.aidl// IRemoteService.aidl package com.example.ipc; interface IRemoteService { String getMessage(); }服务端实现放在一个 Service 里public class RemoteService extends Service { private final IRemoteService.Stub mBinder new IRemoteService.Stub() { Override public String getMessage() { return Hello from Binder; } }; Override public IBinder onBind(Intent intent) { return mBinder; } }AndroidManifest.xml里记得注册这个 Serviceservice android:name.RemoteService android:enabledtrue android:exportedtrue /客户端绑定并调用private IRemoteService mService; private final ServiceConnection mConnection new ServiceConnection() { Override public void onServiceConnected(ComponentName name, IBinder binder) { mService IRemoteService.Stub.asInterface(binder); try { String msg mService.getMessage(); Log.i(BinderDemo, server: msg); } catch (RemoteException e) { e.printStackTrace(); } } Override public void onServiceDisconnected(ComponentName name) { mService null; } };注意IRemoteService.Stub.asInterface(binder)这一行是关键它就是判断“同进程直接强转跨进程包 Proxy”的那个入口。跑通这个例子之后你再试着把getMessage()改成带自定义数据的复杂方法传一个 Parcelable 对象慢慢就会对 Binder 的全貌有感觉。行文到这里Binder 概念引入的主体内容就结束了。我个人在实际操作里有一个习惯每学一个新知识点就用“能不能把它画成四个角色和一条消息流”来检验理解。Binder 尤其如此只要能在纸上把 Client、Server、ServiceManager、Binder 驱动这条链路画清楚Proxy 和 Stub 的区别能随口说上来再回头看 AIDL 生成代码、系统服务注册流程、跨进程崩溃日志都会觉得亲切很多这也是学习 Android Framework 最值得投入的一段时间。

相关新闻

ComfyUI 本地部署 不求人 教程

ComfyUI 本地部署 不求人 教程

想玩图像生成,视频生成,基本都绕不过ComfyUI ,因为这些应用都需要一套可视化的工作流。 但是不同的电脑,硬件配置不同,所需要的模型和版本也不相同,导致在本地部署的时候 需要定制,虽然 一些 第…

2026/10/5 9:05:47 阅读更多 →
计网八股笔记

计网八股笔记

一、计网相关的1.cookie 和 session 的区别cookie 和 session 的作用是做会话状态管理,cookie 是保存在客户端的,session 保存在服务端,一次过程是这样的,用户输入账号和密码通过 http 主体去发送,服务端验证之后把用户…

2026/10/5 9:05:47 阅读更多 →
RAG 应用链路:查询检索、提示词增强、生成与 Dify 实践

RAG 应用链路:查询检索、提示词增强、生成与 Dify 实践

知识库建好后,一次请求发生了什么 离线阶段已经完成文档解析、分块、向量化和入库。到了在线阶段,系统要围绕用户问题完成查询理解、检索、过滤与重排、上下文组装、模型生成和引用返回。 这条链路中,检索决定模型能看到什么,提示…

2026/10/5 9:05:47 阅读更多 →

最新新闻

安卓逆向学习路线:从应用层分析到Native层对抗

安卓逆向学习路线:从应用层分析到Native层对抗

这几年时不时就有人跑来问我:安卓逆向怎么学?是不是得会汇编?要不要先学破解?也有人直接在搜索框里敲“android 逆向学习路线”“安卓逆向教程”,然后被一堆零散的资料劝退。作为常年在这行折腾的人,我太清…

2026/10/5 9:45:38 阅读更多 →
STC8H硬件IIC驱动OLED屏:主从关系、初始化与实战排错指南

STC8H硬件IIC驱动OLED屏:主从关系、初始化与实战排错指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 9:45:38 阅读更多 →
如何读懂芯片时序图并写出可靠驱动代码?从时序图到嵌入式驱动开发实战

如何读懂芯片时序图并写出可靠驱动代码?从时序图到嵌入式驱动开发实战

1. 为什么时序图是驱动开发的“翻译蓝本”干了这么多年嵌入式驱动,我见过太多人拿到芯片手册直接翻寄存器表,抄一段网上的例程就跑,跑不通就抓瞎。说句实在话,芯片手册里最值得反复琢磨的既不是引脚定义,也不是寄存器位…

2026/10/5 9:45:38 阅读更多 →
从Q-Learning到DQN:深度强化学习实战解析与代码实现

从Q-Learning到DQN:深度强化学习实战解析与代码实现

1. 为什么深度学习能在决策问题上发力:从Q-Learning到DQN的认知跃迁先纠正一个常见的误区:DQN不是"用神经网络替换Q表"这么简单。如果你只把它理解成查表方式的升级版,后面遇到的收敛困难、训练震荡、奖励炸掉这些问题,…

2026/10/5 9:45:38 阅读更多 →
AgentKit模型网关实战:统一多模型接入、路由与治理

AgentKit模型网关实战:统一多模型接入、路由与治理

我最早接触模型网关这个概念,不是因为赶时髦,而是被真实的混乱逼的。当时手头一个项目要同时接三家模型服务——对话用一家,轻量任务用另一家,偶尔还要切到第三家做对比评测。结果就是代码里堆满了分支判断,每个模型一…

2026/10/5 9:45:38 阅读更多 →
OpenRig 开放式机架主机,从选材到组装的完整 DIY 指南

OpenRig 开放式机架主机,从选材到组装的完整 DIY 指南

组装过几台 OpenRig 之后,我发现这个项目比想象中成熟得多。OpenRig 不是什么新概念,它是一套开源的开放式机架主机方案,简单说就是把传统机箱的侧板、前面板和顶盖全部去掉,用铝型材搭出一个开放测试平台,让主板、显卡…

2026/10/5 9:44:38 阅读更多 →

日新闻

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:22 阅读更多 →
AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

1. 从“plugins”这个词说起:它到底在解决什么问题如果你最近在折腾 AI 编程工具,尤其是 Cursor、Codex CLI、Claude Code 这类带 CLI 的编辑器或命令行助手,那你大概率绕不开一个词——plugins。这个词本身不新鲜,从浏览器到 IDE…

2026/10/5 0:00:23 阅读更多 →
第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:23 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:06:42 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:10:22 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 3:06:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →