半小时实现GPT纯血鸿蒙版
仅需半小时,即可实现纯血鸿蒙版本的ChatGPT!
废话少说,先看效果图:

如上图所示,这个小Demo实现了AI智能问答。靠右加粗的文本是用户点击底部提交按钮后出现的;后面靠左对齐的普通文本是来自AI的回答内容。当然,整个内容是可滑动浏览的,当内容被滑动时,屏幕右侧将出现滚动条。最后,为什么UI是英文呢?因为鸿蒙的模拟器目前没有内置中文输入法,恰好这个AI服务也可以用英文来回答。
值得注意的是:这个小Demo之所以我称其为Demo,是因为它的功能实在是太简单了。只有一个基础的AI对话功能,如果要做成一个产品,我觉得起码得有个数据持久化的过程,而且还能支持文本的编辑、复制、删除,还要提供收藏功能。更重要的,UI也需要好好美化一下……
所以,这篇文章就权当抛砖引玉,让大家体会一下开发原生纯血鸿蒙版本的App是有多么轻松。
前置条件
- DevEco 3.1.1 Release;
- 在百度智能云控制台上创建好应用,保存好API Key和Secret Key。
创建项目(5分钟)
使用DevEco创建项目仅需两步,第一步选择类型,第二步填写项目信息。
对于第一步,我们选择Application(应用程序)->Empty Ability(空白Ability);
对于第二步,我们选择迄今为止最新的Compile SDK,即3.1.0(API 9),Model选择Stage,不开启“Enable Super Visual”。其余的内容大家根据自身环境配置进行填写就好。
编码实现(20分钟)
整个编码过程分为三个步骤,首先添加权限,然后实现UI,最后实现网络操作。
添加权限(2分钟)
在整个App的项目结构中,找到默认创建的entry模块,依次定位到src->main->module.json5,权限在该文件中进行配置。
这个Demo功能非常简单,但仍需对其添加必要的网络访问权限,以确保可以打开网络套接字,完成HTTP请求和响应。
代码片段如下:
"requestPermissions": [ { "name": "ohos.permission.INTERNET", } ],
UI实现(10分钟)
回过头来看本文最上方的截图,经过布局分析后,可以得到如下结论:整个界面是纵向布局,由两个部分构成。一是可滚动的对话历史;二是下方的输入框和提交按钮。
因此,整个UI布局最外层应该是一个Column,表示纵向布局。其中,占据90%高度的对话历史区域,占据10%高度的输入框和按钮区域。
先来看对话历史区域,它其实本质上也是一个Column,每个item就是一段文字。根据文字的类型来判断是居左还是居右。在这个Column之外,为了让整个对话历史区域可以上下滚动查看,因此还需要Scroll组件将整个Column组件包裹起来。
代码片段如下:
@State messagesList: Object[] = [{ 'role': 'user', 'content': 'What can I help you with?' }] // 历史问答 Scroll() { Column() { ForEach(this.messagesList, (item: Object) => { if (item['role'] == 'user') { Text(item['content']).fontSize(20).fontWeight(FontWeight.Bold).width('100%').textAlign(TextAlign.End) } else { Text(item['content']).fontSize(20).width('100%').textAlign(TextAlign.Start) } }) }.width('100%').alignItems(HorizontalAlign.Center) } .scrollable(ScrollDirection.Vertical) .scrollBar(BarState.Auto) .scrollBarColor(Color.Gray) .scrollBarWidth(10) .edgeEffect(EdgeEffect.Fade) .height('90%') .width('100%')
请注意这段代码中的messagesList,它是一个对象数组。role表示角色,即该条消息是用户发送的,还是服务器返回的;content表示文字内容。
在由Scroll包裹的Column组件之中,使用了ArkTS提供的ForEach渲染方式进行逐条消息的渲染,并使用if...else...条件判断语句对角色来源进行区分。
再来看底部的输入框和操作按钮,由于它们是横向排列的,所以使用Row组件进行布局。在此,我将文本输入框设定了80%的宽度,提交按钮设定了20%的宽度。
代码片段如下:
@State questionStr: string = '' // 文本输入和提交 Row() { TextInput({ placeholder: 'Please input your question', text: this.questionStr }) .type(InputType.Normal) .onChange((value: string) => { this.questionStr = value }) .width('80%') Button('提交').type(ButtonType.Capsule).onClick(() => { this.messagesList.push({ 'role': 'user', 'content': this.questionStr }) getAnswer(this.questionStr, this.messagesList) this.questionStr = '' }).width('20%') }.height('10%').width('100%')
在这段代码中,questionStr表示输入框中的文字字符串。getAnswer()函数发起并接收HTTP请求,向服务器提交用户问题字符串,并等待接收响应内容,将问题的回答放入messagesList对象数组之中,完成整个问答流程。
最后,将上述Scroll组件和Row组件一并放入Column内,完成整个UI绘制。
网络访问(8分钟)
根据百度官方的开发文档,完成整个AI问答过程至少需要两个步骤:获取access_token和获取问题答案。
获取access_token的过程在程序一开始就可以进行了,因为在后续的操作中都要用到access_token。因此,我声明了access_token的全局变量,并将获取该值的方法封装为getToken()函数,具体代码如下:
var access_token: string = '' function getToken() { let httpRequest = http.createHttp(); httpRequest.on('headersReceive', (header) => { console.info('header: ' + JSON.stringify(header)); }); httpRequest.request( "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=[你的应用的API Key]&client_secret=[你的应用的Secret Key]", { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json', }, expectDataType: http.HttpDataType.OBJECT, usingCache: true, priority: 1, connectTimeout: 60000, readTimeout: 60000, usingProtocol: http.HttpProtocol.HTTP1_1, }, (err, data) => { if (!err) { access_token = data.result['access_token'] } else { httpRequest.off('headersReceive') httpRequest.destroy() } }); }
getToken()函数我在回调的onPageShow()函数中使用,即程序启动后,就获取access_token。
最后,我们来实现getAnswer()函数,它是向服务器提交问题和接收响应的函数,具体代码如下:
function getAnswer(questionStr: string, messageList: Object[]) { let httpRequest = http.createHttp(); httpRequest.on('headersReceive', (header) => { console.info('header: ' + JSON.stringify(header)); }); httpRequest.request( "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/yi_34b_chat?access_token=" + access_token, { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json' }, extraData: { "messages": [{ "role": "user", "content": questionStr }] }, expectDataType: http.HttpDataType.OBJECT, usingCache: true, priority: 1, connectTimeout: 60000, readTimeout: 60000, usingProtocol: http.HttpProtocol.HTTP1_1, }, (err, data) => { if (!err) { messageList.push({ 'role': 'assist', 'content': data.result['result'] }) } else { httpRequest.off('headersReceive') httpRequest.destroy() } } ); }
还需要做些别的吗?
答案是:没有了,真的不用了。
运行项目(5分钟)
无论是本地模拟器,还是真机,抑或是远程模拟器,只需要启动其中一个,然后让这个程序跑起来吧!如无意外,你将会得到一个超级简易的AI问答机器人,纯血鸿蒙版。
当然,这里我写了需要5分钟,是包括了下载模拟器镜像的时间,如果你有真机或是使用远程模拟器的话,那就会更快了。
最后,我们来对比一下。我完成上述功能,Index.ets一共117行,你的呢?
非常荣幸您能阅读到最后,希望文章中的内容能够帮助到您。
本博客内容均为博主原创,未经授权,请勿转载,谢谢!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?