• 在 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'))        ...    }    ...  }  ...}
    
    

    计步传感器

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

    1. // HomePage.ets
    2. requestPermissions(): void {
    3. let atManager = abilityAccessCtrl.createAtManager();
    4. try {
    5. atManager.requestPermissionsFromUser(this.context, CommonConstants.REQUEST_PERMISSIONS).then((data) => {
    6. if (data.authResults[0] !== 0 || data.authResults[1] !== 0) {
    7. return;
    8. }
    9. const that = this;
    10. try {
    11. sensor.on(sensor.SensorId.PEDOMETER, (data) => {
    12. try {
    13. if (that.isStart) {
    14. if (StepsUtil.checkStrIsEmpty(that.oldSteps)) {
    15. that.oldSteps = data.steps.toString();
    16. StepsUtil.putStorageValue(CommonConstants.OLD_STEPS, that.oldSteps);
    17. } else {
    18. that.currentSteps = (data.steps - NumberUtil._parseInt(that.oldSteps, 10)).toString();
    19. }
    20. } else {
    21. that.currentSteps = data.steps.toString();
    22. }
    23. if (StepsUtil.checkStrIsEmpty(that.stepGoal) || !that.isStart) {
    24. return;
    25. }
    26. StepsUtil.putStorageValue(CommonConstants.CURRENT_STEPS, that.currentSteps);
    27. that.progressValue = StepsUtil.getProgressValue(NumberUtil._parseInt(that.stepGoal, 10),
    28. NumberUtil._parseInt(that.currentSteps, 10));
    29. StepsUtil.putStorageValue(CommonConstants.PROGRESS_VALUE_TAG, String(that.progressValue));
    30. } catch (err) {
    31. Logger.error(TAG, 'Sensor on err' + JSON.stringify(err));
    32. }
    33. }, { interval: CommonConstants.SENSOR_INTERVAL });
    34. ...
    35. }


     

    位置服务

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

    1. // HomePage.ets
    2. requestPermissions(): void {
    3. ...
    4. LocationUtil.geolocationOn((location: geoLocationManager.Location) => {
    5. if (this.latitude === location.latitude && this.longitude === location.longitude) {
    6. return;
    7. }
    8. this.latitude = location.latitude;
    9. this.longitude = location.longitude;
    10. let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
    11. 'latitude': this.latitude,
    12. 'longitude': this.longitude
    13. };
    14. geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest).then(data => {
    15. if (data[0].placeName) {
    16. this.currentLocation = data[0].placeName;
    17. }
    18. }).catch((err: Error) => {
    19. Logger.error(TAG, 'GetAddressesFromLocation err ' + JSON.stringify(err));
    20. });
    21. });
    22. ...
    23. }

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

    1. // LocationUtil.ets
    2. class LocationUtil {
    3. geolocationOn(locationChange: (location: geoLocationManager.Location) => void): void {
    4.     let requestInfo: geoLocationManager.LocationRequest = {
    5. 'priority': 0x203,
    6. 'scenario': 0x300,
    7. 'timeInterval': 0,
    8. 'distanceInterval': 0,
    9. 'maxAccuracy': 0
    10. }
    11. try {
    12.       geoLocationManager.on('locationChange', requestInfo, locationChange);
    13. } catch (err) {
    14.       console.error("locationChange error:" + JSON.stringify(err));
    15. }
    16. }
    17. geolocationOff(): void {
    18.     geoLocationManager.off('locationChange');
    19. }
    20. }


     

    后台任务

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

    1. // HomePage.ets
    2. build() {
    3. Stack({ alignContent: Alignment.TopStart }) {
    4. ...
    5. Row() {
    6. Button(this.isStart ? $r('app.string.stop') : $r('app.string.start'))
    7. ...
    8. .onClick(() => {
    9. if (this.isStart) {
    10. ...
    11. BackgroundUtil.stopContinuousTask(this.context);
    12. } else {
    13. if (this.stepGoal === '' || this.currentLocation === '') {
    14. promptAction.showToast({ message: CommonConstants.WAIT });
    15. } else {
    16. ...
    17. BackgroundUtil.startContinuousTask(this.context);
    18. }
    19. }
    20. StepsUtil.putStorageValue(CommonConstants.IS_START, String(this.isStart));
    21. })
    22. }
    23. ...
    24. }
    25. // BackgroundUtil.ets
    26. export class BackgroundUtil {
    27. public static startContinuousTask(context: common.UIAbilityContext): void {
    28. let wantAgentInfo: wantAgent.WantAgentInfo = {
    29. wants: [
    30. {
    31. bundleName: context.abilityInfo.bundleName,
    32. abilityName: context.abilityInfo.name
    33. }
    34. ],
    35. operationType: wantAgent.OperationType.START_ABILITY,
    36. requestCode: 0,
    37. wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    38. };
    39. wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
    40. try {
    41. backgroundTaskManager.startBackgroundRunning(context,
    42. backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
    43. Logger.info(TAG, 'startBackgroundRunning succeeded');
    44. }).catch((err: Error) => {
    45. Logger.error(TAG, `startBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
    46. });
    47. } catch (error) {
    48. Logger.error(TAG, `stopBackgroundRunning failed. error: ${JSON.stringify(error)} `);
    49. }
    50. });
    51. }
    52. public static stopContinuousTask(context: common.UIAbilityContext): void {
    53. try {
    54. backgroundTaskManager.stopBackgroundRunning(context).then(() => {
    55. Logger.info(TAG, 'stopBackgroundRunning succeeded');
    56. }).catch((err: Error) => {
    57. Logger.error(TAG, `stopBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
    58. });
    59. } catch (error) {
    60. Logger.error(TAG, `stopBackgroundRunning failed. error: ${JSON.stringify(error)} `);
    61. }
    62. }
    63. }

    总结

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

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

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

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

  • 相关阅读:
    [PAT练级笔记] 69 Basic Level 1069 微博转发抽奖
    QCustomPlot 坐标轴间隔显示刻度标签
    STM32解析航模遥控器的PPM信号
    并发编程day06
    Python:实现magicdiamondpattern魔法钻石图案算法(附完整源码)
    丝丝入扣,毫不违和,AI一键换脸和微调,基于Rope-Ruby,2024最新整合包
    传媒行业指哪些?需要过等保吗?
    海康G5系列(armv7l) heop模式下交叉编译Qt qmqtt demo,出现moc缺少高版本GLibc问题之解决
    400电话申请办理:为企业提供高效沟通的必备工具
    pandas中的数据结构
  • 原文地址:https://blog.csdn.net/HarmonyOSDev/article/details/134437831