最新案例动态,请查阅基于华为开发者空间鸿蒙云手机+MaaS的鸿蒙原生智能应用开发 - 翻译助手APP。小伙伴们快来进行实操吧!
一、概述
1.1 案例介绍
鸿蒙云手机是基于华为鸿蒙操作系统(HarmonyOS)的云端虚拟化手机服务,通过云计算技术将手机功能迁移至云端,为用户提供跨终端、高性能的移动体验。
MaaS(MaaS 模型即服务)是华为云面向 AI 开发者推出的一站式大模型开发平台,支持开发者一键体验大模型能力,快速构建大模型应用。Mass 平台提供大模型训练、推理、部署、管理、监控等全生命周期管理能力,帮助开发者快速构建大模型应用,加速 AI 开发。
DevEco Studio 是 HarmonyOS 应用及服务的集成开发环境(IDE),提供了一站式的开发平台,包括代码编辑、编译构建、代码调试、性能调优、模拟器、应用测试等能力。
DevEco Testing提供一站式的应用测试服务平台。为开发者提供稳定性、性能、应用基础质量等专项测试服务,覆盖应用测试全周期,助力打造高品质应用。
本案例通过鸿蒙应用开发工具 DevEco Studio、测试工具 DevEco Testing 远程连接鸿蒙云手机,集成华为云 MaaS,开发调试鸿蒙原生应用-翻译助手 APP。
1.2 适用对象
1.3 案例时间
本案例总时长预计 90 分钟。
1.4 案例流程
说明:
开发者下载开发工具 DevEco Studio 和测试工具 DevEco Testing;
领取华为云 MaaS 平台大模型 Tokens 福利;
用户登录开发者空间,创建并远程连接鸿蒙云手机;
使用鸿蒙云手机调试运行翻译助手 APP 代码。
1.5 资源总览
本案例预计花费 0 或 1 元。
二、基础环境与资源准备
2.1 获取 AK/SK 及工具下载
登录华为开发者空间,参考案例《华为开发者空间-鸿蒙云手机操作指导手册(Windows)》中的“二、环境及资源准备”章节内容,获取 AK/SK、下载开发工具(DevEco Studio)与测试工具(DevEco Testing)。
2.2 领取华为云 MaaS 平台大模型 Tokens 福利(任选其一)
方式一: 登录华为开发者空间,参考案例《华为开发者空间 - ModelArts Studio大模型通用代金券领取使用指导》中的“二、 开通 MaaS 平台大模型”章节内容领取代金券,获取到模型的 API 地址、模型名称和 API Key。
方式二: 登录华为开发者空间,参考案例《华为云MaaS平台大模型Tokens领取使用指导》中的“二、 领取 MaaS 平台大模型 Tokens”章节内容,领取 MaaS 平台 DeepSeek V3 系列大模型 Tokens 代金券,购买 ModelArts Studio DeepSeek Tokens 套餐包,开通模型服务,最后获取到模型的 API 地址、模型名称和 API Key。
注意:记录 API Key、API 地址以及模型名称留作后面步骤使用。
三、远程连接鸿蒙云手机
注意:鸿蒙云手机目前处于公测中,按照以下步骤参与公测,在线开发与调试鸿蒙应用。
登录华为开发者空间,点击鸿蒙云手机,选择职业,输入使用的业务场景,参与公测。
公测申请将在 1~3 个工作日内审核完成。
审核完成后,即可创建和使用鸿蒙云手机。
参考案例《华为开发者空间-鸿蒙云手机操作指导手册(Windows)》中的“三、鸿蒙云手机操作指导”的内容,完成“1. 创建鸿蒙云手机 ~ 4. DevEco Studio 连接云手机”章节步骤。
四、翻译助手 APP 代码实践
4.1 项目介绍
本项目集成 AI 能力,提供主流语言(包括汉语、英语、法语、俄语、西班牙语、阿拉伯语、德语)互译能力,输入需要翻译的语言,点击翻译按钮,即可将输入的语言文字翻译成目标语言。
4.2 项目结构
TranslationAssistant:项目名称
AppScope > app.json5:应用的全局配置信息。
entry:HarmonyOS 工程模块,编译构建生成一个 HAP 包。
src > main > ets:用于存放 ArkTS 源码。
bean:
MessageBean.ets、ModelMessageBean.ets、ModelResultBean.ets:华为云 MaaS API 返回数据封装。
entryability:
EntryAbility.ets 应用/服务的入口。
entrybackupability:
EntryBackupAbility.ets:应用提供扩展的备份恢复能力。
model:
IndexViewModel.ets:返回数据 model 层封装,解析并获取模型数据。
ImagesViewModel.ets:首页轮播图片数据获取。
pages:
Index.ets:应用主界面。
utils:
HttpHelper.ets:网络请求工具类。
Logger.ets:日志打印工具类。
ModelConstants.ets:常量配置工具类。
view:
UploadingLayout.ets:自定义网络加载组件。
src > main > resources:用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。
src > main > module.json5:模块配置文件。
build-profile.json5:当前的模块信息 、编译信息配置项,包括 buildOption、targets 配置等。
hvigorfile.ts:模块级编译构建任务脚本。
obfuscation-rules.txt:混淆规则文件。混淆开启后,在使用 Release 模式进行编译时,会对代码进行编译、混淆及压缩处理,保护代码资产。
oh-package.json5:用来描述包名、版本、入口文件(类型声明文件)和依赖项等信息。
oh_modules:用于存放三方库依赖信息。
build-profile.json5:工程级配置信息,包括签名 signingConfigs、产品配置 products 等。其中 products 中可配置当前运行环境,默认为 HarmonyOS。
hvigorfile.ts:工程级编译构建任务脚本。
oh-package.json5:主要用来描述全局配置。
4.3 核心代码解析
4.3.1 应用主界面
pages/Index.ets:翻译助手的主界面,用户在此界面选择需要翻译的语种,输入语言文字,点击翻译按钮,即可将对应的语言翻译成目标语言并提供复制功能。
Index.ets 完整代码如下:
import mainViewModel from '../model/ImagesViewModel';import pasteboard from '@ohos.pasteboard';import promptAction from '@ohos.promptAction';import UploadingLayout from '../view/UploadingLayout';import Logger from '../utils/Logger';import DetailViewModel from '../model/IndexViewModel';
@Entry@Componentstruct Index { private languageArray: Array<string> = ['汉语', '英语', '法语', '俄语', '西班牙语', '阿拉伯语', '德语']; private swiperController: SwiperController = new SwiperController(); @State translationResult: string = ''; @State fromLanguage: string = '汉语'; @State toLanguage: string = '英语'; @State isUploading: boolean = false; @State select: number = 0; @State prompt: string = "" content: string = ""; textImage?: Resource; title?: ResourceStr; onFromItemClick = (): void => { this.getUIContext().showTextPickerDialog({ range: this.languageArray, selected: this.select, canLoop: false, onAccept: (value: TextPickerResult) => { this.select = value.index as number; this.fromLanguage = value.value as string; }, onChange: (value: TextPickerResult) => { this.select = value.index as number; } }) }; onToItemClick = (): void => { this.getUIContext().showTextPickerDialog({ range: this.languageArray, selected: this.select, canLoop: false, onAccept: (value: TextPickerResult) => { this.select = value.index as number; this.toLanguage = value.value as string; }, onChange: (value: TextPickerResult) => { this.select = value.index as number; } }) };
build() { Stack() { Column() { Swiper(this.swiperController) { ForEach(mainViewModel.getSwiperImages(), (img: Resource) => { Image(img) .height('200vp') .width('100%') }, (img: Resource) => JSON.stringify(img.id)) } .autoPlay(true)
Row() { Row() { Text(this.fromLanguage) .fontSize(17) .layoutWeight(0.85) .margin({ left: 10 }) .height('100%')
Row() { Image($r('app.media.icon_down')) .width(13) .height(13) } .layoutWeight(0.15) } .layoutWeight(1) .border({ width: 0.8, color: Color.Gray, style: BorderStyle.Solid }) .borderRadius(10) .backgroundColor(Color.White) .onClick(this.onFromItemClick)
Image($r('app.media.icon_trans')) .width(20) .height(20) .margin({ left: 5, right: 5 })
Row() { Text(this.toLanguage) .fontSize(17) .margin({ left: 16 }) .layoutWeight(0.85) .height('100%')
Row() { Image($r('app.media.icon_down')) .width(13) .height(13) } .layoutWeight(0.15) } .layoutWeight(1) .borderRadius(10) .border({ width: 0.8, color: Color.Gray, style: BorderStyle.Solid }) .backgroundColor(Color.White) .onClick(this.onToItemClick)
}.height(50).margin({ left: 5, right: 5, top: 10 })
Stack({ alignContent: Alignment.BottomEnd }) { TextArea({ placeholder: '请输入文字' }) .border({ width: 0.8, color: Color.Gray, style: BorderStyle.Solid }) .placeholderFont({ size: "16fp" }) .width('98%') .fontColor("#182431") .height("150vp") .borderRadius(10) .fontSize("16fp") .margin({ top: "10vp" }) .backgroundColor(Color.White) .onChange((value: string) => { this.content = value; })
Button('翻译') .width('20%') .height('35vp') .margin({ bottom: "10vp", right: "10vp" }) .fontSize('18fp') .onClick(() => { this.translateLanguage();
}) .fontWeight(FontWeight.Medium) .backgroundColor('#007DFF') }
Column() { Stack({ alignContent: Alignment.BottomEnd }) { Text(this.translationResult) .fontColor("#182431") .align(Alignment.Top) .padding('10vp') .width('98%') .height("280vp") .borderRadius(10) .fontSize("16fp") .border({ width: 0.8, color: Color.Gray, style: BorderStyle.Solid }) .margin({ top: "10vp" }) .backgroundColor(Color.White)
Button('复制') .width('20%') .height('35vp') .margin({ bottom: "10vp", right: "10vp" }) .fontSize('18fp') .fontWeight(FontWeight.Medium) .onClick(() => { this.copyText(); }) .backgroundColor('#007DFF')
} } } .backgroundColor('#F1F3F5') .width("100%") .height("100%")
if (this.isUploading) { UploadingLayout() } } }
/** * 复制文本到剪贴板的方法 */ async copyText() { if (!this.translationResult) { promptAction.showToast({ message: '内容为空' }); return; }
try { let systemPasteboard = pasteboard.getSystemPasteboard(); await systemPasteboard.setData(pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.translationResult)); // 提示用户 promptAction.showToast({ message: '复制成功', duration: 1000 }); } catch (err) { console.error('Copy failed:', JSON.stringify(err)); promptAction.showToast({ message: '复制失败' }); } }
async translateLanguage() { if (!this.content) { promptAction.showToast({ message: '请输入文字' }); return; } this.prompt = `您好,请将下面这句话:${this.content},从${this.fromLanguage}翻译成${this.toLanguage}`; Logger.info(`prompt: ${this.prompt}`); this.isUploading = true DetailViewModel.requestModelData(this.prompt).then((content) => { Logger.info(`content: ${content}`); this.isUploading = false if (!content) { promptAction.showToast({ message: '请求失败' }); return; } this.translationResult = content }) }}
复制代码
4.3.2 添加网络权限
鸿蒙系统提供一种通用权限访问方式,允许应用访问系统资源(如通讯录)和系统能力(如摄像头、麦克风),以保护系统数据(包括用户个人数据)和功能,防止不当或恶意使用。访问网络需要在配置文件声明权限。
在 entry->src->main->module.json5 文件中声明网络权限:
"requestPermissions": [ { "name": "ohos.permission.INTERNET" } ],
复制代码
4.3.3 模型数据获取
封装网络请求工具类 HttpHelper,用于获取模型返回的数据。
HttpHelper.ets:
HttpHelper.ets 完整代码如下:
import { http } from '@kit.NetworkKit';import { ModelResultBean } from '../bean/ModelResultBean';import Logger from './Logger';import { ModelConstants } from './ModelConstants';
class HttpHelper { async requestData(prompt: string): Promise<string> { // 每一个httpRequest对应一个HTTP请求任务,不可复用 let httpRequest = http.createHttp(); // 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息 httpRequest.on('headersReceive', (header) => { console.info('header: ' + JSON.stringify(header)); });
let options: http.HttpRequestOptions = { method: http.RequestMethod.POST, // 开发者根据自身业务需要添加header字段 header: { 'Content-Type': 'application/json', // 把yourApiKey替换成真实的API Key "Authorization": `${ModelConstants.API_KEY}` }, // 当使用POST请求时此字段用于传递请求体内容,具体格式与服务端协商确定 extraData: { "model": `${ModelConstants.MODEL_NAME}`, "messages": [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": prompt } ], "stream": false, "temperature": 0.6 }, // 可选,指定返回数据的类型 expectDataType: http.HttpDataType.STRING, // 可选,默认为true usingCache: true, // 可选,默认为1 priority: 1, // 可选,默认为60000ms connectTimeout: 60000, // 可选,默认为60000ms readTimeout: 60000, // 可选,协议类型默认值由系统自动指定 usingProtocol: http.HttpProtocol.HTTP1_1, } let messageResult: string = '' try { let httpResponse = await httpRequest.request(ModelConstants.API_URL, options) let responseCode = httpResponse.responseCode let responseResult = httpResponse.result as string Logger.info(`responseResult: ${responseResult}`); Logger.info(`responseCode: ${responseCode}`); if (responseCode == 200) { let modelResultBean = JSON.parse(responseResult) as ModelResultBean messageResult = JSON.stringify(modelResultBean.choices) Logger.info(`messageResult: ${messageResult}`); } else { messageResult = "" } } catch (err) { Logger.info(`messageResult: ${JSON.stringify(err)}`); messageResult = "" } httpRequest.off('headersReceive'); // 当该请求使用完毕时,调用destroy方法主动销毁 httpRequest.destroy(); return messageResult }}
export default new HttpHelper();
复制代码
注意:替换 ModelConstants.ets 文件中的常量 MODEL_NAME、API_URL、API_KEY。
ModelConstants.ets:
export class ModelConstants { static readonly API_URL: string = 'YOUR_API_URL'; static readonly MODEL_NAME: string = 'YOUR_MODEL_NAME'; static readonly API_KEY: string = 'Bearer YOUR_API_KEY';}
复制代码
YOUR_API_URL:替换成步骤“2.2 领取华为云 MaaS 平台大模型 Tokens 福利”中获取的 API 地址。
YOUR_MODEL_NAME:替换成步骤“2.2 领取华为云 MaaS 平台大模型 Tokens 福利”中获取的模型名称。
YOUR_API_KEY:替换成步骤“2.2 领取华为云 MaaS 平台大模型 Tokens 福利”中获取的 API Key。
4.4 调试运行代码
源码下载:TranslationAssistant,下载解压完成后,使用 DevEco Studio 打开项目源码:
修改项目级目录下 build-profile.json5 文件中 sdk 编译版本。因为鸿蒙云手机是鸿蒙 5.0,所以需要修改代码编译 sdk 版本,compatibleSdkVersion 修改为:5.0.5(17)
替换 src/main/ets/utils/ModelConstants.ets 文件中的常量。
YOUR_API_URL:替换成步骤“2.2 领取华为云 MaaS 平台大模型 Tokens 福利”中获取的 API 地址。
YOUR_MODEL_NAME:替换成步骤“2.2 领取华为云 MaaS 平台大模型 Tokens 福利”中获取的模型名称。
YOUR_API_KEY:替换成步骤“2.2 领取华为云 MaaS 平台大模型 Tokens 福利”中获取的 API Key。
DevEco Studio 编译器连接云手机后,点击右上角运行按钮,运行项目代码:
打开 DevEco Testing,选择【标准】模式,翻译助手 APP 代码已经运行在鸿蒙云手机上了。
汉语翻译成英语:
汉语翻译成俄语:
至此,基于华为开发者空间鸿蒙云手机+MaaS 的鸿蒙原生智能应用开发-翻译助手 APP 的案例已全部完成。
五、反馈改进建议
如您在案例实操过程中遇到问题或有改进建议,可以到论坛帖评论区反馈即可,我们会及时响应处理,谢谢!
评论