鸿蒙端云一体化开发(一):云服务开发前置与云函数
鸿蒙端云一体化开发:快速构建应用后端服务 华为Cloud Foundation Kit为鸿蒙应用开发者提供了一套完整的云端服务解决方案,包括云函数、云数据库、云存储等功能,显著降低后端开发门槛。主要优势包括: 低运维成本:无需自行搭建服务器,直接调用API即可使用云端资源 弹性伸缩:自动应对流量高峰,按实际使用量计费 安全可靠:支持数据全密态加密和多重认证机制 开发便捷:在DevEco Studi
今天咱们来聊聊鸿蒙应用开发中一个非常实用的功能——端云一体化开发。如果你正在开发鸿蒙应用,可能会遇到这样的场景:应用需要一些后端服务,比如数据存储、用户认证、文件上传下载等等。传统做法是自己搭建服务器、配置环境、维护数据库,这些工作既费时又费力。
华为的Cloud Foundation Kit(云开发服务)就是为了解决这个问题而生的。它提供了云函数、云数据库、云存储、预加载等一整套云端服务,你只需要关注业务逻辑,服务器、操作系统、容器这些基础设施都由云端平台搞定。而且,在DevEco Studio中还提供了端云一体化开发的体验,前端开发人员可以轻松转变为全栈工程师,大大提高开发效率。
一、云开发服务能帮你做什么?
1.1 主要优势
低运维成本:不用自己构建和管理云端资源,Cloud Foundation Kit提供了函数计算、数据库、存储、预加载等一系列能力,你只需要调用相应的API就能使用。
弹性伸缩、按量计费:这个特点特别实用。比如你的应用在节假日访问量激增,云服务会自动扩容应对高峰;访问量下降后又自动缩容,释放闲置资源。你只需要为实际使用的资源付费,不用为空闲资源买单。
安全可靠:支持数据全密态加密,支持APP、用户和服务三重认证,还有基于角色的权限管理机制,全方位保障数据安全。
端云一体化开发:在DevEco Studio中可以同时开发端侧和云侧代码,调试、编译、部署都能在一个IDE里完成,非常方便。

