鸿蒙录音机开发实战:基于ArkTS与AudioCapturer实现
做鸿蒙应用开发有一段时间了最近把一个经典的“录音机”Demo从零到一完整跑通了从权限申请、AudioCapturer采集、WAV文件头封装、文件列表扫描到AVPlayer回放一整条链路都亲手摸了一遍。录音机这个项目看似简单其实是鸿蒙原生开发里非常典型的一个“全家桶”练习它要碰动态权限、音频流、文件I/O、UI状态联动、生命周期管理每一个环节单独拆出来都够写一篇笔记。这篇就围绕“ArkTS Stage模型 AudioCapturer 实现鸿蒙录音机”展开把我踩过的坑、调通的代码、设计时的取舍全部摊开讲给正在学鸿蒙开发或者准备拿录音机练手的朋友做一个可直接参考的实操记录。1. 整体设计思路为什么用 WAV为什么音频逻辑单独封装1.1 技术选型的出发点录音机App的第一件事不是写代码而是想清楚“录出来的文件是什么格式”。当前主流的录音格式有AAC、AMR、MP3、WAV等鸿蒙系统本身也支持AAC和AMR编码但我的结论是第一个版本直接用 WAV 裸流不要一上来就上编码器。原因很简单WAV的本质是“PCM裸音频数据 一个44字节的文件头”不涉及复杂的编码算法采集到的原始数据是什么字节写入文件的基本就是什么字节调试链路时非常直观。文件头有问题导致播放器打不开排查起来也就那一小块逻辑。反观AAC和AMR要先引入编码器配置编码参数、码率控制、ADTS头等一堆概念对新手不友好而且一旦音频数据流写错了你分不清是采集的问题还是编码的问题。另一个出发点是把音频逻辑和UI页面解耦。很多初学者喜欢把录音代码直接写在页面里按钮一按就在onClick里启停这样Demo跑起来确实没问题但等到要加后台录音、录音列表回放、多页面共享录音状态时代码会立刻变成一团乱麻。我的做法是单独建一个RecorderManager类和一个PlayerManager类页面只负责调用start/stop/pause这些对外方法再通过回调或状态变量感知录音状态变化。这样页面代码干净以后扩展也顺手。1.2 数据流全链路整个录音机的数据流可以概括为麦克风拾音 → AudioCapturer按固定采样率吐原始PCM数据 → 应用收到Buffer后按WAV格式封装 → 写入应用沙箱文件 → 首页扫描文件目录展示列表 → 点击列表项用AVPlayer播放。这条链路里最关键的一点是PCM数据本身没有时长、采样率、声道数这些元信息如果不加WAV头播放器根本不知道该怎么解读这堆字节。这也是为什么“先封装再落盘”的顺序不能搞反。有的实现是先录完再补头这也行但必须在停止录音时把文件末尾的数据长度回填到头部否则造成时长信息缺失。1.3 页面与功能模块划分功能上我划分成三大块录音控制区、录音列表区、播放控制区。录音控制区放“开始/暂停/停止”三个按钮根据录音状态切换显隐列表区展示当前沙箱目录下的所有WAV文件按修改时间倒序显示文件名、时长和大小播放控制区在点击列表项时出现支持播放/暂停和停止。这个结构对标的其实就是系统自带的录音机应用麻雀虽小五脏俱全。2. 环境准备与工程骨架搭建2.1 开发环境与工程配置我用的是DevEco Studio创建工程时选择Empty Ability模板。这里的模板对应Stage模型入口是Ability如果你看到的是FA模型Feature Ability老模型的模板建议切到Stage模型因为新版本API和文档都以Stage为主网上能搜到的资料也大多基于Stage。API版本建议9及以上我的工程配置是compileSdkVersion和targetSdkVersion相同。如果你的设备系统版本比较旧targetSdkVersion可以适当调低但权限和audioCapturer相关的API至少要API 9才稳定。工程默认会生成 main/ets 目录EntryAbility.ts 是入口pages/Index.ets 是默认页面。2.2 模块权限声明在Stage模型里权限声明在 module.json5 中而不是在AndroidManifest里。录音需要麦克风权限配置如下{ module: { requestPermissions: [ { name: ohos.permission.MICROPHONE, reason: 用于录制音频, usedScene: { abilities: [EntryAbility] } } ] } }这一项不配置后面动态权限申请弹窗压根不会出现。注意reason字段在应用上架审核时会展示给用户最好写清楚用途。### 2.3 动态权限申请的正确姿势 鸿蒙的权限分系统授权和用户授权两类麦克风属于用户授权光在module.json5声明还不够必须在运行时弹窗申请。申请代码要放在真正点击“开始录音”的时候不要放在Ability的onCreate里这样用户体验更自然。 typescript import { abilityAccessCtrl, common, Permissions } from kit.AbilityKit; let atManager abilityAccessCtrl.createAtManager(); let context getContext(this) as common.UIAbilityContext; let permissions: ArrayPermissions [ohos.permission.MICROPHONE]; atManager.requestPermissionsFromUser(context, permissions).then((result) { if (result.authResults[0] 0) { // 用户已授权开始录音 } else { // 用户拒绝提示需要权限 } });这里有个新手容易犯的错requestPermissionsFromUser的第一个参数一定要传UIAbilityContext也就是通过getContext(this)拿到的那个context。如果传的是应用全局的ApplicationContext部分版本上会静默失败不弹窗也不报错排查起来特别费劲。3. 录音核心逻辑实现3.1 创建AudioCapturer与参数选型AudioCapturer是鸿蒙的音频采集器本质是一个底层指针可以持续从麦克风读取PCM数据。创建时需要传入AudioCapturerOptions里面包括streamInfo音频流参数和capturerInfo采集器类型参数。import audio from ohos.multimedia.audio; let audioStreamInfo: audio.AudioStreamInfo { samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, channels: audio.AudioChannel.CHANNEL_2, sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW }; let audioCapturerInfo: audio.AudioCapturerInfo { sourceType: audio.SourceType.SOURCE_TYPE_MIC, capturerFlags: 0 }; let audioCapturerOptions: audio.AudioCapturerOptions { streamInfo: audioStreamInfo, capturerInfo: audioCapturerInfo }; let audioCapturer await audio.createAudioCapturer(audioCapturerOptions);几个参数背后的逻辑说一下。采样率选48000Hz是目前主流设备的HD音频标准兼容性最好如果你的应用有明确的语音识别场景44100Hz的CD音质采样率也完全够用。声道数我选双声道CHANNEL_2这样录出来的声音会更有空间感实际听感也比单声道饱满一些。sampleFormat用S16LE即16位有符号小端这是WAV格式里最通用的PCM编码格式绝大多数播放器都支持。3.2 WAV文件头封装与数据写入WAV文件头是44字节结构固定但里面的字段需要根据实际录音参数计算后填入。关键在于RIFF块大小和data数据块大小这两个字段必须在录音结束时才能确定因为只有那时才知道一共写了多少字节。function buildWavHeader(dataSize: number, sampleRate: number, channels: number, bitsPerSample: number): ArrayBuffer { let buffer new ArrayBuffer(44); let view new DataView(buffer); const writeString (offset: number, str: string) { for (let i 0; i str.length; i) { view.setUint8(offset i, str.charCodeAt(i)); } }; // RIFF chunk writeString(0, RIFF); view.setUint32(4, 36 dataSize, true); // 文件总长度减去8字节 writeString(8, WAVE); // fmt chunk writeString(12, fmt ); view.setUint32(16, 16, true); // fmt块长度 view.setUint16(20, 1, true); // 编码格式1表示PCM view.setUint16(22, channels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, sampleRate * channels * bitsPerSample / 8, true); // 字节率 view.setUint16(32, channels * bitsPerSample / 8, true); // 块对齐 view.setUint16(34, bitsPerSample, true); // data chunk writeString(36, data); view.setUint32(40, dataSize, true); return buffer; }这段代码有两个易错点一是所有多字节字段都是小端序true参数因为WAV规范就是小端二是字节率的计算是采样率乘以声道数乘以位深除以8单位是bytes/s写错了播放器会报“文件损坏”。如果录音过程中断头文件里的dataSize就会偏小播放是能播放但音频会提前结束。写入音频数据时我采用循环read的方式。AudioCapturer提供了read方法返回实际读取字节数循环读直到捕获器被stoplet filePath this.context.filesDir / this.fileName; let file fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); let buffer new ArrayBuffer(4096); // 先写一个占位头录音结束后回填正确大小 fs.writeSync(file.fd, new Uint8Array(buildWavHeader(0, 48000, 2, 16))); while (this.isRecording) { let size await audioCapturer.read(buffer, true); if (size 0) { let data buffer.slice(0, size); fs.writeSync(file.fd, new Uint8Array(data)); this.recordedSize size; } } // 停止后回填WAV头 fs.closeSync(file.fd);9.0的AudioCapturer还有一种基于事件回调的方式通过on(readData)监听采集到的数据这种方案对资源消耗更友好不会在while循环里白白空转CPU。但是稍复杂入门阶段用同步读取的循环更容易理解每一步数据去了哪里等这个写熟了再去迁移回调方案逻辑会清晰很多。3.3 录音状态管理与生命周期控制录音过程里至少有idle、recording、paused三个状态加上一个“已停止但文件未保存”的过渡态。我设计了一个枚举类来管理enum RecorderState { IDLE, RECORDING, PAUSED }页面上的按钮根据状态渲染IDLE时显示“开始”RECORDING时显示“暂停”和“停止”PAUSED时显示“继续”和“停止”。这里有个体验细节停止按钮点击后要主动把RecorderState切回IDLE关闭文件句柄同时释放AudioCapturer否则下次点击开始会报“capturer already started”。AudioCapturer本身有release方法负责释放底层麦克风资源。如果录音过程中切换页面或者应用退到后台一定要在onPageHide或者Ability的onBackground里处理stop和release不然麦克风会被一直占着其他应用无法录音。鸿蒙对这类资源占用的管理比较严格不及时释放有时不会立刻报错但后续会积累出诡异行为比如再次打开页面时创建capturer失败。3.4 来电等音频中断处理录音最怕的就是录到一半来电话或者被其他应用抢占麦克风。AudioCapturer注册了audioInterrupt事件后系统在中断发生时会给应用回调。我把这段逻辑放在录音启动后立即注册audioCapturer.on(audioInterrupt, (interrupt: audio.InterruptEvent) { if (interrupt.eventType audio.InterruptType.INTERRUPT_TYPE_BEGIN) { // 中断开始自动暂停保存避免数据丢失 this.pauseRecord(); } else if (interrupt.eventType audio.InterruptType.INTERRUPT_TYPE_END) { // 中断结束可以提示用户手动恢复或者直接自动继续 } });需要注意的是中断后立刻恢复录音会导致WAV文件里多出一个不算长的静默段但这是可接受的真正不可接受的是中断期间还在继续写文件那样录到的会是系统铃声或环境杂音回放体验很差。实际测试过电话进来后如果不暂停录进去的内容会混入通话音。可以认为所有音频类App都有必要处理这类事件录音机这种对原始声音保真要求高的类型尤其如此。4. 录音列表与回放实现4.1 文件扫描与列表数据源录音文件保存在应用沙箱目录下具体路径通过context.filesDir获取。不需要申请存储权限这是鸿蒙应用沙箱的一个优势。扫描目录时我用fs.listFileSync列举文件名再逐一读取文件的修改时间和大小import { fileIo as fs } from kit.CoreFileKit; let dirPath this.context.filesDir; let fileNames fs.listFileSync(dirPath); let fileList: ArrayFileItem []; for (let name of fileNames) { if (!name.endsWith(.wav)) { continue; } let stat fs.statSync(dirPath / name); fileList.push({ name: name, size: stat.size, lastModifiedTime: stat.mtime, // 大约时长根据WAV头推算或通过文件大小近似计算 duration: Math.floor((stat.size - 44) / (48000 * 2 * 2)) }); } // 按修改时间倒序排列 fileList.sort((a, b) b.lastModifiedTime - a.lastModifiedTime);时长字段的计算依赖WAV头的信息。如果只有文件名可以通过“文件大小减去44字节后除以字节率”得到近似秒数。因为采样率48000、双声道、16位深字节率是48000乘2乘2等于192000 bytes/s所以一个10秒的录音文件大小接近1.92MB。这个换算关系我经常用来快速估算不用读文件头也能估算录音文件体积。4.2 AVPlayer播放器接入播放部分我选用AVPlayer它是鸿蒙当前主推的媒体播放器组件创建后通过fdSrc指定源文件。播放前要先利用fs.openSync打开文件拿到fdimport media from ohos.multimedia.media; async playFile(filePath: string) { let file fs.openSync(filePath, fs.OpenMode.READ_ONLY); let avPlayer: media.AVPlayer await media.createAVPlayer(); avPlayer.fdSrc { fd: file.fd, offset: 0, length: file.stat.size }; await avPlayer.prepare(); await avPlayer.play(); }播放器还有一个重要细节是“播完状态复位”。AVPlayer有一个stateChange监听播放结束后state会变成COMPLETED这时候需要把UI上的播放按钮从“播放中”状态恢复成初始样式同时seek到0否则用户再次点击播放会无效。这个不处理的话列表里第一次播放正常第二次点击就毫无反应是我遇到过最典型的播放器坑。4.3 文件删除与异常规避列表项长按弹删除操作实现比较简单调fs.unlinkSync即可。但要注意如果这个文件正在被AVPlayer播放直接删除会失败或者播放器报错。删除前要先把播放器stop掉并释放资源再删源文件。否则就会出现文件删了但播放器手里的fd还挂着界面列表刷新了再点播放会报“file not found”。另一个经验是删除后要立即刷新列表数据别刷新太晚。如果删除逻辑放在异步回调里记得回调里先重新listFileSync再用最新的数据源去驱动UI渲染而不是在旧数组上做删除操作。5. 常见问题与排查技巧实录5.1 权限问题弹窗不出现、授权被判拒绝最常见的原因有几种一是module.json5里没有配置requestPermissions二是配置了但name写错比如写成了RECORD_AUDIOAndroid的权限名三是调用了requestPermissionsFromUser但没有用UIAbilityContext。按我的经验建议先确认工程里能搜到“ohos.permission.MICROPHONE”完整字符串再在申请回调里打印result.code定位。真机首次安装时权限弹窗如果没出现可以先杀掉应用进程重新打开一次偶尔会有缓存导致的不弹窗。5.2 录音文件打不开或播放时长不对这种情况十有八九是WAV头字段写错了。排查顺序先用十六进制工具打开文件看前4个字节是不是RIFF看到WAVE再检查fmt块里的采样率和声道数是否与AudioCapturer创建时的参数一致最后看data块的dataSize是否和实际音频数据长度一致。写一个自检工具函数把WAV头的字段解析后打印出来配合文件大小核对基本一遍就能定位。常见错误是把声道数填成1但在AudioCapturer里用的是CHANNEL_2或者dataSize忘了加8字节导致播放器解析偏差。5.3 录音有数据但声音爆音或者明显失真爆音通常不是音频逻辑写错而是实际设备环境问题。真机测试时离麦克风太近或者系统音量增益过高都会导致削波失真。也有一种情况是buffer大小设置过小比如1024导致频繁I/O在部分设备上会出现咔哒声。我测试下来buffer取4096或8192配合writeSync写入声音是比较稳定的。这里有个建议如果只是做功能验证可以用耳机录音或对着声源远一点测先排除环境干扰再怀疑代码问题。5.4 App切后台麦克风失效如果需求是后台录音目前的方案默认不支持。因为Ability在后台时系统会回收一些前台资源AudioCapturer从后台取数据会变得不稳定。要做干后台录音/锁屏录音建议升级方案用Background Task Manager申请长时任务并在module.json5里声明对应的任务类型。这块逻辑比我这里展开的多不少等录音机主流程跑通了再拓展。单纯做一个Demo录音机不切后台问题不大。5.5 录音文件积累导致存储膨胀WAV文件一分钟大约11.5MB48000Hz双声道16位录10分钟就超过100MB了。这个没有太优雅的解法WAV本身就是高保真高体积的格式。我加了一个简单的“剩余空间提示”当应用沙箱容量不足时停止录音并在页面toast提示避免录到一半磁盘写满。这在打磨产品时是个必须处理的问题。问题现象可能原因排查方法权限弹窗不出现module.json5未配置或context传错检查module.json5与getContext用法录音为空文件循环read未正确写入检查writeSync的size是否0播放时长错乱WAV头dataSize或字节率错误用十六进制工具对照头部字段声音爆音失真设备环境或buffer过小更换录音距离增加buffer到4096二次播放无效播放完成状态未复位监听COMPLETED后reset状态文件删除失败播放器还在持有fd先stop释放再unlink6. 实测心得与后续扩展方向真机跑完整条链路之后几点感触比较深。第一鸿蒙的AudioCapturer从底层采集数据的能力非常强原始PCM数据拿在手里的可控程度远超走上层媒体库接口这对于理解音频处理非常有帮助。第二WAV文件头虽然只有44个字节但写错的概率比想象中高强烈建议封装成独立函数并用自检工具验证。第三鸿蒙现在生态还不算特别大网上能搜到的录音机案例比较少但系统文档和API定义基本够用遇到问题多查API参考别急着怀疑系统bug。扩展方向上我打算下一步把编码格式升级到AAC这样文件体积能缩到原来的1/10左右。另外还想加入分贝仪表盘功能利用AudioCapturer读到的PCM数据计算实时音量并画波形这个在技术上完全可行计算方式就是求每帧样本的RMS或者峰值。再往后可以做语音识别唤醒把特定词作为触发指令那就是语音助手方向的雏形了。回到录音机本身我觉得这个项目作为鸿蒙入门实战非常好它覆盖了权限管理、沙箱文件、多媒体采集、媒体播放、UI联动多个模块难度曲线也平滑。凡是能把这个录音机从头到尾独立做出来的人再去做音乐播放器、语音备忘录、即时通讯语音录制基本都能八九不离十地找到迁移路径。这也是我写这篇记录的核心原因希望你也能把整条链路亲手跑通一遍。

