大家好,我是陈杨。相信大家也都认识我了,我们前面也写了很多篇文章,感兴趣可以打开我的主页去了解一下。觉得写的好的,不要忘记给我点赞哦,感谢!!

在上一篇文章中,我们掌握了Location Kit的核心定位能力和地理编码技巧,而地理围栏(GeoFence)作为Location Kit的高阶扩展功能,更是让“基于位置的智能提醒”成为可能——比如员工进入公司园区自动打卡、快递员靠近小区时推送取件通知、用户进入商圈时收到商家优惠,这些场景都能通过地理围栏技术轻松实现。

HarmonyOS提供了端侧GNSS围栏云侧围栏两种方案,分别适配个性化围栏需求和公共围栏需求。今天我们就从方案选型、开发步骤到代码实战,手把手带你搞定地理围栏开发,同时兼顾合规性与实用性。

一、地理围栏核心认知:两种方案怎么选?

在动手开发前,先明确两种地理围栏方案的核心区别,避免选不对场景走弯路。地理围栏本质是“虚拟地理边界”,当设备进入、离开或驻留该边界时,系统会触发预设动作(如拉起App、发送通知),而两种方案的差异集中在“围栏管理方式”和“适用场景”:

方案类型 核心特点 适用场景 依赖条件 围栏形状/管理
端侧GNSS围栏 开发者自主创建/管理,本地触发事件 企业考勤、个人家庭区域提醒、自定义小范围围栏 1. 设备支持GNSS芯片;2. 室外开阔环境(室内信号弱);3. 位置权限+用户授权 仅支持圆形围栏;开发者控制生命周期(创建/删除)
云侧围栏 直接使用华为云侧公共围栏,云端触发 商圈推送、景区导览、城市级服务提醒 1. 设备联网;2. AGC平台配置围栏策略;3. 位置权限+用户授权 支持多种公共围栏(商圈、景区等);华为云维护围栏数据

举个直观例子:如果想做“公司300米范围内打卡提醒”,用端侧GNSS围栏自主定义圆形区域即可;如果想做“用户进入全国任意万达商圈时推送优惠”,直接对接华为云侧的公共商圈围栏,用云侧围栏更高效(无需自己维护全国万达的位置数据)。

二、开发前置:权限与合规准备

地理围栏依赖位置信息,且涉及用户敏感数据,权限申请和合规性是前提,缺一不可。

2.1 必备权限清单

无论端侧还是云侧围栏,都需要申请以下位置权限(同定位功能权限,可复用上一篇的权限配置逻辑):

  • 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" }
      }
    ]
  }
}

2.2 合规要求:用户授权与数据安全

根据华为Location Kit的个人数据处理规范:

  1. 必须获得用户明确同意:使用地理围栏前,需向用户说明“为什么需要监听位置”(如“考勤打卡需识别您是否进入公司区域”),用户同意后才能启用围栏。
  2. 数据不持久化:定位服务处理用户位置数据后会立即删除,不会保存,开发者无需额外处理数据存储合规性,但需在App隐私政策中说明这一点。

三、端侧GNSS围栏开发:自定义圆形围栏(ArkTS)

端侧GNSS围栏是最常用的方案,适合开发者自主定义小范围圆形围栏,事件触发在本地设备,响应速度快。核心流程是“创建WantAgent→配置围栏参数→注册围栏监听”。

3.1 核心概念:WantAgent的作用

地理围栏触发后需要执行预设动作(如拉起App、发送通知),而WantAgent就是HarmonyOS中“封装动作意图”的工具——它能把“拉起某Ability”“发送公共事件”等动作包装成对象,围栏触发时系统会自动执行该动作。

3.2 完整开发步骤

步骤1:导入核心模块

需要导入地理围栏、WantAgent和错误处理相关模块:

import { geoLocationManager } from '@kit.LocationKit';
import { wantAgent } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
步骤2:创建WantAgent(定义围栏触发动作)

根据业务需求选择动作类型,常用两种场景:

场景A:触发时拉起App的Ability(如考勤打卡页面)
/**
 * 创建拉起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);
  });
}
场景B:触发时发送公共事件(如通知App后台处理)
/**
 * 创建发送公共事件的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);
  });
}
步骤3:配置围栏参数并注册监听

端侧围栏仅支持圆形,需指定“中心点经纬度、半径、有效期”,然后调用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}`);
  }
}
步骤4:取消围栏监听(避免内存泄漏)

当不需要围栏时(如用户退出考勤功能),需及时取消监听:

/**
 * 取消端侧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}`);
  }
}

3.3 关键注意事项

  1. 环境限制:端侧围栏依赖GNSS芯片,仅在室外开阔区域能准确触发(室内、高楼遮挡时GNSS信号弱,可能误触发或不触发)。
  2. 半径建议:半径太小(<10米)可能因定位误差导致漏触发,太大(>1000米)精度下降,建议根据场景设置10-500米。
  3. 有效期设置:若不需要长期有效,建议设置合理有效期(如考勤围栏仅设置工作日9:00-18:00有效),避免不必要的功耗。

四、云侧围栏开发:公共围栏快速接入(ArkTS)

云侧围栏无需开发者创建围栏,直接使用华为云侧的公共围栏(如商圈、景区、交通枢纽等),适合需要“大范围、多区域”围栏的场景。核心是通过FenceExtensionAbility接收云端触发的围栏事件。

4.1 核心流程梳理

  1. 开发者在AGC(AppGallery Connect)平台配置要使用的公共围栏(如“全国万达商圈”)。
  2. 设备满足围栏条件(进入/离开)时,华为云会通知设备。
  3. 设备通过FenceExtensionAbility接收事件,执行自定义业务逻辑(如发送通知)。

4.2 完整开发步骤

步骤1:AGC平台配置(前置操作)
  1. 登录AGC平台,进入你的应用。
  2. 开通“Location Kit”服务,在“地理围栏”模块选择需要的公共围栏类型(如商圈、景区),配置下发策略(如“进入围栏时触发”)。
  3. 下载agconnect-services.json文件,放到工程entry/src/main目录下(用于App与AGC关联)。
步骤2:创建FenceExtensionAbility(事件接收组件)

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)

module.json5中注册FenceExtensionAbility,类型必须设为fence

{
  "module": {
    "extensionAbilities": [
      {
        "name": "MyFenceExtensionAbility",
        "srcEntry": "./ets/fenceextensionability/MyFenceExtensionAbility.ets",
        "description": "接收云侧围栏事件的扩展能力",
        "type": "fence", // 固定类型:fence
        "exported": false // 不对外暴露
      }
    ]
  }
}
步骤3:启动云侧围栏监听

云侧围栏无需手动注册围栏(AGC已配置),只需确保App已集成AGC配置,且用户已授权位置权限,设备联网后即可自动接收事件。

4.3 关键参数解析

  • transition.transitionEvent:围栏事件类型,1=进入围栏,2=离开围栏,3=驻留围栏(可根据业务需求处理不同事件)。
  • additions:附加信息,包含围栏名称(fenceName)、围栏类型(fenceType)等,具体字段由AGC配置的公共围栏决定。

五、避坑指南:常见问题与解决方案

5.1 端侧围栏触发不灵敏

  • 原因:室内/高楼遮挡导致GNSS信号弱;围栏半径太小;定位权限未授权。
  • 解决方案:
    1. 提示用户在室外使用该功能。
    2. 半径设置≥30米,避免定位误差影响。
    3. 检查isLocationEnabled()是否为true,确保位置开关已打开。

5.2 云侧围栏收不到事件

  • 原因:AGC未配置围栏策略;设备未联网;FenceExtensionAbility未注册或注册错误。
  • 解决方案:
    1. 检查AGC平台“地理围栏”配置是否生效,agconnect-services.json是否正确放置。
    2. 确保设备联网(云侧事件依赖网络传输)。
    3. 检查module.json5extensionAbilitiestype是否为fencesrcEntry路径是否正确。

5.3 后台无法接收围栏事件

  • 原因:未申请LOCATION_IN_BACKGROUND权限;App被系统后台回收。
  • 解决方案:
    1. 申请后台位置权限,并向用户说明后台使用场景。
    2. 对于云侧围栏,FenceExtensionAbility是系统级组件,后台回收概率低;端侧围栏可申请“长时任务”保活(参考HarmonyOS长时任务机制)。

总结

地理围栏是Location Kit的“场景化核心能力”,端侧GNSS围栏适合自定义小范围场景,云侧围栏适合快速接入公共围栏,两者互补覆盖绝大多数位置提醒需求。开发时需注意:

  1. 权限与合规是前提,必须获得用户授权并说明数据用途。
  2. 端侧围栏依赖GNSS和室外环境,云侧围栏依赖AGC配置和网络。
  3. 及时取消围栏监听或销毁组件,避免内存泄漏和不必要的功耗。

通过本文的实战指南,你可以根据自己的业务场景选择合适的围栏方案,快速实现“基于位置的智能提醒”功能。如果需要更复杂的场景(如多围栏叠加、驻留事件处理),可以进一步参考华为开发者文档的接口详情,结合上一篇的定位和地理编码能力,打造更完整的位置服务体验。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