1.2 典型应用场景
应用后端构建:快速构建应用的后端服务,简化开发和运维工作。
计算密集型任务:比如数据渲染、视频图像处理等任务,云函数可以自动分配足够的算力,任务完成后自动释放资源。还可以配置定时触发器,定时执行任务。
协议适配和转换:用于对接第三方平台,比如身份验证、消息队列、推送通知等功能。
浪涌式访问场景:像秒杀、节日活动这种访问量突然暴增的场景,云服务能快速自动扩容,保证系统稳定运行。
1.3 支持的设备和地区
云函数支持Phone、Tablet、Wearable、TV设备;云数据库、云存储支持Phone、Tablet、Wearable、TV设备;预加载支持Phone、Tablet设备。
支持的签名方式包括关联注册应用进行自动签名(DevEco Studio 6.0.0 Beta5及以上版本)和手动签名两种方式。从6.0.0(20) Beta5版本开始,支持模拟器开发。
二、准备工作
在开始开发之前,需要先完成以下的准备工作。
2.1 基本准备工作
按照"官网应用开发准备"完成基本准备工作,配置签名信息时,使用关联注册应用进行自动签名或者手动签名方式。
2.2 开通云服务
首次使用端云一体化开发前,需要开通云函数服务、云数据库服务、云存储服务、预加载服务。这些服务都是在AppGallery Connect 控制台中开通的。
三、云函数开发实战
云函数是Cloud Foundation Kit的核心功能之一,它允许你在云端运行代码,无需管理服务器。咱们通过一个实际案例来学习如何开发和使用云函数。
3.1 开发场景
假设你正在开发一个电商应用,需要一个功能:用户提交订单后,系统需要计算订单金额、生成订单号、发送确认邮件。这些操作如果放在端侧执行,可能会因为网络问题或者用户关闭应用而中断。如果放在云函数中执行,就能保证这些操作可靠地完成。
3.2 云函数代码结构
云函数使用Node.js开发,入口方法的定义如下:
module.exports.myHandler = function(event, context, callback, logger)
这里有几个关键参数:
- myHandler:入口方法名称,你可以自定义
- event:调用方传递的事件对象,JSON格式
- context:函数运行时上下文对象,封装了日志接口、回调接口、环境变量等
- callback:事件处理结果,必须显式调用callback(object)将结果返回给AGC
- logger:记录日志,支持debug、error、warn、info四个级别
3.3 完整示例代码
下面是一个完整的云函数示例,展示了如何使用日志、环境变量、异常处理等功能:
let myHandler = function (event, context, callback, logger) {
// 获取环境变量
let env1 = context.env.env1;
// 记录不同级别的日志
logger.info("Test info log");
logger.warn("Test warn log");
logger.debug("Test debug log");
logger.error("Test error log");
logger.info("--------Start-------");
try {
let startTime = new Date().getTime();
let endTime = startTime;
let interval = 0;
startTime = process.uptime() * 1000;
// 打印输入参数和环境变量
logger.info("request: " + JSON.stringify(event.request));
logger.info("env1: " + env1);
endTime = process.uptime() * 1000;
interval = endTime - startTime;
logger.info("intervalTime: " + interval);
logger.info("--------Finished-------");
let res = new context.HTTPResponse(context.env, {
"res-type": "context.env",
"faas-content-type": "json"
}, "application/json", "200");
res.body = { "intervalTime": interval };
callback(res);
} catch (error) {
logger.error("--------Error-------");
logger.error("error: " + error);
callback(error);
}
};
module.exports.myHandler = myHandler;
3.4 日志记录
在代码中使用logger接口记录日志,支持四种级别:
- logger.debug():调试信息
- logger.error():错误信息
- logger.warn():警告信息
- logger.info():普通信息
3.5 获取环境变量
环境变量可以在函数运行时读取和使用,访问方式如下:
let env1 = context.env.env1;
如果环境变量未配置,则会返回undefined。
3.6 异常处理
在函数代码中捕获异常,封装成error对象返回给调用方:
try {
logger.info(JSON.stringify(event));
let result = { message: "success" };
callback(result);
} catch (err) {
let error = {
code: 400,
message: err.message
};
callback(error);
}
error对象包含code(错误码)和message(错误描述)两个字段。
3.7 函数部署包结构
上传的nodejs函数部署包须使用如下结构:
my-function.zip
|---- handler.js
|---- node_modules
|----async
|----async-listener
处理程序所在代码文件(如handler.js)必须在zip包根目录下,依赖项放到node_modules目录下。可以通过npm工具安装依赖,例如npm install xxx命令。
四、在AGC中创建云函数
开通云函数服务后,需要在AGC中创建函数并添加代码。
4.1 创建步骤
- 登录AppGallery Connect,点击"开发与服务"
- 在项目列表中点击需要创建云函数的项目
- 在左侧导航栏选择"云开发 > 云函数",进入云函数主界面
- 选择"函数"页签,点击"创建函数"

4.2 函数配置
页面右侧抽屉式滑出"创建函数"窗口,按照"函数配置 -> 触发器 -> 函数代码 -> 层配置"引导顺序配置函数。

在"函数配置"页面,配置以下信息:
- 函数名称:函数的名称
- 描述:函数的描述信息
- 触发方式:配置为"事件调用",表示通过触发器方式调用函数
- 超时时长:函数最大运行时长,单位为秒,取值范围为1~1800
- 同步调用方式时,最大运行时长为55秒
- 异步调用方式时,最大运行时长为1800秒
- 实例并发:函数请求并发量上限,单位为个,取值范围为1~10000
- 环境变量:key-value形式,可以将需要的变量配置信息传入函数执行环境中
环境变量支持表单格式和JSON格式两种编辑方式。
表单格式编辑:

JSON格式编辑:
添加完成后,还可以点击"JSON格式导出",导出环境变量文件以备后续使用。
注意事项:
- 环境变量的key值具有唯一性
- "PROJECT_CREDENTIAL"和"AGC_"为系统级环境变量标识,不允许添加以其命名或以其为前缀的环境变量
- 环境变量总数不超过1000个
4.3 触发器配置
进入"触发器"页面,配置HTTP触发器:

- 触发器类型:HTTP触发器
- 请求方式:目前仅支持POST请求方式
- 认证类型:
- API客户端鉴权(Client适用):端侧网关认证,适用于来自APP客户端侧的函数调用
- API客户端鉴权(Server适用):云侧网关认证,适用于来自APP服务器侧的函数调用
- 启用decode:对于contentType为"application/x-www-form-urlencoded"的触发请求,是否使用URLDecoder对请求body进行解码
4.4 函数代码配置
进入"函数代码"页面,配置以下信息:
- 运行环境:选择nodejs 20.x/latest,其中latest表示使用最新版本
- 内存配置:函数容器所占有的内存大小,单位为MB,取值范围:500,1000,2000,4000
- 代码输入类型:包括"在线编辑"与"*.zip文件"两种方式
- 函数入口:包括入口文件名称和入口方法名称,通过"."连接。例如handler.myHandler
- 代码文件:用于在线编辑函数代码或上传函数部署包
WebIDE功能
当"代码输入类型"选择"在线编辑"时,可以使用集成的WebIDE功能。WebIDE从左至右分两个部分:目录树、代码编辑器和最大化。
目录树功能:

注意:如果在函数实例已经运行的情况下进行函数代码或配置更新,AGC后台会滚动更新函数实例,请耐心等待10-20秒。
4.5 层配置(可选)
层可以提供公共依赖库的发布与部署能力。开发者可以将函数依赖的公共库和相关依赖项提炼到层,通过为函数绑定层,便可以在函数中使用库,而不必将库包含在函数的代码包中。

一个函数最多可以绑定5个层。如果尚未创建层,可以跳过此步骤,后续创建层之后再进行绑定。

返回到“层配置”界面,绑定成功的层将展示在层列表中。如果需要解除层与函数的绑定关系,点击“解绑”即可。

五、测试云函数
函数创建后可以在AGC控制台测试函数的代码运行是否正常。
5.1 进入测试界面
有两种方式进入测试界面:
方式一:在函数列表中点击函数名称右侧"操作"列的"测试"

方式二:在函数列表中点击已创建的函数名称,进入函数详情页面,选择"函数代码"页签,点击"测试函数"

5.2 测试方式
使用默认测试事件
直接点击"测试"对函数进行测试。

创建新测试事件
如果需要设置调用函数的请求消息体,可以按照如下步骤配置测试参数:
- 在"事件"文本框中输入JSON格式的事件参数
- 点击"保存"
- 在弹出的提示框中输入事件名称
- 点击"确认"保存测试事件


"事件"文本框内输入的JSON对象,对应的是触发器的event事件格式,会透传给函数。
使用已保存测试事件
在"测试函数"界面,点击展开已保存的测试事件列表,选择已配置的事件名称右侧的"加载",然后点击"测试"。
5.3 查看测试结果
测试结果包含三部分:
- 执行结果:展示测试后获得的响应结果
- 运行日志:展示函数运行过程中,通过logger API打印的日志
- 执行摘要:展示该次测试请求相关信息
- 请求ID:该条测试请求的RequestID
- 持续时间:函数执行的端到端时间
- 执行版本:该次调用测试的具体函数版本

5.4 修改函数代码
"代码输入类型"为"在线编辑"的函数:
可以直接在"函数代码"页签的代码编辑器中修改代码,然后点击页面底部的"提交"。更新成功后可以点击"测试函数"对更改后的代码进行测试。

“代码输入类型"为”.zip文件"的函数:
需要在本地修改且打包完成后,点击重新上传函数部署包,然后点击页面底部的"提交"。更新成功后可以点击"测试函数"对更改后的代码进行测试。
如果代码更新量比较大,需要调整函数内存配置,可点击"内存配置"下拉框进行调整,然后再上传函数部署包。
函数测试无误后,可在"函数代码"页签点击"导出函数"导出函数部署包。导出包以"函数名称+函数版本.zip"格式命名。