相关新闻

便携式除湿机除湿量性能测试全流程:实验设计、数据处理与避坑实战

便携式除湿机除湿量性能测试全流程:实验设计、数据处理与避坑实战

把一台便携式除湿机放进房间开始测性能,听起来很简单——开机器、接水、按秒表,一天下来看水箱里有多少水就行。但真要把“除湿能力”测到一个能做横向对比、能指导产品改进的精度,事情就没那么轻松了。实际测试中会遇到功耗波动、空间湿度场…

2026/10/13 22:14:54 阅读更多 →
告别循环地狱:R语言Reduce函数与向量化操作实战

告别循环地狱:R语言Reduce函数与向量化操作实战

处理一批同结构的数据集时,大多数人第一步想的是for循环,第二步想到apply家族,但很少有人真正把Reduce和“向量化操作”玩明白。R 语言里处理多个数据集,最痛的并不是读取或合并,而是代码又长又慢,循环一层…

2026/10/13 22:14:54 阅读更多 →
ESP-12F实现WiFi数据透传源码详解:从原理到稳定运行

ESP-12F实现WiFi数据透传源码详解:从原理到稳定运行

接触过不少串口设备联网的项目,凡是涉及到WiFi数据透传,我基本都会先问一句:能不能用ESP-12F?这不是因为它参数多惊艳,而是这类需求里,它往往用最少的成本把串口数据搬上网络。今天这篇就围绕ESP-12F实现Wi…

