在 HarmonyOS 上使用 ArkUI 实现计步器应用

介绍

本篇 Codelab 使用 ArkTS 语言实现计步器应用,应用主要包括计步传感器、定位服务和后台任务功能:

  1. 通过订阅计步器传感器获取计步器数据,处理后显示。

  2. 通过订阅位置服务获取位置数据,处理后显示。

  3. 通过服务开发实现后台任务功能。

相关概念

计步传感器:订阅计步器传感器数据,系统返回相关数据。

后台任务管理:应用中存在用户能够直观感受到的且需要一直在后台运行的业务时(如,后台播放音乐),可以使用长时任务机制。

位置服务:位置服务提供 GNSS 定位、网络定位、地理编码、逆地理编码、国家码和地理围栏等基本功能。

相关权限

本篇 Codelab 用到了计步传感器、后台任务及位置服务功能,需要在配置文件 module.json5 里添加权限:

● ohos.permission.ACTIVITY_MOTION

● ohos.permission.KEEP_BACKGROUND_RUNNING

● ohos.permission.APPROXIMATELY_LOCATION

● ohos.permission.LOCATION

● ohos.permission.LOCATION_IN_BACKGROUND

完整示例

gitee源码地址

源码下载

计步器应用(ArkTS).zip

环境搭建

我们首先需要完成 HarmonyOS 开发环境搭建,可参照如下步骤进行。

软件要求

DevEco Studio版本:DevEco Studio 3.1 Release。

HarmonyOS SDK版本:API version 9。

硬件要求

设备类型:华为手机或运行在 DevEco Studio 上的华为手机设备模拟器。

HarmonyOS 系统:3.1.0 Developer Release。

环境搭建

安装 DevEco Studio,详情请参考下载和安装软件

设置 DevEco Studio 开发环境,DevEco Studio 开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:如果可以直接访问 Internet,只需进行下载HarmonyOS SDK操作。

如果网络不能直接访问 Internet,需要通过代理服务器才可以访问,请参考配置开发环境

开发者可以参考以下链接,完成设备调试的相关配置:使用真机进行调试

使用模拟器进行调试

代码结构解读

本篇 Codelab 只对核心代码进行讲解,对于完整代码,我们会在源码下载或 gitee 中提供。

复制代码
├──entry/src/main/ets               // 代码区│  ├──common│  │  ├──constants│  │  │  └──CommonConstants.ets     // 公共常量│  │  └──utils                      // 日志类│  │     ├──BackgroundUtil.ets      // 后台任务工具类│  │     ├──GlobalContext.ets       // 首选项工具类│  │     ├──LocationUtil.ets        // 位置服务工具类│  │     ├──Logger.ets              // 日志工具类│  │     ├──NumberUtil.ets          // 数字处理工具类│  │     └──StepsUtil.ets           // 计步器工具类│  ├──entryability│  │  └──EntryAbility.ets           // 程序入口类│  ├──pages│  │  └──HomePage.ets               // 应用首页│  └──view│     ├──CompletionStatus.ets       // 目标设置页│     ├──CurrentSituation.ets       // 计步信息页│     └──InputDialog.ets            // 自定义弹窗└──entry/src/main/resources         // 资源文件夹

构建应用界面

计步器页面主要由 Stack 堆叠容器组件、Component 自定义组件和 CustomDialog 自定义弹窗组件完成页面布局,效果如图所示:

复制代码
// HomePage.etsbuild() {  Stack({ alignContent: Alignment.TopStart }) {    CompletionStatus({      progressValue: $progressValue    })
    CurrentSituation({      currentSteps: this.currentSteps,      startPosition: this.startPosition,      currentLocation: this.currentLocation    })
    Row() {      Button(this.isStart ? $r('app.string.stop') : $r('app.string.start'))        ...    }    ...  }  ...}

计步传感器

应用启动后申请计步传感器权限,获取权限后订阅计步器传感器。通过订阅获取到计步传感器数据,解析处理后在页面显示。效果如图所示:

复制代码
// HomePage.ets
requestPermissions(): void {
  let atManager = abilityAccessCtrl.createAtManager();
  try {
    atManager.requestPermissionsFromUser(this.context, CommonConstants.REQUEST_PERMISSIONS).then((data) => {
      if (data.authResults[0] !== 0 || data.authResults[1] !== 0) {
        return;
      }
      const that = this;
      try {
        sensor.on(sensor.SensorId.PEDOMETER, (data) => {
          try {
            if (that.isStart) {
              if (StepsUtil.checkStrIsEmpty(that.oldSteps)) {
                that.oldSteps = data.steps.toString();
                StepsUtil.putStorageValue(CommonConstants.OLD_STEPS, that.oldSteps);
              } else {
                that.currentSteps = (data.steps - NumberUtil._parseInt(that.oldSteps, 10)).toString();
              }
            } else {
              that.currentSteps = data.steps.toString();
            }

            if (StepsUtil.checkStrIsEmpty(that.stepGoal) || !that.isStart) {
              return;
            }
            StepsUtil.putStorageValue(CommonConstants.CURRENT_STEPS, that.currentSteps);
            that.progressValue = StepsUtil.getProgressValue(NumberUtil._parseInt(that.stepGoal, 10),
              NumberUtil._parseInt(that.currentSteps, 10));
            StepsUtil.putStorageValue(CommonConstants.PROGRESS_VALUE_TAG, String(that.progressValue));
          } catch (err) {
            Logger.error(TAG, 'Sensor on err' + JSON.stringify(err));
          }
        }, { interval: CommonConstants.SENSOR_INTERVAL });
        ...
}

位置服务

应用启动后申请位置服务权限,获取权限后启动服务,启动服务后订阅位置服务。通过订阅获取到位置服务数据,解析处理后在页面显示。效果如图所示:

复制代码
// HomePage.ets
requestPermissions(): void {
  ...
  LocationUtil.geolocationOn((location: geoLocationManager.Location) => {
    if (this.latitude === location.latitude && this.longitude === location.longitude) {
      return;
    }
    this.latitude = location.latitude;
    this.longitude = location.longitude;
    let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
      'latitude': this.latitude,
      'longitude': this.longitude
    };
    geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest).then(data => {
      if (data[0].placeName) {
        this.currentLocation = data[0].placeName;
      }
    }).catch((err: Error) => {
      Logger.error(TAG, 'GetAddressesFromLocation err ' + JSON.stringify(err));
    });
  });
  ...
}

将位置服务相关的函数封装到工具类中。

复制代码
// LocationUtil.ets
class LocationUtil {
  geolocationOn(locationChange: (location: geoLocationManager.Location) => void): void {
    let requestInfo: geoLocationManager.LocationRequest = {
      'priority': 0x203,
      'scenario': 0x300,
      'timeInterval': 0,
      'distanceInterval': 0,
      'maxAccuracy': 0
    }
    try {
      geoLocationManager.on('locationChange', requestInfo, locationChange);
    } catch (err) {
      console.error("locationChange error:" + JSON.stringify(err));
    }
  }

  geolocationOff(): void {
    geoLocationManager.off('locationChange');
  }
}

后台任务

点击开始按钮开启后台任务,通过后台任务管理方法配置申请的后台模式等参数启动后台任务。

复制代码
// HomePage.ets
build() {
  Stack({ alignContent: Alignment.TopStart }) {
    ...
    Row() {
      Button(this.isStart ? $r('app.string.stop') : $r('app.string.start'))
        ...
        .onClick(() => {
          if (this.isStart) {
            ...
            BackgroundUtil.stopContinuousTask(this.context);
          } else {
            if (this.stepGoal === '' || this.currentLocation === '') {
              promptAction.showToast({ message: CommonConstants.WAIT });
            } else {
              ...
              BackgroundUtil.startContinuousTask(this.context);
            }
          }
          StepsUtil.putStorageValue(CommonConstants.IS_START, String(this.isStart));
        })
    }
    ...
}

// BackgroundUtil.ets
export class BackgroundUtil {
  public static startContinuousTask(context: common.UIAbilityContext): void {
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
          bundleName: context.abilityInfo.bundleName,
          abilityName: context.abilityInfo.name
        }
      ],
      operationType: wantAgent.OperationType.START_ABILITY,
      requestCode: 0,
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };

    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
      try {
        backgroundTaskManager.startBackgroundRunning(context,
          backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
          Logger.info(TAG, 'startBackgroundRunning succeeded');
        }).catch((err: Error) => {
          Logger.error(TAG, `startBackgroundRunning failed Cause:  ${JSON.stringify(err)}`);
        });
      } catch (error) {
        Logger.error(TAG, `stopBackgroundRunning failed. error: ${JSON.stringify(error)} `);
      }
    });
  }

  public static stopContinuousTask(context: common.UIAbilityContext): void {
    try {
      backgroundTaskManager.stopBackgroundRunning(context).then(() => {
        Logger.info(TAG, 'stopBackgroundRunning succeeded');
      }).catch((err: Error) => {
        Logger.error(TAG, `stopBackgroundRunning failed Cause:  ${JSON.stringify(err)}`);
      });
    } catch (error) {
      Logger.error(TAG, `stopBackgroundRunning failed. error: ${JSON.stringify(error)} `);
    }
  }
}

总结

您已经完成了本次 Codelab 的学习,并了解到以下知识点:

  1. 计步器传感器的功能实现。

  2. 位置服务的功能实现。

  3. 后台任务的功能实现。

相关推荐
一只栖枝5 小时前
华为 HCIE 大数据认证中 Linux 命令行的运用及价值
大数据·linux·运维·华为·华为认证·hcie·it
zhanshuo9 小时前
在鸿蒙里优雅地处理网络错误:从 Demo 到实战案例
harmonyos
zhanshuo9 小时前
在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
harmonyos
whysqwhw14 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw15 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw17 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw18 小时前
鸿蒙音频编码
harmonyos
whysqwhw18 小时前
鸿蒙音频解码
harmonyos
whysqwhw18 小时前
鸿蒙视频解码
harmonyos
whysqwhw18 小时前
鸿蒙视频编码
harmonyos