HarmonyOS 提供了端侧 GNSS 围栏和云侧围栏两种方案,分别适配个性化围栏需求和公共围栏需求。本文将从方案选型、开发步骤到代码实战,带你搞定地理围栏开发,同时兼顾合规性与实用性。
一、地理围栏核心认知:两种方案怎么选?
在动手开发前,先明确两种地理围栏方案的核心区别,避免选不对场景走弯路。地理围栏本质是'虚拟地理边界',当设备进入、离开或驻留该边界时,系统会触发预设动作(如拉起 App、发送通知),而两种方案的差异集中在'围栏管理方式'和'适用场景':
HarmonyOS Location Kit 提供端侧 GNSS 围栏与云侧围栏两种方案。端侧适合自定义小范围圆形区域,依赖本地 GNSS;云侧利用公共围栏数据,适合商圈景区等大范围场景。涵盖权限申请、合规要求、WantAgent 配置及 FenceExtensionAbility 开发步骤,并总结常见问题解决方案,帮助开发者实现基于位置的智能提醒功能。
HarmonyOS 提供了端侧 GNSS 围栏和云侧围栏两种方案,分别适配个性化围栏需求和公共围栏需求。本文将从方案选型、开发步骤到代码实战,带你搞定地理围栏开发,同时兼顾合规性与实用性。
在动手开发前,先明确两种地理围栏方案的核心区别,避免选不对场景走弯路。地理围栏本质是'虚拟地理边界',当设备进入、离开或驻留该边界时,系统会触发预设动作(如拉起 App、发送通知),而两种方案的差异集中在'围栏管理方式'和'适用场景':
| 方案类型 | 核心特点 | 适用场景 | 依赖条件 | 围栏形状/管理 |
|---|
| 端侧 GNSS 围栏 | 开发者自主创建/管理,本地触发事件 | 企业考勤、个人家庭区域提醒、自定义小范围围栏 | 1. 设备支持 GNSS 芯片;2. 室外开阔环境(室内信号弱);3. 位置权限 + 用户授权 | 仅支持圆形围栏;开发者控制生命周期(创建/删除) |
| 云侧围栏 | 直接使用华为云侧公共围栏,云端触发 | 商圈推送、景区导览、城市级服务提醒 | 1. 设备联网;2. AGC 平台配置围栏策略;3. 位置权限 + 用户授权 | 支持多种公共围栏(商圈、景区等);华为云维护围栏数据 |
举个直观例子:如果想做'公司 300 米范围内打卡提醒',用端侧 GNSS 围栏自主定义圆形区域即可;如果想做'用户进入全国任意万达商圈时推送优惠',直接对接华为云侧的公共商圈围栏,用云侧围栏更高效(无需自己维护全国万达的位置数据)。
地理围栏依赖位置信息,且涉及用户敏感数据,权限申请和合规性是前提,缺一不可。
无论端侧还是云侧围栏,都需要申请以下位置权限(同定位功能权限,可复用相关权限配置逻辑):
ohos.permission.LOCATION:获取精准位置(保证围栏触发精度)ohos.permission.APPROXIMATELY_LOCATION:获取模糊位置(兜底方案)ohos.permission.LOCATION_IN_BACKGROUND(后台位置权限)权限声明示例(module.json5):
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "$string:fence_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:fence_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "$string:background_fence_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}
根据华为 Location Kit 的个人数据处理规范:
端侧 GNSS 围栏是最常用的方案,适合开发者自主定义小范围圆形围栏,事件触发在本地设备,响应速度快。核心流程是'创建 WantAgent→配置围栏参数→注册围栏监听'。
地理围栏触发后需要执行预设动作(如拉起 App、发送通知),而 WantAgent 就是 HarmonyOS 中'封装动作意图'的工具——它能把'拉起某 Ability''发送公共事件'等动作包装成对象,围栏触发时系统会自动执行该动作。
需要导入地理围栏、WantAgent 和错误处理相关模块:
import { geoLocationManager } from '@kit.LocationKit';
import { wantAgent } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
根据业务需求选择动作类型,常用两种场景:
/**
* 创建拉起 Ability 的 WantAgent
* 围栏触发时,自动打开 EntryAbility
*/
function createStartAbilityWantAgent() {
// 配置 WantAgent 信息:指定要拉起的 Ability
const wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
deviceId: '', // 空表示当前设备
bundleName: 'com.example.myapplication', // 你的 App 包名
abilityName: 'EntryAbility', // 要拉起的 Ability 名称
parameters: { "fenceAction": "checkIn" } // 传递额外参数(如'打卡'动作)
}
],
operationType: wantAgent.OperationType.START_ABILITY, // 动作类型:拉起 Ability
requestCode: 0, // 请求码(自定义,用于区分不同 WantAgent)
wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG] // 常量标志(确保动作可重复触发)
};
// 创建 WantAgent 对象
wantAgent.getWantAgent(wantAgentInfo, (err: BusinessError, data: wantAgent.WantAgent) => {
if (err) {
console.error(`创建 WantAgent 失败:errCode=${err.code}, errMsg=${err.message}`);
return;
}
console.info("创建拉起 Ability 的 WantAgent 成功");
// 下一步:注册地理围栏(传入该 WantAgent)
registerGnssGeofence(data);
});
}
/**
* 创建发送公共事件的 WantAgent
* 围栏触发时,发送自定义事件,App 后台监听处理
*/
function createSendEventWantAgent() {
const wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
action: 'com.example.geofence.CHECK_IN_EVENT', // 自定义事件名(需唯一)
parameters: { "fenceId": "companyFence" } // 传递围栏 ID 等参数
}
],
operationType: wantAgent.OperationType.SEND_COMMON_EVENT, // 动作类型:发送公共事件
requestCode: 1,
wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG]
};
wantAgent.getWantAgent(wantAgentInfo, (err: BusinessError, data: wantAgent.WantAgent) => {
if (err) {
console.error(`创建 WantAgent 失败:errCode=${err.code}, errMsg=${err.message}`);
return;
}
console.info("创建发送公共事件的 WantAgent 成功");
registerGnssGeofence(data);
});
}
端侧围栏仅支持圆形,需指定'中心点经纬度、半径、有效期',然后调用 on('gnssFenceStatusChange') 注册围栏:
/**
* 注册端侧 GNSS 地理围栏
* @param wantAgentObj 已创建的 WantAgent 对象
*/
function registerGnssGeofence(wantAgentObj: wantAgent.WantAgent) {
// 1. 配置围栏参数
const geofenceRequest: geoLocationManager.GeofenceRequest = {
scenario: 0x301, // 围栏场景:0x301 表示'日常位置监控'(固定值)
geofence: {
latitude: 31.12, // 围栏中心点纬度(如公司纬度)
longitude: 121.11, // 围栏中心点经度(如公司经度)
radius: 100, // 围栏半径(单位:米,建议 10-500 米,太大影响精度)
expiration: 86400000 // 围栏有效期(单位:毫秒,86400000=24 小时)
}
};
// 2. 注册围栏监听
try {
geoLocationManager.on('gnssFenceStatusChange', geofenceRequest, wantAgentObj);
console.info("端侧 GNSS 围栏注册成功");
} catch (err) {
const error = err as BusinessError;
console.error(`围栏注册失败:errCode=${error.code}, errMsg=${error.message}`);
}
}
当不需要围栏时(如用户退出考勤功能),需及时取消监听:
/**
* 取消端侧 GNSS 围栏监听
*/
function unregisterGnssGeofence() {
try {
geoLocationManager.off('gnssFenceStatusChange');
console.info("端侧 GNSS 围栏已取消");
} catch (err) {
const error = err as BusinessError;
console.error(`取消围栏失败:errCode=${error.code}, errMsg=${error.message}`);
}
}
云侧围栏无需开发者创建围栏,直接使用华为云侧的公共围栏(如商圈、景区、交通枢纽等),适合需要'大范围、多区域'围栏的场景。核心是通过 FenceExtensionAbility 接收云端触发的围栏事件。
FenceExtensionAbility 接收事件,执行自定义业务逻辑(如发送通知)。agconnect-services.json 文件,放到工程 entry/src/main 目录下(用于 App 与 AGC 关联)。FenceExtensionAbility 是专门接收云侧围栏事件的组件,需要单独创建和注册。
在工程 entry/ets 目录下新建 fenceextensionability 文件夹,创建 MyFenceExtensionAbility.ets 文件:
import { FenceExtensionAbility, geoLocationManager } from '@kit.LocationKit';
import { notificationManager } from '@kit.NotificationKit';
import { Want, wantAgent } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 自定义 FenceExtensionAbility,接收云侧围栏事件
*/
export class MyFenceExtensionAbility extends FenceExtensionAbility {
/**
* 围栏状态变化时回调(进入/离开/驻留)
* @param transition 围栏事件信息(围栏 ID、事件类型等)
* @param additions 附加信息(如围栏名称、类型)
*/
onFenceStatusChange(transition: geoLocationManager.GeofenceTransition, additions: Record<string, string>): void {
console.info(`收到云侧围栏事件:围栏 ID=${transition.geofenceId}, 事件类型=${transition.transitionEvent}`);
console.info(`围栏附加信息:${JSON.stringify(additions)}`);
// 业务逻辑:发送通知提醒用户(如商圈优惠通知)
this.sendFenceNotification(transition, additions);
}
/**
* 组件销毁时回调(释放资源)
*/
onDestroy(): void {
console.info("FenceExtensionAbility 已销毁");
super.onDestroy();
}
/**
* 发送围栏触发通知
*/
private sendFenceNotification(
transition: geoLocationManager.GeofenceTransition,
additions: Record<string, string>
): void {
// 1. 创建 WantAgent:点击通知时拉起 App
const wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: 'com.example.myapplication',
abilityName: 'EntryAbility',
parameters: {
geofenceId: transition.geofenceId,
transitionEvent: transition.transitionEvent,
fenceName: additions.fenceName // 围栏名称(从附加信息中获取)
} as Record<string, any>
} as Want
],
operationType: wantAgent.OperationType.START_ABILITY,
requestCode: 100
};
// 2. 创建通知并发布
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: wantAgent.WantAgent) => {
// 配置通知内容
const notificationRequest: notificationManager.NotificationRequest = {
id: Math.floor(Math.random() * 1000), // 唯一通知 ID
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: `【${additions.fenceName}】围栏通知`,
text: `您已${transition.transitionEvent === 1 ? '进入' : '离开'}${additions.fenceName},点击查看详情`,
}
},
notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION, // 通知类型(社交/服务等)
wantAgent: wantAgentObj // 点击通知触发的动作
};
// 发布通知
notificationManager.publish(notificationRequest).catch((err: BusinessError) => {
console.error(`发布通知失败:errCode=${err.code}, errMsg=${err.message}`);
});
}).catch((err: BusinessError) => {
console.error(`创建 WantAgent 失败:errCode=${err.code}, errMsg=${err.message}`);
});
}
}
在 module.json5 中注册 FenceExtensionAbility,类型必须设为 fence:
{
"module": {
"extensionAbilities": [
{
"name": "MyFenceExtensionAbility",
"srcEntry": "./ets/fenceextensionability/MyFenceExtensionAbility.ets",
"description": "接收云侧围栏事件的扩展能力",
"type": "fence", // 固定类型:fence
"exported": false // 不对外暴露
}
]
}
}
云侧围栏无需手动注册围栏(AGC 已配置),只需确保 App 已集成 AGC 配置,且用户已授权位置权限,设备联网后即可自动接收事件。
transition.transitionEvent:围栏事件类型,1=进入围栏,2=离开围栏,3=驻留围栏(可根据业务需求处理不同事件)。additions:附加信息,包含围栏名称(fenceName)、围栏类型(fenceType)等,具体字段由 AGC 配置的公共围栏决定。isLocationEnabled() 是否为 true,确保位置开关已打开。FenceExtensionAbility 未注册或注册错误。agconnect-services.json 是否正确放置。module.json5 中 extensionAbilities 的 type 是否为 fence,srcEntry 路径是否正确。LOCATION_IN_BACKGROUND 权限;App 被系统后台回收。FenceExtensionAbility 是系统级组件,后台回收概率低;端侧围栏可申请'长时任务'保活(参考 HarmonyOS 长时任务机制)。地理围栏是 Location Kit 的'场景化核心能力',端侧 GNSS 围栏适合自定义小范围场景,云侧围栏适合快速接入公共围栏,两者互补覆盖绝大多数位置提醒需求。开发时需注意:
通过本文的实战指南,你可以根据自己的业务场景选择合适的围栏方案,快速实现'基于位置的智能提醒'功能。如果需要更复杂的场景(如多围栏叠加、驻留事件处理),可以进一步参考华为开发者文档的接口详情,结合定位和地理编码能力,打造更完整的位置服务体验。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online