2026/10/13 22:14:54 阅读更多 →

最新新闻

LaTeX 2025环境配置:texlive + TeXstudio

LaTeX 2025环境配置:texlive + TeXstudio

文章目录Latex2025环境配置一、LaTeX 简介1.1 什么是 LaTeX?1.2 为什么使用 LaTeX?1.3 如何开始使用 LaTeX?二、texlive下载安装2.1 texlive下载2.2 texlive安装2.3 常见问题2.3.1 用管理员权限打开install-tl-windows.bat 报错2.3.2 Latex安…

2026/10/14 2:20:23 阅读更多 →
从工勘图到资源库:提交 nVisual Studio 与机房设计全生命周期的闭环

从工勘图到资源库:提交 nVisual Studio 与机房设计全生命周期的闭环

从工勘图到资源库:提交 nVisual Studio 与机房设计全生命周期的闭环摘要:工勘图纸的价值在「进入可检索、可复用资源库」之后才被放大——本地文件解决现场与交付,平台解决长期资产。DCDesigner 的「提交 nVisual Studio」把工勘 JSON 一键回…

2026/10/14 2:20:23 阅读更多 →
因果推断教材实战:从AB实验到双重机器学习与策略学习

因果推断教材实战:从AB实验到双重机器学习与策略学习

