在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
完整示例
源码下载
环境搭建
我们首先需要完成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中提供。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ├──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自定义弹窗组件完成页面布局,效果如图所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // HomePage.ets build() { 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // 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 }); ... } |
位置服务
应用启动后申请位置服务权限,获取权限后启动服务,启动服务后订阅位置服务。通过订阅获取到位置服务数据,解析处理后在页面显示。效果如图所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // 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)); }); }); ... } |
将位置服务相关的函数封装到工具类中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 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' ); } } |
后台任务
点击开始按钮开启后台任务,通过后台任务管理方法配置申请的后台模式等参数启动后台任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | // 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. 后台任务的功能实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2022-11-16 【#HDC2022】HarmonyOS体验官活动正式开启,赶快投稿赢限量奖品吧!