六、在应用中调用云函数
6.1 设置网络权限
在"entry/src/main/module.json5"文件中添加网络权限:
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
6.2 查询函数名和版本
在函数的触发器页面点击"HTTP触发器",查看"触发URL"的后缀,获取触发器的标识,格式为"函数名-版本号"。例如"myhandlerxxxx- l a t e s t " ,其中 " m y h a n d l e r x x x x " 为函数名, " latest",其中"myhandlerxxxx"为函数名," latest",其中"myhandlerxxxx"为函数名,"latest"为版本号。
6.3 调用云函数
在项目中导入cloudFunction组件:
import { cloudFunction } from '@kit.CloudFoundationKit';
import { BusinessError } from '@kit.BasicServicesKit';
调用call()方法设置函数,在方法中传入函数名称,返回调用结果。
使用Promise异步回调
import { hilog } from '@kit.PerformanceAnalysisKit';
import { cloudFunction } from '@kit.CloudFoundationKit';
import { BusinessError } from '@kit.BasicServicesKit';
function callFunction() {
cloudFunction.call({
name: 'functionName', // functionName需替换为实际的函数名
version: '$latest', // 如果不传入版本号,默认为"$latest"
timeout: 10 * 1000, // 单位为毫秒,默认为70*1000毫秒
data: { // data为函数请求体
param1: 'val1',
param2: 'val2'
}
}).then((value: cloudFunction.FunctionResult) => {
hilog.info(0x0000, 'testTag', `Succeeded in calling the function, result: ${JSON.stringify(value.result)}`);
}).catch((err: BusinessError) => {
hilog.error(0x0000, 'testTag', `Failed to call the function, code: ${err.code}, message: ${err.message}`);
})
}
使用callback异步回调
import { hilog } from '@kit.PerformanceAnalysisKit';
import { cloudFunction } from '@kit.CloudFoundationKit';
import { BusinessError } from '@kit.BasicServicesKit';
function callFunction() {
cloudFunction.call({
name: 'functionName', // functionName需替换成实际的函数名
version: '$latest', // 如果不传入版本号,默认为"$latest"
timeout: 10 * 1000, // 单位为毫秒,默认为70*1000毫秒
data: { // data为函数请求体
param1: 'val1',
param2: 'val2'
}
}, (err: BusinessError, value: cloudFunction.FunctionResult) => {
if (err) {
hilog.error(0x0000, 'testTag', `Failed to call the function, code: ${err.code}, message: ${err.message}`);
return;
}
hilog.info(0x0000, 'testTag', `Succeeded in calling the function, result: ${JSON.stringify(value.result)}`);
})
}
6.4 获取返回值
如果需要关注函数的返回值,可调用result属性获取:
let returnValue = value.result;
value为调用call()方法返回的cloudFunction.FunctionResult对象,返回值为云函数body返回的值。
七、本地调试云函数
从6.0.1(21)版本开始,新增支持调用本地云函数功能。这个功能对于开发调试非常有用,可以不用每次修改代码都上传到云端。
7.1 启动本地云函数
- 创建端云一体化开发工程:选择合适的云开发模板,根据工程向导创建端云一体化开发工程
- 开发云函数:使用DevEco Studio在端云一体化云侧工程下创建函数、开发函数、调试函数(通过本地调用方式调试函数)
- 调试函数过程中,如果下方通知栏的"cloudfunctions"窗口显示"Cloud Functions loaded successfully",则表示本地云函数启动成功,将生成本地函数的Function URI。请记录下该Function URI的域名和端口信息,例如"http://localhost:18090"
重要提示:由于本地云函数和部署至云端的函数获取请求体的方式不同,开发函数时必须按照如下示例获取请求体:
let body = event.body ? JSON.parse(event.body) : event;
7.2 设置端口映射
调用本地云函数之前,需要设置"设备端口"到"主机端口"的映射:
hdc rport tcp:18090 tcp:18090
其中,设备端口和主机端口即为Function URI中的端口。
7.3 调用本地云函数
使用Promise异步回调
function callFunctionLocal() {
cloudFunction.call({
name: 'my-cloud-function', // my-cloud-function需替换为实际的函数名
version: '$latest', // 如果不传入版本号,默认为"$latest"
timeout: 10 * 1000, // 单位为毫秒,默认为70*1000毫秒
data: { // data为函数请求体
param1: 'val1',
param2: 'val2'
},
localUrl: 'http://localhost:18090' // 本地启动的云函数地址
}).then((value: cloudFunction.FunctionResult) => {
hilog.info(0x0000, 'testTag', `Succeeded in calling the function, result: ${JSON.stringify(value.result)}`);
}).catch((err: BusinessError) => {
hilog.error(0x0000, 'testTag', `Failed to call the function, code: ${err.code}, message: ${err.message}`);
})
}
使用callback异步回调
function callFunctionLocal() {
cloudFunction.call({
name: 'my-cloud-function', // my-cloud-function需替换为实际的函数名
version: '$latest', // 如果不传入版本号,默认为"$latest"
timeout: 10 * 1000, // 单位为毫秒,默认为70*1000毫秒
data: { // data为函数请求体
param1: 'val1',
param2: 'val2'
},
localUrl: 'http://localhost:18090' // 本地启动的云函数地址
}, (err: BusinessError, value: cloudFunction.FunctionResult) => {
if (err) {
hilog.error(0x0000, 'testTag', `Failed to call the function, code: ${err.code}, message: ${err.message}`);
return;
}
hilog.info(0x0000, 'testTag', `Succeeded in calling the function, result: ${JSON.stringify(value.result)}`);
})
}
八、实际开发案例
8.1 场景描述
假设你正在开发一个社交应用,用户发布动态后,系统需要完成以下操作:
- 保存动态内容到数据库
- 提取动态中的关键词
- 根据关键词推荐相关用户
- 发送通知给相关用户
这些操作如果放在端侧执行,会占用用户设备的资源,而且可能因为网络问题导致操作失败。如果放在云函数中执行,就能保证这些操作可靠地完成,而且不占用用户设备资源。
8.2 云函数实现
let myHandler = function (event, context, callback, logger) {
logger.info("--------Start processing post-------");
try {
let body = event.body ? JSON.parse(event.body) : event;
let userId = body.userId;
let content = body.content;
logger.info("Processing post for user: " + userId);
logger.info("Post content: " + content);
// 1. 保存动态内容到数据库(这里只是示例,实际需要调用云数据库API)
let postId = savePostToDatabase(userId, content);
logger.info("Post saved with ID: " + postId);
// 2. 提取动态中的关键词
let keywords = extractKeywords(content);
logger.info("Extracted keywords: " + JSON.stringify(keywords));
// 3. 根据关键词推荐相关用户
let recommendedUsers = recommendUsers(keywords);
logger.info("Recommended users: " + JSON.stringify(recommendedUsers));
// 4. 发送通知给相关用户(这里只是示例,实际需要调用推送服务API)
sendNotifications(recommendedUsers, postId);
logger.info("Notifications sent");
let res = new context.HTTPResponse(context.env, {
"res-type": "context.env",
"faas-content-type": "json"
}, "application/json", "200");
res.body = {
postId: postId,
keywords: keywords,
recommendedUsers: recommendedUsers
};
callback(res);
} catch (error) {
logger.error("--------Error processing post-------");
logger.error("error: " + error);
let errorObj = {
code: 500,
message: error.message
};
callback(errorObj);
}
};
function savePostToDatabase(userId, content) {
// 这里实现保存到数据库的逻辑
// 返回动态ID
return "post_" + Date.now();
}
function extractKeywords(content) {
// 这里实现关键词提取的逻辑
// 返回关键词数组
return ["keyword1", "keyword2"];
}
function recommendUsers(keywords) {
// 这里实现用户推荐的逻辑
// 返回推荐用户列表
return ["user1", "user2", "user3"];
}
function sendNotifications(users, postId) {
// 这里实现发送通知的逻辑
logger.info("Sending notifications to users: " + JSON.stringify(users));
}
module.exports.myHandler = myHandler;
8.3 端侧调用
import { hilog } from '@kit.PerformanceAnalysisKit';
import { cloudFunction } from '@kit.CloudFoundationKit';
import { BusinessError } from '@kit.BasicServicesKit';
function publishPost() {
cloudFunction.call({
name: 'process-post',
version: '$latest',
timeout: 30 * 1000,
data: {
userId: 'user123',
content: '今天天气真好,适合出去游玩!'
}
}).then((value: cloudFunction.FunctionResult) => {
hilog.info(0x0000, 'testTag', `Post published successfully, result: ${JSON.stringify(value.result)}`);
}).catch((err: BusinessError) => {
hilog.error(0x0000, 'testTag', `Failed to publish post, code: ${err.code}, message: ${err.message}`);
})
}
九、注意事项
-
超时时长设置:根据实际业务需求设置合适的超时时长。同步调用最大55秒,异步调用最大1800秒。
-
实例并发配置:根据预期的并发量设置实例并发数,取值范围为1~10000。
-
内存配置:根据函数的内存需求选择合适的内存大小,取值范围:500,1000,2000,4000 MB。
-
环境变量管理:环境变量的key值具有唯一性,不要使用"PROJECT_CREDENTIAL"和"AGC_"作为前缀。
-
异常处理:在函数代码中一定要做好异常处理,避免因为未捕获的异常导致函数执行失败。
-
日志记录:合理使用不同级别的日志记录,方便调试和问题排查。
-
本地调试:开发时可以使用本地调试功能,提高开发效率。注意本地云函数和云端函数获取请求体的方式不同。
-
函数更新:如果在函数实例已经运行的情况下进行函数代码或配置更新,AGC后台会滚动更新函数实例,请耐心等待10-20秒。
十、总结
云函数是端云一体化开发的核心功能,它让你可以轻松地在云端运行代码,无需管理服务器。在实际开发中,你可以根据业务需求,灵活运用云函数来完成各种任务。
愣着干啥呢?还不麻溜的去上手试试!
更多推荐


所有评论(0)