-
智能手表行业曾长期陷入 “参数堆砌” 与 “功能同质化” 的瓶颈:健康数据零散无解读、交互依赖屏幕滑动、设备间联动割裂。华为鸿蒙运动手表(以 WATCH 5 为代表)的出现,通过 “端 - 软 - 云” 三大技术底座的深度协同,首次将 AI 大模型、分布式感知、全场景互联融入腕上设备,彻底重构了智能穿戴的技术范式。它不再是简单的 “数据采集工具”,而是进化为具备主动感知、智能分析、跨端协同能力的 “腕上智慧伙伴”,标志着穿戴设备正式迈入鸿蒙 AI 时代。一、技术内核:“端 - 软 - 云” 协同的全栈架构鸿蒙运动手表的核心竞争力,源于华为打造的 “硬件端算力支撑 + 软件层智能调度 + 云端大模型赋能” 的闭环体系,三者协同实现 “数据采集 - 分析 - 决策 - 服务” 的全链路优化。1. 硬件端:分布式感知与算力升级硬件是技术落地的基础,鸿蒙运动手表通过架构创新与核心组件升级,解决了传统穿戴设备 “感知精度不足、算力有限” 的痛点:双引擎智慧架构:搭载麒麟芯片与 NPU 神经网络单元,为复杂健康算法(如 ECG 心电分析、高血压风险评估)和 AI 交互提供强大算力支撑,确保本地数据处理延迟低于 100ms,无需依赖手机即可完成核心功能。X-TAP 智感窗:分布式感知革命:作为玄玑感知系统的核心创新,将 ECG(心电)、PPG(光电容积)、压感三大高精度传感器集成于表体侧面,与表底超感知模组形成分布式部署。这种微米级架构突破,让健康检测从 “多步骤操作” 浓缩为 “一触即达”—— 长按 3 秒开启 60 秒微体检,覆盖 17 项健康指标;指尖轻触 10 秒完成血氧检测,持续按压可实现秒级实时刷新,适配高原旅行、呼吸健康监测等场景。先锋通信技术:全球首款搭载星闪技术的智能手表,构建稳定高速的数据传输通道。星闪车钥匙功能实现 10 米唤起迎宾灯语、3 米精准解锁、8 米自动锁车,定位精度与解闭锁精确度较传统蓝牙车钥匙分别提升 5 倍和 6 倍,重新定义人车交互体验。2. 软件层:HarmonyOS 5.1 的轻量化与智能化优化针对穿戴设备 “资源有限、续航敏感” 的特性,HarmonyOS 5.1 进行了深度定制,实现 “流畅度与功耗的平衡”:轻量化内核与精准调度:裁剪冗余服务与组件,保留核心功能模块,系统占用内存降低 30%;智能识别关键任务(如心率监测、消息通知)与非关键任务,优先为核心功能分配算力,后台任务采用延迟调度与合并唤醒机制,既保证抬腕响应零延迟,又减少 CPU 频繁唤醒导致的功耗浪费。精细化功耗管理:与硬件深度耦合,通过传感器融合判断设备使用状态 —— 手臂下垂时快速进入低功耗熄屏模式;AOD 熄屏显示采用动态像素管理,减少点亮区域;蓝牙连接采用自适应心跳机制,根据数据交互需求动态调整间隔,续航较前代提升 20% 以上。场景化智能交互:支持 “划一划”“敲一敲” 手势操控,覆盖电话接挂、音视频切换、遥控拍照等高频操作;智慧助手小艺升级语音控制 100 + 功能,从饮食录入、运动目标设置到健康数据查询,实现 “一句话直达”。3. 云端:大模型赋能的智慧升级鸿蒙运动手表的 “智能” 核心源于云端大模型的加持,彻底解决了传统手表 “有数据、无智慧” 的行业痛点:融合大模型架构:接入盘古大模型与 DeepSeek 大模型,并通过运动健康专业模型精调,具备复杂推理与深度语义理解能力。小艺不仅能响应语音指令,还能基于用户心率、睡眠、运动等多维度数据,提供个性化健康问答与改善建议,覆盖 22 类运动场景与近 200 项健康指标分析。健康数据智慧解读:首发 “健康摘要 APP”,可分析不同场景下健康指标的关联性(如睡眠质量与心率变异性的关联),异常时推送针对性建议。例如,当检测到用户睡眠数据偏差较大时,自动推荐睡前放松模式,调整晨间闹钟并同步至手机日程。二、核心功能突破:从健康监测到全场景互联鸿蒙运动手表以技术为支撑,在健康管理、运动辅助、跨端协同三大核心场景实现跨越式创新:1. 健康管理:从数据采集到风险早筛依托玄玑感知系统与 AI 大模型,鸿蒙运动手表构建了 “全维度、专业化” 的健康管理体系:多维度健康监测:覆盖心血管健康(ECG 心电分析、血管弹性研究、心律失常统计)、内分泌健康(卵巢功能评估)、呼吸健康(血氧、呼吸暂停异常监测)、情绪健康等领域,实现 “日常监测 - 风险预警 - 专业建议” 的闭环。高血压风险评估:联合上海交通大学附属医院瑞金医院发起免气囊式高血压风险研究,用户通过 “华为创新研究” APP 即可加入,无需额外设备即可完成日常血压管理与风险识别,为潜在高血压人群提供早期干预依据。生态协同健康管理:接入 OtterLife 情绪监测、Grow 压力缓解、美柚女性健康等第三方应用,通过震动反馈、个性化建议等形式,实现身心同频养护,构建开放的健康生态。2. 运动辅助:个性化指导与数据精准分析针对不同运动场景,提供从目标设置到效果评估的全流程支持:运动模式适配:覆盖跑步、游泳、骑行等数十种运动模式,结合传感器数据精准计算卡路里消耗、运动时长、配速等指标;AI 个性化建议:基于用户运动历史、身体状态数据,推荐合适的运动强度与时长,避免过度运动或训练不足;运动后生成详细报告,分析心率区间分布、运动效果,辅助优化训练计划。3. 全场景互联:打破设备孤岛HarmonyOS 的分布式能力让鸿蒙运动手表成为全场景生态的核心节点:跨设备协同:与华为手机、耳机、体脂秤、智能家居等设备无缝联动 —— 手机导航信息自动流转至手表,抬腕即可查看下一步指引;运动时耳机自动切换至运动模式,手表控制音量与曲目;紧急救援协同:驾车时遭遇意外,手表自动检测佩戴状态与心率数据,同步上传求助信息,为救援提供身体状态参考;生态拓展:支持星闪车钥匙、智能家居控制等场景,实现 “一表控万物”,成为用户与智能设备交互的第一触点。三、技术护城河:十二年深耕的 “产 - 学 - 研 - 用” 闭环鸿蒙运动手表的技术突破,源于华为在穿戴领域十二年的持续积累与开放合作:技术沉淀:从 2015 年第一代 WATCH 亮相,到玄玑感知系统发布,再到鸿蒙 AI 架构落地,华为构建了 “硬件创新 - 软件优化 - 生态完善” 的技术迭代路径,全球穿戴设备发货量累计超 2 亿台;专业合作:联合 100 家机构开展 200 余项创新研究,1300 万用户参与数据积累,形成 “用户数据 - 健康研究 - 产品迭代” 的闭环。从高血糖风险评估到高血压风险研究,持续推进可穿戴设备在医疗健康领域的专业化落地;生态开放:兼容第三方健康应用与智能设备,通过 HarmonyOS Connect 实现快速接入,构建多元化的全场景生态,让技术创新惠及更多用户。总结:鸿蒙运动手表的技术价值与行业影响鸿蒙运动手表以 “端 - 软 - 云” 协同架构为核心,通过 X-TAP 智感窗的硬件创新、HarmonyOS 5.1 的轻量化优化、大模型赋能的智慧升级,重新定义了智能手表的技术标准。它的出现,不仅解决了行业 “数据零散、交互繁琐、续航焦虑” 的核心痛点,更将穿戴设备从 “消费电子产品” 升级为 “专业健康管理伙伴” 与 “全场景交互中枢”。未来,随着 AI 大模型的持续迭代、健康监测技术的深化(如更多疾病风险早筛功能)、全场景生态的进一步拓展,鸿蒙运动手表将在健康管理、智能交互、跨端协同等领域持续突破,推动智能穿戴行业从 “功能满足” 向 “体验升级”“价值创造” 转型,成为数字健康时代的核心入口。
-
在开源鸿蒙的实际部署中,init.cfg 是系统启动的关键配置文件,但稍有格式错误就可能导致设备卡在 Logo、系统进程无法启动。DontCrack 正是为此而生 —— 一个专注于鸿蒙 Linux Kernel 侧的高可靠进程管理器。✅ 核心优势低耦合高隔离:不挑管理对象,支持二进制、Shell、Python、Node、Perl、Ruby 等脚本时序稳定性保障:避免因启动顺序或配置错误导致系统异常Restful API 控制:支持 /startup、/shutdown、/heartbeat 等接口,轻松远程管理配置灵活:支持独立设置路径、参数、环境变量、预处理脚本、自动重启策略、日志缓存等跨架构免 CGO:Go 编译即可运行,支持 ARM、x86 等架构,适配嵌入式与模拟器环境🛠️ 快速启动示例bash./DontCrack -path /home/test_program.sh \ -args "-key=test123 -shell=/bin/bash" \ -start-now🌐 项目地址📦 GitCode 开源仓库: 👉https://gitcode.com/tyza66/DontCrack📄 示例配置文件: 👉init.cfg 示例🔜 TodoList(欢迎共建)日志本地持久化与定时清理自动重启次数复位与无限重启开关API 访问加密与远程控制增强结构重构,提升可维护性与扩展性欢迎开源鸿蒙社区的伙伴试用、反馈、共建!DontCrack 致力于让init.cfg 更安全、更稳定、更可控,为设备启动保驾护航。如需适配嵌入式平台、模拟器环境或定制启动策略,欢迎交流!# 开鸿Developer社区
-
在开源鸿蒙的实际部署中,init.cfg 是系统启动的关键配置文件,但稍有格式错误就可能导致设备卡在 Logo、系统进程无法启动。DontCrack 正是为此而生 —— 一个专注于鸿蒙 Linux Kernel 侧的高可靠进程管理器。✅ 核心优势低耦合高隔离:不挑管理对象,支持二进制、Shell、Python、Node、Perl、Ruby 等脚本时序稳定性保障:避免因启动顺序或配置错误导致系统异常Restful API 控制:支持 /startup、/shutdown、/heartbeat 等接口,轻松远程管理配置灵活:支持独立设置路径、参数、环境变量、预处理脚本、自动重启策略、日志缓存等跨架构免 CGO:Go 编译即可运行,支持 ARM、x86 等架构,适配嵌入式与模拟器环境🛠️ 快速启动示例bash./DontCrack -path /home/test_program.sh-args “-key=test123 -shell=/bin/bash”-start-now🌐 项目地址📦 GitCode 开源仓库: 👉 https://gitcode.com/tyza66/DontCrack📄 示例配置文件: 👉 init.cfg 示例🔜 TodoList(欢迎共建)日志本地持久化与定时清理自动重启次数复位与无限重启开关API 访问加密与远程控制增强结构重构,提升可维护性与扩展性欢迎开源鸿蒙社区的伙伴试用、反馈、共建!DontCrack 致力于让 init.cfg 更安全、更稳定、更可控,为设备启动保驾护航。如需适配嵌入式平台、模拟器环境或定制启动策略,欢迎交流!
-
一、问题说明对象数组使用ForEach进行循环遍历渲染时,将循环的Item的数据进行改变,数据发生变化但是ui没有进行刷新 二、原因分析1.数据源未深度检测;2.数据引用地址未更新;3.ForEach使用不当 三、解决思路1.当数据源为嵌套对象或数组时,若未使用@Observed/ObservedV2装饰器修饰类,属性变更无法触发UI刷新。2.直接修改this.list.property = newValue 但未装饰数据类。 四、解决方法1.装饰器说明:@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步(1).使用new创建被@Observed装饰的类,可以被观察到属性的变化。(2).子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。(3.)@Observed用于嵌套类场景中,观察对象类属性变化,要配合自定义组件使用(示例详见嵌套对象),如果要做数据双/单向同步,需要搭配@ObjectLink或者@Prop使用(示例详见@Prop与@ObjectLink的差异)。(4).@ObjectLink不支持简单类型,如果开发者需要使用简单类型,可以使用@Prop。 2.代码示例(1).在item导出class添加@Observed装饰器(2).定义接收数据并在赋值的同时将每一项new出来(3).使用@ObjectLink监听子组件单项数据进行渲染 五、总结UI刷新依赖数据引用地址变化或深度观测装饰器。对于List组件,优先组合使用@Observed+新对象创建,或LazyDataSource的notifyDataReload()+引用变更。
-
在开发过程中遇到两个毫无联系的组件,页面,可以公共EmitterUtil工具来实现跨组件事件调用。传参 Emitter工具类(进行线程间通信)1.发送事件(示例) EmitterUtil.post(Keys.SHOP_COUNT, true)第一个参数是命名事件ID,string类型的eventId不支持空字符串第二个参数为要传递的参数注:在A页面一个方法即将执行完成要与B页面发生交互的时候调用该方法2.接收订阅事件(示例) EmitterUtil.onSubscribe<boolean>(Keys.SHOP_COUNT, (data: boolean) => { this.shopCount()})第一个参数同样是命名事件ID,string类型的eventId不支持空字符串第二个参数为callback 事件的回调处理函数可以在该函数内进行参数赋值,方法调用注:在该页面的生命周期aboutToAppear内调用3.取消订阅事件(示例) aboutToDisappear(): void { EmitterUtil.unSubscribe(Keys.SHOP_COUNT)}注:参数是之前定义好的事件ID,调用取消订阅释放内存
-
由于在项目开发过程中需要将一些数据隐藏,但是又不想暴露出去,可以将数据放到so库中,在so库中经过一些加密算法的加工在给arkts端使用。以下是自定义的so库的步骤。1.生成.so创建Native工程:DevEco Studio -> File -> New -> Create Project -> Native C++ 创建成功之后,main目录下会有一个cpp目录,在cpp中可以编写自己的c代码了 其中 Index.d.ts: 是一个声明文件,用来声明导出的 C++ 函数,在 JS 中可以直接使用这些函数。oh-package.json5: 这是一个配置文件,用来配置so名称、版本等信息CMakeLists.txt、napi_init.cpp: C++代码以及 CMakeLists.txt 文件,用来编译生成 .so 文件,.cpp 文件内用于编写你的逻辑代码我的c代码,大致如下:其中,.nm_modname = "entry",必须和你的目录名字保持一致。将你的函数注册到index.d.ts中即可2.打包Build -> Build Module,在build -> intermediates -> libs -> default目录下生成.so 3.使用.so将自己的so库copy到你的项目中,放到新建的libs下在oh-package.json5添加依赖在使用的地方引入以上就可以成功调用了
-
一,问题说明原先已实现 UniAPP 项目转鸿蒙,客户提出需要实现类似于微信加载多个小程序并能热更新二、需求分析实现热更新需要能够动态加载小程序资源包,原先的方案是将小程序项目直接打包进鸿蒙 hap中,只支持单个小程序不支持热更新三、解决思路通过查阅UniApp 官方网站,找到小程序动态加载方案及sdk,根据客户需求进行开发适配实现需求四、解决方案可通过 DCloud 平台配置小程序热更新资源包,或将 wgt 资源包上传自己的服务器,app下载资源包更新(一).开发环境DevEco-Studio 5.0.3.800 以上鸿蒙系统版本 API 12 以上 (DevEco-Studio有内置鸿蒙模拟器)HBuilderX-4.27+ 下载uni小程序 SDK不支持x86模拟器(二).配置uni小程序SDK1.修改鸿蒙项目根目录文件 oh-package.json5 的依赖 "@dcloudio/uni-app-runtime": "版本号" 2.点击右上角 Sync Now,并等待 Sync 结束(三).通过wgt包导入小程序应用资源选中您的 uni-app 项目,右键->发行->App-制作应用wgt包 项目编译完成后会在控制台,输出wgt包的路径,点击路径可以直接打开wgt所在目录 如图,__UNI__6275E02.wgt 就是应用资源包,(__UNI__6275E02 为小程序的 appid)如果提示导出失败,请删除项目根目录 manifest.json 源码里的 app-harmony 属性 将生成的wgt包拷贝到 entry/src/main/resources/resfile 目录下,如下图所示 再通过 releaseWgtToRunPath 函数释放 wgt 包到运行目录,最后通过 openUniMP 函数打开小程序,代码如下import { openUniMP,isExistsUniMP, releaseWgtToRunPath } from '@dcloudio/uni-app-runtime';@Entry@Componentstruct Index { @State message: string = 'Hello World'; build() { RelativeContainer() { Text(this.message) .id('HelloWorld') .fontSize(50) .fontWeight(FontWeight.Bold) .alignRules({ center: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center } }) .onClick(async ()=>{ const mpId = "__UNI__6275E02" await new Promise<void>((resolve, reject) => { try { // 判断应用是否已释放到运行目录 let isExists = isExistsUniMP(mpId) console.log("isExists:"+isExists) // 拼接wgt包路径 let path = getContext().resourceDir + "/"+mpId+".wgt" // 释放 wgt 包到运行目录 releaseWgtToRunPath(mpId,path, (code:number, data: object)=>{ console.log(JSON.stringify({code,data})) resolve() }) } catch(err){ reject(err) } }) // 启动小程序 const mp = openUniMP(mpId) mp.on('close',()=>{ console.log('UniMP-close') }) mp.on('show',()=>{ console.log('UniMP-show') }) mp.on('hide',()=>{ console.log('UniMP-hide') }) }) } .height('100%') .width('100%') }}
-
1. 提前初始化Web引擎 在`页面`的`aboutToAppear`阶段调用`initializeWebEngine()`,预加载Web内核动态库,减少首次白屏时间(约优化140ms)。 aboutToAppear(): void { webview.WebviewController.initializeWebEngine(); // 启动时预加载内核}2. DNS预解析与Socket预连接 在web组件的`onAppear`阶段对目标URL调用`prepareForPageLoad()`,提前建立网络连接(优化约80ms)。 注:url替换成自己的url就可以 .onAppear(() => {webview.WebviewController.prepareForPageLoad(this.url, true, 2)})3. 页面资源预加载 在`onPageEnd`事件中预判用户下一步操作,用`prefetchPage()`下载下一页资源(如小说/商品详情页) .onPageEnd(()=>{ this.webController.prefetchPage('https://example.com/next-page');})二、渲染与架构优化(进阶提升)1. 预渲染高频页面 对确定性跳转页(如首页→详情),通过`NodeController`创建隐藏的Web组件进行后台渲染,切换时直接展示。 ```typescript // 创建NodeController管理离线Web组件 const nodeController = new NodeController(); nodeController.buildHiddenWebView(url); // 后台渲染 // 跳转时挂载到NodeContainer显示 ```2. 动态加载非必要模块 使用“动态import() ”按需加载子页面,减少主包体积(主页加载耗时从22.9ms→7.9ms)。 ```typescript Button('跳转') .onClick(async () => { const module = await import('./DetailPage'); // 点击时才加载 this.pageStack.pushPath(module.buildPage); }) ```3. 优化H5页面设计 - 合并CSS/JS文件,压缩资源(Webpack/Vite配置`manualChunks`分割代码) - 图片懒加载:`<Image loadMode="lazy">` - 长任务拆解:用`Web Worker`执行耗时逻辑
-
由于AES为分组加密算法,分组长度为128位。如果最后一组明文不足128位(16字节),可以通过不同的填充模式进行数据填充1.问题代码中填充模式选择NoPadding,不会对明文进行填充,所以当最后一组不足128位时程序崩溃,可以选择PKCS5和PKCS7进行填充注:选择PKCS7进行填充加密后再解密,解密数据后会加上一串二进制字符串 需要将用到的数据进行截取2.如果需要ZeroPadding,需要开发者手动对密文进行填充。stringPadding(str: string){ let len = str.length switch (len % 16) { case 0: break; case 1: str.padEnd(16 - 1, '0'); break; case 2: str.padEnd(16 - 2, '0'); break; case 3: str.padEnd(16 - 3, '0'); break; case 4: str.padEnd(16 - 4, '0'); break; case 5: str.padEnd(16 - 5, '0'); break; case 6: str.padEnd(16 - 6, '0'); break; case 7: str.padEnd(16 - 7, '0'); break; case 8: str.padEnd(16 - 8, '0'); break; case 9: str.padEnd(16 - 9, '0'); break; case 10: str.padEnd(16 - 10, '0'); break; case 11: str.padEnd(16 - 11, '0'); break; case 12: str.padEnd(16 - 12, '0'); break; case 13: str.padEnd(16 - 13, '0'); break; case 14: str.padEnd(16 - 14, '0'); break; case 15: str.padEnd(16 - 15, '0'); break; }}注:手动加0填充后解密出来的数据后会加对应16位的字符串0需要手动截取
-
示例demo是以录音ohos.permission.MICROPHONE权限为例子1.通过requestPermissionsFromUser接口进行权限状态获取关键代码: let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();let context = getContext(this) as common.UIAbilityContext;// 等待权限请求完成const data: PermissionRequestResult = await atManager.requestPermissionsFromUser(context, ['ohos.permission.MICROPHONE']);2.通过checkAccessToken接口进行首次授权弹窗拉起关键代码: let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;let tokenID: number = appInfo.accessTokenId;atManager.checkAccessToken(tokenID, 'ohos.permission.MICROPHONE').then((data: abilityAccessCtrl.GrantStatus) => { const authResult = data === 0 ? 1 : 0; console.log(`checkAccessToken success, data->${JSON.stringify(data)}`,authResult.toString());}).catch((err: BusinessError) => { console.error(`checkAccessToken fail, err->${JSON.stringify(err)}`);});3.首次弹窗取消或拒绝后checkAccessToken接口将无法二次拉起,所以需要通过requestPermissionOnSetting进行系统授权弹窗拉起关键代码: let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();let context= getContext(this) as common.UIAbilityContext;atManager.requestPermissionOnSetting(context, ['ohos.permission.MICROPHONE']).then((data: Array<abilityAccessCtrl.GrantStatus>) => { console.info('data:' + JSON.stringify(data)); const authResult = data[0] === 0 ? 1 : 0;}).catch((err: BusinessError) => { console.error('data:' + JSON.stringify(err));});
-
如何除saveButton按钮外保存视频/图片等使用弹窗授权保存图片视频/** * 保存base64图片/视频到本地相册 */async saveQRCodeImage(type: string, base64: string) { if (!base64) { return } let context = getContext(this) as common.UIAbilityContext; let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); //判断是视频还是图片 if (type.includes("video")) { try { let content = base64.split(',')[1] let dirPath: string = context.cacheDir; let fileName = 'test.mp4' this.writeBufferToFile(dirPath, fileName, buffer.from(content, 'base64').buffer) let uri = fileUri.getUriFromPath(`${dirPath}/${fileName}`); let srcFileUris: Array<string> = [uri ];// 实际场景请使用真实的uri let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{ title: 'test', fileNameExtension: 'mp4', photoType: photoAccessHelper.PhotoType.VIDEO, subtype: photoAccessHelper.PhotoSubtype.DEFAULT, } ]; let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs); if (desFileUris.length > 0) { for (let index = 0; index < desFileUris.length; index++) { this.copyFileContentTos(srcFileUris[0], desFileUris[index]) promptAction.showToast({ message:$r('app.string.save_success')}) } } } catch (err) { promptAction.showToast({ message: $r('app.string.save_failed') }) console.error('showAssetsCreationDialog failed, errCode is ' + err.code + ', errMsg is ' + err.message); }} else { try { const modifiedData = base64.replace( "data:application/octet-stream;base64", "data:image/png;base64" ); let content = modifiedData.split(',')[1] let dirPath: string = context.cacheDir; let fileName = 'test11.jpg' this.writeBufferToFile(dirPath, fileName, buffer.from(content, 'base64').buffer) let uri = fileUri.getUriFromPath(`${dirPath}/${fileName}`); let srcFileUris: Array<string> = [uri ];// 实际场景请使用真实的uri let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{ title: 'test', fileNameExtension: 'png', photoType: photoAccessHelper.PhotoType.IMAGE, subtype: photoAccessHelper.PhotoSubtype.DEFAULT, } ]; let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs); if (desFileUris.length > 0) { for (let index = 0; index < desFileUris.length; index++) { this.copyFileContentTo(srcFileUris[0], desFileUris[index]) } promptAction.showToast({ message:$r('app.string.save_success')}) } } catch (err) { promptAction.showToast({ message: $r('app.string.save_failed') }) console.error('showAssetsCreationDialog failed, errCode is ' + err.code + ', errMsg is ' + err.message); } } }copyFileContentTo(srcFilePath: string, destFilePath: string) { let srcFile = fs.openSync(srcFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) let destFile = fs.openSync(destFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) // 读取源文件内容并写入至目的文件 let bufSize = 4096; let readSize = 0; let buf = new ArrayBuffer(bufSize); let readOptions: ReadOptions = { offset: readSize, length: bufSize }; let readLen = fs.readSync(srcFile.fd, buf, readOptions); while (readLen > 0) { readSize += readLen; let writeOptions: WriteOptions = { length: readLen }; fs.writeSync(destFile.fd, buf, writeOptions); readOptions.offset = readSize; readLen = fs.readSync(srcFile.fd, buf, readOptions); } // 关闭文件 fs.closeSync(srcFile); fs.closeSync(destFile);}copyFileContentTos(srcFilePath: string, destFilePath: string) { let srcFile = fs.openSync(srcFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) let destFile = fs.openSync(destFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) // 读取源文件内容并写入至目的文件 let bufSize = 20400; let readSize = 0; let buf = new ArrayBuffer(bufSize); let readOptions: ReadOptions = { offset: readSize, length: bufSize }; let readLen = fs.readSync(srcFile.fd, buf, readOptions); while (readLen > 0) { readSize += readLen; let writeOptions: WriteOptions = { length: readLen }; fs.writeSync(destFile.fd, buf, writeOptions); readOptions.offset = readSize; readLen = fs.readSync(srcFile.fd, buf, readOptions); } // 关闭文件 fs.closeSync(srcFile); fs.closeSync(destFile);}
-
原生跳转拨打电话页面原创男孩收纳专栏 : 日常记录 2025-08-01 18:32发布于:山西 21开发步骤1.import需要的模块。2.调用hasVoiceCapability()接口获取当前设备呼叫能力,如果支持继续下一步;如果不支持则无法发起呼叫。3.跳转到拨号界面,并显示拨号的号码。 // import需要的模块import call from '@ohos.telephony.call';import observer from '@ohos.telephony.observer';// 调用查询能力接口let isSupport = call.hasVoiceCapability();if (!isSupport) { console.log("not support voice capability, return."); return;}// 如果设备支持呼叫能力,则继续跳转到拨号界面,并显示拨号的号码call.makeCall("13xxxx", (err)=> { if (!err) { console.log("make call success."); } else { console.log("make call fail, err is:" + JSON.stringify(err)); }});// 订阅通话业务状态变化(可选)observer.on("callStateChange", (data) => { console.log("call state change, data is:" + JSON.stringify(data));});
-
一丶使用DialogHelper三方组件进行全局或者当前页面loading加载1.首先在页面@state一个弹窗id(在本页使用或者在全局使用都会用到)@statedialogId:string = ''''2.在需要loading加载的地方进行DialogHelper调用this.dialogId = DialogHelper.showLoadingDialog({ autoCancel: false })3.loading弹窗取消DialogHelper.closeDialog(this.dialogId)注:如果在全局使用当前loading可以通过持久化的方式存储dialogId然后在取消的时候进行调用二丶自定义封装弹窗1.可以自定义封装一个LoadingProgress2.通过自定义方法动态绑定该弹窗的Visibility属性: (1) Visibility.Visible 显示(2) Visibility.None 移除布局不占位3.将该组件暴露出去,在使用的地方使用方法进行显隐控制注:这是一个大概的思路,希望可以帮助到大家
-
一丶可以使用官方文档的@ohos.net.http(数据请求)进行搭建1.导入模块import { http } from '@kit.NetworkKit';2.示例代码// 引入包名import { http } from '@kit.NetworkKit';import { BusinessError } from '@kit.BasicServicesKit';// 每一个httpRequest对应一个HTTP请求任务,不可复用。let httpRequest = http.createHttp();// 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息。// 从API 8开始,使用on('headersReceive', Callback)替代on('headerReceive', AsyncCallback)。 8+httpRequest.on('headersReceive', (header: Object) => { console.info('header: ' + JSON.stringify(header));});httpRequest.request(// 填写HTTP请求的URL地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定。 "EXAMPLE_URL", { method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET。 // 当使用POST请求时此字段用于传递请求体内容,具体格式与服务端协商确定。 extraData: 'data to send', expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型。 usingCache: true, // 可选,默认为true。 priority: 1, // 可选,默认为1。 // 开发者根据自身业务需要添加header字段。 header: { 'Accept' : 'application/json' }, readTimeout: 60000, // 可选,默认为60000ms。 connectTimeout: 60000, // 可选,默认为60000ms。 usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定。 usingProxy: false, //可选,默认不使用网络代理,自API 10开始支持该属性。 caPath: '/path/to/cacert.pem', // 可选,默认使用系统预设CA证书,自API 10开始支持该属性。 clientCert: { // 可选,默认不使用客户端证书,自API 11开始支持该属性。 certPath: '/path/to/client.pem', // 默认不使用客户端证书,自API 11开始支持该属性。 keyPath: '/path/to/client.key', // 若证书包含Key信息,传入空字符串,自API 11开始支持该属性。 certType: http.CertType.PEM, // 可选,默认使用PEM,自API 11开始支持该属性。 keyPassword: "passwordToKey" // 可选,输入key文件的密码,自API 11开始支持该属性。 }, certificatePinning: [ // 可选,支持证书锁定配置信息的动态设置,自API 12开始支持该属性。 { publicKeyHash: 'Pin1', // 由应用传入的证书PIN码,自API 12开始支持该属性。 hashAlgorithm: 'SHA-256' // 加密算法,当前仅支持SHA-256,自API 12开始支持该属性。 }, { publicKeyHash: 'Pin2', // 由应用传入的证书PIN码,自API 12开始支持该属性。 hashAlgorithm: 'SHA-256' // 加密算法,当前仅支持SHA-256,自API 12开始支持该属性。 } ], multiFormDataList: [ // 可选,仅当Header中,'content-Type'为'multipart/form-data'时生效,自API 11开始支持该属性。 { name: "Part1", // 数据名,自API 11开始支持该属性。 contentType: 'text/plain', // 数据类型,自API 11开始支持该属性。 data: 'Example data', // 可选,数据内容,自API 11开始支持该属性。 remoteFileName: 'example.txt' // 可选,自API 11开始支持该属性。 }, { name: "Part2", // 数据名,自API 11开始支持该属性。 contentType: 'text/plain', // 数据类型,自API 11开始支持该属性。 // data/app/el2/100/base/com.example.myapplication/haps/entry/files/fileName.txt filePath: `${getContext(this).filesDir}/fileName.txt`, // 可选,传入文件路径,自API 11开始支持该属性。 remoteFileName: 'fileName.txt' // 可选,自API 11开始支持该属性。 } ] }, (err: BusinessError, data: http.HttpResponse) => { if (!err) { // data.result为HTTP响应内容,可根据业务需要进行解析。 console.info('Result:' + JSON.stringify(data.result)); console.info('code:' + JSON.stringify(data.responseCode)); console.info('type:' + JSON.stringify(data.resultType)); // data.header为HTTP响应头,可根据业务需要进行解析。 console.info('header:' + JSON.stringify(data.header)); console.info('cookies:' + JSON.stringify(data.cookies)); // 自API version 8开始支持cookie。 // 取消订阅HTTP响应头事件。 httpRequest.off('headersReceive'); // 当该请求使用完毕时,开发者务必调用destroy方法主动销毁该JavaScript Object。 httpRequest.destroy(); } else { console.info('error:' + JSON.stringify(err)); // 取消订阅HTTP响应头事件。 httpRequest.off('headersReceive'); // 当该请求使用完毕时,开发者务必调用destroy方法主动销毁该JavaScript Object。 httpRequest.destroy(); } });二丶axios:Axios,是一个基于 promise 的网络请求库,本库基于Axios 原库v1.3.4版本进行适配,使其可以运行在 OpenHarmony。1.下载安装依赖ohpm install @ohos/axios2.进行权限设置ohos.permission.INTERNET3.接口与属性列表接口参数功能axios(config)config:请求配置发送请求axios.create(config)config:请求配置创建实例axios.request(config)config:请求配置发送请求axios.get(url[, config])url:请求地址config:请求配置发送get请求axios.delete(url[, config])url:请求地址config:请求配置发送delete请求axios.post(url[, data[, config]])url:请求地址data:发送请求体数据config:请求配置发送post请求axios.put(url[, data[, config]])url:请求地址data:发送请求体数据config:请求配置发送put请求属性列表属性描述axios.defaults['xxx']默认设置 。值为请求配置 config 中的配置项例如 axios.defaults.headers 获取头部信息axios.interceptors拦截器。参考 拦截器 的使用说明:由于ArkTS不再支持any类型,需指定参数的具体类型。 如:axios.get<T = any, R = AxiosResponse, D = any>(url)T: 是响应数据类型。当发送一个 POST 请求时,客户端可能会收到一个 JSON 对象。T 就是这个 JSON 对象的类型。默认情况下,T 是 any,这意味着可以接收任何类型的数据。R: 是响应体的类型。当服务器返回一个响应时,响应体通常是一个 JSON 对象。R 就是这个 JSON 对象的类型。默认情况下,R 是 AxiosResponse,这意味着响应体是一个 AxiosResponse 对象,它的 data 属性是 T 类型的D: 是请求参数的类型。当发送一个 GET 请求时,可能会在 URL 中添加一些查询参数。D 就是这些查询参数的类型。参数为空情况下,D 是 null类型。4.示例interface user { firstName: string, lastName: string } axios.post<string, AxiosResponse<string>, user>('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then((response: AxiosResponse<string>) => { console.info(JSON.stringify(response)); }) .catch((error) => { console.info(JSON.stringify(error)); });三丶@yunkss/ef_rcp三方库的组件efRcpClientApi
-
1.获取应用窗口 let windowClass: window.Window = windowStage.getMainWindowSync(); // 获取应用主窗口2.注册监听函数,动态获取避让区域数据 windowClass.on('avoidAreaChange', (data) => { // 判断当前变化的避让区域类型是否为系统避让区域 if (data.type === window.AvoidAreaType.TYPE_SYSTEM) { // 获取系统避让区域的高度并存储 let topRectHeight = data.area.topRect.height; LogUtil.info('状态栏高度:',topRectHeight.toString()) AppStorage.setOrCreate(Keys.topRectHeight, topRectHeight); } else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) { // 获取导航条避让区域的高度并存储 let bottomRectHeight = data.area.bottomRect.height; LogUtil.info('导航栏高度:',bottomRectHeight.toString()) AppStorage.setOrCreate(Keys.bottomRectHeight, bottomRectHeight); }});
上滑加载中
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签