简介:这份资源是斯坦福大学Stefan Wager于2024年9月更新的《Causal Inference: A Statistical Learning Approach》教科书草稿,面向数据科学、统计学、经济学与公共政策领域的学习者和研究者,帮助其系统掌握基于统计学习方法的因果推断理论与…

2026/10/14 2:20:23 阅读更多 →
游集美Day1:逛完学村走老巷,被这几家街坊常来的小店拦住了脚步

游集美Day1:逛完学村走老巷,被这几家街坊常来的小店拦住了脚步

很多人来集美,打卡完嘉庚建筑、龙舟池,拍几张照片就匆匆返程。 但集美真正动人的烟火,不在网红打卡点,藏在黄昏的社区老巷。 这条路线我按地理位置理顺,不跨区乱跑,骑行顺路,慢逛无赶路压力&…

2026/10/14 2:20:23 阅读更多 →
系统辨识实战教案:M序列设计、RLS实现与工业白化检验

系统辨识实战教案:M序列设计、RLS实现与工业白化检验

简介:本资源是一份面向自动化专业本科生与研究生的《系统辨识》课程教案,聚焦工程建模核心能力培养,解决从实际系统数据中构建可用数学模型的关键问题。教案覆盖实体与模型本质辨析、Zadeh三要素(数据/模型类/准则)理论…

2026/10/14 2:20:23 阅读更多 →
AI 辅助科研溯源闸门:输入·命令·决策·证据四类记录的失败含义与当天最小实验

AI 辅助科研溯源闸门:输入·命令·决策·证据四类记录的失败含义与当天最小实验

千笔-AIWritePaper https://www.aiwritepaper.com Agent 能很快从原始表跑出一张宇宙学风格的斜率图,不等于结果可审查。缺了输入校验和,文件可以被静默替换;命令行事后改参,YAML 里仍写着旧参数;决策条目没有理由&am…

2026/10/14 2:19:23 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →