Android 14自定义系统服务注册全流程:从AIDL到SELinux
我前阵子在搞一个 Android 14 的系统定制需求碰到一个挺典型的场景设备厂商想让第三方 App 通过标准的Context.getSystemService()拿到一个自定义的系统能力而不是靠广播、ContentProvider 或者直接开 Socket。说白了就是把我们自己的服务“挂”进系统里让应用层调起来跟调PowerManager、AlarmManager一样自然。这个活计在 AOSP 定制圈里几乎是必修课但网上资料大多只讲半截要么只给你贴个addService要么只给你个 AIDL 文件真正把SystemServer、SystemServiceRegistry、SELinux 权限、编译验证这一整套链路讲完整的文章很少。我当时踩了不少坑趁着项目刚收尾把整个注册链路和实操过程捋了一遍希望对正在做 Android Framework 定制的朋友有帮助。1. 先搞清楚一个“系统服务”到底藏在哪1.1 应用眼中的服务Context.getSystemService 的调用链很多做应用开发的同学会把context.getSystemService()当成黑盒但做 Framework 定制你必须把这个调用链拆开看。应用进程拿到一个Context实际上是ContextImpl调用getSystemService(String name)后真正干活的是SystemServiceRegistry.getSystemService(name)。这个类维护了一个静态的HashMapString, ServiceFetcher?应用层每一次系统服务请求本质上都是在查这张表。表的 key 是服务名比如power、alarm、windowvalue 是一个 fetcher负责在需要时创建或返回对应的服务对象。所以“注册一个自定义服务给应用用”说白了就两件事一个是在SystemServer进程里把真正的 Binder 服务发布出去让所有进程都能通过ServiceManager找到它另一个是在应用进程的SystemServiceRegistry表里注册一个 fetcher让Context.getSystemService(my_service)能找到回去的路。这两件事缺一不可。只做第一步应用层得自己去反射调ServiceManager.getService()体验很割裂只做第二步服务本体不在ServiceManager里fetcher 拿不到 binder注册了个寂寞。1.2 Framework 侧的三层结构把视角拉到 AOSP 源码一个能让应用通过getSystemService调用的自定义服务至少要拆成三个部分服务实现运行在system_server进程里的具体类通常继承IMyService.Stub里面写真正的业务逻辑比如查设备状态、改系统配置、下发指令。AIDL 接口跨进程通信的“合同”定义了客户端能调哪些方法、传什么参数、返回什么类型。注册与门面一部分在SystemServer启动流程里把服务发布出去另一部分在SystemServiceRegistry里注册 fetcher让应用能通过Context拿到接口代理。我习惯用一个类比服务实现是后厨AIDL 接口是菜单SystemServer是餐馆老板Context.getSystemService是前台档口。应用作为食客只跟档口打交道档口按菜单下单后厨出菜全链路对应用透明。2. 开工前的准备源码环境与方案选型2.1 搭建可编译的 Android 14 源码环境做这种项目你手上必须有一套能编译的 Android 14API 34源码。很多人直接去百度网盘拖别人编译好的产物我建议不要这么干——你后面改SystemServer.java、改SystemServiceRegistry.java、加 SELinux 规则每一步都要重新编译、重新刷机手里没源码根本没法迭代。搭建环境这块老生常谈的几点我就不啰嗦了重点提醒三个容易翻车的分支要选对AOSP 分支名类似android-14.0.0_r15不同 patch 版本之间差异不大但尽量选你设备对应或接近的版本别随便拉 master。磁盘至少 400G 起步Android 14 全量源码加 out 目录轻松吃掉 300G我见过有人用 256G 盘编译到一半爆掉真不是开玩笑。内存 16G 是底线32G 才踏实编译 Framework 时 javac 和 Soong 都非常吃内存内存不够经常报Killed或者生成半截的.ninja文件后面会专门讲这个坑。源码同步完按常规流程来source build/envsetup.sh lunch aosp_arm64-userdebug这里用userdebug版本很重要原因是你后面要用adb root调试、要能查看dumpsys输出user版本默认不开这些东西。2.2 三种实现“系统服务”的姿势我见过不少同行在方案选型上栽跟头搞混了三种“系统服务”的概念。我先把三者掰开说你再决定用哪种。第一种LocalService进程内服务LocalService是今年来 AOSP 里推荐用的轻量级方案它通过LocalServices.addService()注册但只能在system_server进程内部使用应用进程拿不到。适合的场景是系统模块之间的解耦比如PowerManagerService需要调用音量服务的内部方法但不希望走 Binder。第二种Binder Service传统意义上的“注册到系统”这是标题里说的“注册到系统”的标准姿势实现一个继承IMyService.Stub的类在SystemServer启动时通过ServiceManager.addService()或封装好的publishBinderService()把它挂到servicemanager所有进程都能拿到 Binder 代理。这是跨进程的服务也是本项目的核心方案。第三种Context 层“伪服务”有些服务不一定要真的跑在system_server比如某个纯计算服务可以在应用进程本地实现然后在SystemServiceRegistry里注册一个 fetcher每次getSystemService都返回新对象。这种适合无状态、不需要跨进程共享的服务。我们这次要做的是一个真正“注册到系统”的 Binder 服务既要在SystemServer里发布又要让应用通过标准 API 获取。所以方案定得很明确AIDL 接口 SystemService子类 SystemServiceRegistry注册。2.3 代码放哪AOSP 源码目录分配聊代码前先明确文件位置这比写代码还重要放错目录编译系统根本不认。AIDL 接口frameworks/base/core/java/com/android/internal/os/IMyService.aidl。放在 core 里意味着它会被打进framework.jar应用侧编译时能引用。服务实现frameworks/base/services/core/java/com/android/server/os/MyService.java。services 目录是system_server的专属地盘。SystemServer 启动改动frameworks/base/services/java/com/android/server/SystemServer.java。Context 常量与注册表改动frameworks/base/core/java/android/content/Context.java和frameworks/base/core/java/android/app/SystemServiceRegistry.java。这套目录遵循 AOSP 对“API 层”和“服务层”的边界划分接口跑到 core实现沉到 services这样底层服务的细节不会污染 API 层。3. 核心细节解析把 AIDL 接口和实现类写好3.1 定义 AIDL 接口的讲究AIDL 文件是整个链路的第一关写不好后面全是坑。我们这个需求是提供getCurrentLevel()和setCustomMode(int mode)两个方法再加一个queryConfig(String key)返回字符串。文件内容如下// frameworks/base/core/java/com/android/internal/os/IMyService.aidl package com.android.internal.os; interface IMyService { int getCurrentLevel(); void setCustomMode(int mode); String queryConfig(String key); }有几个细节经验值得单独说包名要跟目录一致。如果你把 aidl 放在com/android/internal/os下包名就必须是com.android.internal.os。AIDL 的包名直接决定生成的 Stub 类全限定名不一致会导致编译期报找不到类。自定义类型要显式import。比如返回一个Parcelable对象必须在 aidl 文件顶部加import com.android.internal.os.MyResult;否则 aapt 会直接报错。事务方向默认是in。大对象用out或inout时要小心inout会产生额外序列化开销不是必需就别用。接口方法数量不要贪多。Binder 事务有 1MB 左右的大小限制单个方法传超大 list 或 bitmap 我会直接劝退要么拆分要么走文件通道。3.2 写实现类的注意点实现类一般长这样// frameworks/base/services/core/java/com/android/server/os/MyService.java package com.android.server.os; import android.content.Context; import android.os.RemoteException; import com.android.internal.os.IMyService; import android.content.pm.PackageManager; public class MyService extends IMyService.Stub { private static final String TAG MyService; private final Context mContext; private final Object mLock new Object(); private int mMode 0; public MyService(Context context) { mContext context; } Override public int getCurrentLevel() { mContext.enforceCallingPermission( android.Manifest.permission.MY_CUSTOM_PERMISSION, getCurrentLevel requires MY_CUSTOM_PERMISSION); synchronized (mLock) { return mMode; } } Override public void setCustomMode(int mode) { mContext.enforceCallingPermission( android.Manifest.permission.MY_CUSTOM_PERMISSION, setCustomMode requires MY_CUSTOM_PERMISSION); synchronized (mLock) { mMode mode; } } Override public String queryConfig(String key) { return config_ key _value; } }这里有几处不是随便写的权限检查是必做项。enforceCallingPermission()会校验调用进程的 UID/PID 是否持有指定权限拿不到就抛SecurityException。你不能因为它是系统服务就裸奔很多厂商服务的泄露事故都是这么出的。权限本身要提前在frameworks/base/core/res/AndroidManifest.xml里声明protectionLevel 用signature或者signature|privileged保证只有系统签名的应用能调用。线程模型要心里有数。Binder 方法默认跑在 Binder 线程池里多个客户端并发调用时你的内部状态必须加锁。上面代码里的mLock就是干这个的忘了加锁线上并发一上来数据错乱是迟早的事。不要在 Binder 方法里做耗时操作。Binder 调用是同步的客户端线程会一直阻塞等待返回。你要是把 I/O、网络请求直接写在queryConfig()里应用侧两三秒卡死是常有的事。我一般是耗时任务丢到独立线程接口只负责触发和回调。3.3 通过 SystemService 把服务“发出去”直接ServiceManager.addService()能注册成功但不是推荐做法。Android 从很早开始就推荐自定义服务继承SystemService// frameworks/base/services/core/java/com/android/server/os/MyService.java public class MyService extends SystemService { private MyServiceImpl mImpl; public MyService(Context context) { super(context); mImpl new MyServiceImpl(context); } Override public void onStart() { publishBinderService(Context.MY_SERVICE, mImpl); } Override public void onBootPhase(int phase) { // 在 PHASE_BOOT_COMPLETED 之后做初始化比较稳妥 } }注意把前面的MyServiceImpl改一下名字然后MyService变成壳负责声明周期。我这里为了叙事方便直接用MyService做实现类也可以但正式项目我会拆成两层MyService管生命周期MyServiceImpl管业务。好处有这么几个publishBinderService()内部就是ServiceManager.addService()少写一行是一行SystemServiceManager会统一管理服务的启动、崩溃处理、Boot 阶段回调以后想要在开机流程某个阶段做初始化直接覆写onBootPhase()就行不用自己监听广播。4. 实操过程与核心环节实现从源码到编译验证4.1 在 SystemServer 中拉起这个服务打开frameworks/base/services/java/com/android/server/SystemServer.java在startOtherServices()方法里找个合适的位置加上这样一段private MyService mMyService; private void startOtherServices() { // ... 前面有一大堆已有服务启动 try { Slog.i(TAG, Starting MyService); mMyService new MyService(mSystemContext); mSystemServiceManager.startService(mMyService); } catch (Throwable e) { reportWtf(Starting MyService, e); } // ... 后面该干嘛干嘛 }mSystemServiceManager.startService()会调用MyService的onStart()也就是publishBinderService()那一步到此系统进程这边的服务发布就完成了。插桩位置有个讲究如果你的服务被其他系统服务依赖要放在依赖服务之后如果只是给应用用那么在startOtherServices()中段找个位置就行没必要非要跟某一批服务挤在一起。4.2 让 getSystemService(my_service) 在应用层生效系统进程那边发布的是 Binder 服务但应用进程的Context.getSystemService()还不知道这个服务存在必须做两处改动。第一处在 Context 里加常量。打开frameworks/base/core/java/android/content/Context.java在常量区加入public static final String MY_SERVICE my_service;应用层就可以写context.getSystemService(Context.MY_SERVICE)了。第二处在 SystemServiceRegistry 里注册 fetcher。打开frameworks/base/core/java/android/app/SystemServiceRegistry.java在静态注册区加入registerService(Context.MY_SERVICE, IMyService.class, new CachedServiceFetcherIMyService() { Override public IMyService createService(Context ctx) { IBinder b ServiceManager.getService(Context.MY_SERVICE); return IMyService.Stub.asInterface(b); } });这里用CachedServiceFetcher的原因同一个进程里多次getSystemService我们希望在进程内复用一个 Binder 代理对象而不是每次调用都新建减少 binder 代理对象的创建开销。到这里应用的调用链就通了IMyService service (IMyService) context.getSystemService(Context.MY_SERVICE); int level service.getCurrentLevel();这里有个经验点getSystemService返回类型是 Object如果你不想让应用侧强制强转可以再写一个MyServiceManager封装类内部持有IMyService对外暴露业务方法。这一步看你们项目风格车机和电视厂商一般会比较喜欢 Manager 模式因为对应用开发者更友好。4.3 权限和 SELinux这步别省很多第一次搞系统服务的人烧完自己加的服务发现应用侧要么ServiceNotFound要么莫名其妙SIGSEGV最后查半天发现是 SELinux 把 Binder 调用拦了。Android 的 Binder 通信是过 SELinux 的system_server发布服务、应用进程查询服务、应用进程调用服务每一步都有对应的 te 规则要放行。第一步自定义权限。在frameworks/base/core/res/AndroidManifest.xml里加permission android:nameandroid.permission.MY_CUSTOM_PERMISSION android:protectionLevelsignature /signature级别意味着只有系统签名的应用才可能拿到这个权限第三方普通应用默认申请不到。如果你们的生态是封闭的比如所有 App 都是你们预置的这个级别合适如果有合作应用要走 MDM 之类的高权限通道可以拆成signature|privileged给预置应用放行。第二步SELinux 规则。在system_server.te里加如果你服务实现跑在 system_server 里的话allow system_server my_service_service:service_manager add; allow system_server my_service_service:binder transfer;在appdomain.te里加allow appdomain my_service_service:service_manager find; allow appdomain my_service_service:binder call;同时在service_contexts文件中加映射my_service u:object_r:my_service_service:s0然后到对应.te文件里声明这个类型type my_service_service, service_manager_type;这套规则不是可选项。我在调试初期偷懒只加了service_manager find结果应用进程一调用 Binder 方法就立刻被 SIGSEGV 杀掉logcat 里只有一行avc: denied { call }非常隐蔽。4.4 编译验证步骤改动完成不要一部到位去刷整机先编译验证source build/envsetup.sh lunch aosp_arm64-userdebug m framework-services # 验证 services 模块 m framework # 验证 framework.jar确认编译通过后做整包编译m -j32刷机后第一件事验证服务有没有注册上去adb root adb shell service list | grep my_service能看到my_service: [com.android.internal.os.IMyService]这行说明 system_server 那边已经就绪。然后写一个测试 App注册MY_CUSTOM_PERMISSION调用IMyService service (IMyService) getSystemService(Context.MY_SERVICE); Log.d(test, level service.getCurrentLevel());如果SystemServiceRegistry那步没写对这里返回的是 null如果 SELinux 没放行这里就是进程被杀。跑通了这行日志你的整个链路就通了。5. 常见问题与排查技巧实录5.1 编译报错处理failed: out/soong/build.ninja 等我之前在推进这个项目时最痛苦的不是写代码而是编译反复报错。有一个特别典型的failed: out/soong/build.ninja看起来像号“No such file or directory”实际原因往往不外乎这几个内存不够导致 ninja 文件写了一半。系统编译时 OOMSoong 生成的中间文件不完整。解决办法是清掉out/soong目录里对应的缓存文件或者干脆rm -rf out/soong重新生成。磁盘满了。df -h看一眼低于 20G 就赶紧清理不然报错会非常随机。并发开太高。m -j32在 16G 内存机器上基本必挂老老实实m -j8慢一点但稳。还有一个容易踩的改 AIDL 文件之后编译只编 framework-services但framework.jar没编导致系统 image 里接口还是旧的。我建议凡是动了 AIDL、Context 或SystemServiceRegistry直接m framework一起编。5.2 运行时报错服务不存在、空指针、权限拒绝如果service list | grep my_service查不到服务先看SystemServer有没有把服务起起来。打开 logcat 过滤SystemServer和MyServiceadb logcat -s SystemServer MyService常见原因startOtherServices()里new MyService()抛了异常reportWtf打印了堆栈服务没注册成功publishBinderService()前某个初始化依赖没准备好比如mSystemServiceManager.startService()之前其他服务没起来。如果服务在但应用调 null重点排查SystemServiceRegistry那行代码服务名是不是写岔了Context.MY_SERVICE和ServiceManager.addService或publishBinderService用的 key 必须完全一致fetcher 里ServiceManager.getService()返回的 binder 是否为空如果服务没注册这里就是 nullasInterface(null)返回 null。权限拒绝比较直接SecurityException打到 logcat说明你enforceCallingPermission拦住了调用方。这时先确认调用方有没有申请权限再确认权限是不是signature级别、应用是不是系统签名。5.3 升级接口遇到 Binder 崩溃这个项目做完一版后后续肯定会加接口。AIDL 接口一旦发布出去后面改接口方法签名参数、返回值就很容易踩 Binder 版本兼容的坑——老客户端用旧接口请求新服务端返回的 Parcel 结构对不上直接 TransactionTooLarge 或 ClassCastException。我的经验是接口方法只能加不能删不要改已有方法的参数顺序和类型真要改务必给 AIDL 接口加版本号字段做兼容分支。有一次我只把返回类型从String改成了ListString老客户端全部闪退查 log 发现是ClassCastException那之后我每逢改接口都特别谨慎。5.4 给新手的三条心法这算是我做完几个这类服务后的总结第一先小步验证再加业务逻辑。第一版接口就写一个getVersion()跑通了整条链路再加业务方法不然报错都不知道是链路问题还是业务问题。第二始终保持最小改动。能不动 AOSP 已有代码就不动。Context 常量加一个没问题但不要顺手重构SystemServiceRegistry的注册机制改动面越大后续同步 AOSP 新版本时冲突越多。第三守住 SELinux 底线。遇到权限问题第一反应不要是setenforce 0敷衍过去。开发调试可以临时关闭但交付时一定要补全规则否则系统升级、安全补丁一打你的服务就是第一个崩溃的。最后再分享一个小技巧做这类系统服务我强烈建议你在服务里顺手实现一个dump()方法或者干脆继承SystemService后覆写dump(FileDescriptor, PrintWriter, String[])。这样调试时只需adb shell dumpsys my_service就能直接看到服务内部状态、最近调用记录、当前 mode 值排查问题效率能高一截。我第一次做这个的时候没写 dump某个状态异常找了一下午后来补上 dump问题定位只花了十分钟。Android 14 时代注册一个自定义系统服务其实没有想象中那么玄乎核心就是接口在 core、实现在 services、注册进SystemServer、挂到SystemServiceRegistry、再配好权限和 SELinux。把这五步走通你的系统能力对接第三方应用的姿势就标准了后面想加什么复杂业务都是水到渠成的事。

相关新闻

ANSYS许可合规检查:授权文件、日志台账与并发审计实战

ANSYS许可合规检查:授权文件、日志台账与并发审计实战

1. 许可合规检查真正查的是什么:从"能不能跑起来"到"跑得合不合规"大部分人第一次接触ANSYS 许可合规性检查,都是被动的——要么是采购部门要续费,需要一份"到底有多少人真在用"的说明;要么是外部合…

2026/10/1 18:16:34 阅读更多 →
SpringBoot2+Vue3实战:大创项目管理系统设计与部署

SpringBoot2+Vue3实战:大创项目管理系统设计与部署

前阵子帮一所高校做了一个大学生创新创业训练计划项目管理系统,也就是大家常说的“大创管理系统”。这系统说白了就是用来管理大学生创新创业训练计划全流程的:学生线上申报项目、指导教师审核、学院推荐、学校审批,再到中期检查、结题验收和…

2026/10/1 18:16:34 阅读更多 →
第一性原理:从底层事实出发,拆解问题、重建方案的思维框架

第一性原理:从底层事实出发,拆解问题、重建方案的思维框架

1. 开篇引子:为什么“模仿”永远成不了真正的强者 我见过很多聪明人,学东西极快,看别人做什么成什么,立刻就能照着做一遍,成果还很好看。但几年后你会发现,这些人依然在原地打转:别人换赛道他就…

2026/10/1 18:16:34 阅读更多 →

最新新闻

MCP实战:用AI构建Excel自动化处理服务

MCP实战:用AI构建Excel自动化处理服务

每天跟Excel打交道的朋友应该都有这种体会:处理报表本身不是最费时间的,费时间的是那些重复性的操作——打开表格、定位列、写公式、复制粘贴、再生成新表。尤其是当数据源有变动、格式不统一的时候,整个人都会烦躁起来。 我最近用MCP&#…

2026/10/1 19:00:57 阅读更多 →
SSM+JSP文化遗产管理系统实战:毕业设计稳过方案

SSM+JSP文化遗产管理系统实战:毕业设计稳过方案

简介:本资源是一套基于SSM(SpringSpringMVCMyBatis)框架与JSP技术实现的文化遗产数字化管理系统的完整毕业设计项目,面向计算机、数学、电子信息等专业的本科生,适用于课程设计、期末大作业及毕业论文实践,…

2026/10/1 19:00:57 阅读更多 →
游戏更新后闪退卡死掉帧?三层排查法与系统级优化实战指南

游戏更新后闪退卡死掉帧?三层排查法与系统级优化实战指南

1. 问题定位:先搞清楚是哪种“卡” 9月22号那波更新之后,社区里炸了锅。我自己的机器、帮朋友远程调的几台、还有群里反馈的案例,加起来少说也有二十来台,症状基本能归成三类: 开局加载到一半直接闪退 、 进游戏后画…

2026/10/1 19:00:57 阅读更多 →
AI桌面工作区实战:文档、表格、智能体与工作流的一体化架构

AI桌面工作区实战:文档、表格、智能体与工作流的一体化架构

1. 为什么要把文档、表格、智能体和流程塞进同一个桌面窗口我最早接触“AI 桌面工作区”这个概念,是因为自己每天的工作流实在太碎了。写方案要开文档工具,整理数据要开表格工具,跑自动化要开浏览器或者命令行,调用模型又得切到另…

2026/10/1 19:00:57 阅读更多 →
UEditor下载安装与配置实操:从零到可用完整指南

UEditor下载安装与配置实操:从零到可用完整指南

这段时间因为要维护一个老项目,我把UEditor的下载和安装整个流程重新过了一遍。说实话,这个编辑器虽然年纪不小了,但在很多企业内部系统、CMS后台里依然是主力,网上能找到的教程又零散又过时,真正能把“下载—安装—调…

2026/10/1 19:00:57 阅读更多 →
12G显存硬扛256K上下文:KV缓存卸载到内存的工程实践

12G显存硬扛256K上下文:KV缓存卸载到内存的工程实践

1. 12G 显存硬扛 256K 上下文,这事到底卡在哪先把结论摆在前面:12G 显存想跑 256K 上下文,靠的不是什么黑科技,而是把KV 缓存从显存里"请"出去,挪到内存里。这个思路听起来简单,但真正动手的时候…

2026/10/1 18:59:56 阅读更多 →

日新闻

我发现了一个新思路:用 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/1 0:00:30 阅读更多 →
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/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练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/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 18:13:06 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 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/1 0:00:30 阅读更多 →
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/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练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/1 1:01:17 阅读更多 →