本篇Codelab使用ArkTS语言实现计步器应用,应用主要包括计步传感器、定位服务和后台任务功能:
本篇Codelab用到了计步传感器、后台任务及位置服务功能,需要在配置文件module.json5里添加权限:
● ohos.permission.ACTIVITY_MOTION
● ohos.permission.KEEP_BACKGROUND_RUNNING
● ohos.permission.APPROXIMATELY_LOCATION
● ohos.permission.LOCATION
● ohos.permission.LOCATION_IN_BACKGROUND
安装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.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'))
...
}
...
}
...
}
应用启动后申请计步传感器权限,获取权限后订阅计步器传感器。通过订阅获取到计步传感器数据,解析处理后在页面显示。效果如图所示:
// 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
classLocationUtil{
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 classBackgroundUtil{
publicstaticstartContinuousTask(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)} `);
}
});
}
publicstaticstopContinuousTask(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的学习,并了解到以下知识点:
为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05
HarmonyOS 概念:https://qr21.cn/FV7h05
如何快速入门:https://qr21.cn/FV7h05
开发基础知识:https://qr21.cn/FV7h05
基于ArkTS 开发:https://qr21.cn/FV7h05