员工外出工作,需要每隔一定时间上报一次位置,应用处于后台运行的案例。
在module.json5配置文件中abilities下的backgroundModes字段里,为需要使用长时任务的UIAbility声明相应的长时任务类型,配置文件中填写长时任务类型的配置项。
- "backgroundModes": [
- "location"
- ],
- import { geoLocationManager } from '@kit.LocationKit';
- import { abilityAccessCtrl, common, PermissionRequestResult, Permissions, WantAgent, wantAgent } from '@kit.AbilityKit';
- import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
-
- @Entry
- @Component
- struct Index {
- private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
- @State workingState: boolean = false;
- @State locationInfos: Array<LocationInfo> = [];
-
- // Start solution1
- aboutToAppear() {
- // 申请定位权限
- let permissions: Array<Permissions> = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
- this.reqPermissionsFromUser(permissions);
- }
-
- reqPermissionsFromUser(permissions: Array<Permissions>): void {
- let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
- // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
- atManager.requestPermissionsFromUser(this.context, permissions).then((data: PermissionRequestResult) => {
- console.info(`requestPermissionsFromUser success, result: ${JSON.stringify(data)}`);
- }).catch((err: BusinessError) => {
- console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
- });
- }
- // End solution1
-
- build() {
- Column({ space: 16 }) {
- Row() {
- Column({ space: 20 }) {
- Row({ space: 15 }) {
- Image($r('sys.media.person_shield'))
- .width(30)
- .height(30)
-
- Text('张三')
- }
-
- Text('外出场所:大运广场')
- }
- .alignItems(HorizontalAlign.Start)
- .width('70%')
-
- Blank().width(2).backgroundColor(Color.Gray)
-
- Column() {
- Column() {
- if (this.workingState) {
- Text('结束')
- } else {
- Text('开始')
- }
- Text('工作')
- }
- .padding(5)
- .backgroundColor(this.workingState ? '#E84026' : '#64BB5C')
- .borderRadius($r('sys.float.corner_radius_level4'))
- .onClick(() => {
- if (this.workingState) {
- this.offLocationListen();
- this.stopContinuousTask();
- } else {
- this.getLocation();
- this.startContinuousTask();
- }
- });
- }
- .width('29%')
- }
- .width('100%')
- .padding(14)
- .borderRadius(8)
- .backgroundColor(Color.White)
-
- Column() {
- Row() {
- Text('位置信息列表')
-
- Text(`Total: ${this.locationInfos.length}`)
- }
- .width('100%')
- .justifyContent(FlexAlign.SpaceBetween)
-
- Scroll() {
- List({ space: 5 }) {
- ForEach(this.locationInfos, (item: LocationInfo) => {
- ListItem() {
- Column() {
- Text(`时间:${item.time}`)
-
- Text(`坐标:[${item.latitude}, ${item.longitude}]`)
-
- Text(`地址:${item.address}`)
- }
- .width('100%')
- .padding(5)
- .borderRadius(5)
- .alignItems(HorizontalAlign.Start)
- .backgroundColor('#0C0A59F7')
- }
- })
- }
- }
- .scrollBar(BarState.Off)
- .padding({ top: 5, bottom: 10 })
- }
- .width('100%')
- .height('100%')
- .padding(14)
- .borderRadius(8)
- .flexShrink(999)
- .backgroundColor(Color.White)
- }
- .height('100%')
- .width('100%')
- .padding(12)
- .backgroundColor('#f1f3f5')
- }
-
- locationChange = async (location: geoLocationManager.Location) => {
- if (location) {
- this.workingState = true;
- if (this.locationInfos.length >= 50) {
- this.locationInfos.pop();
- }
-
- let info = new LocationInfo();
- info.location = location;
- info.latitude = location?.latitude.toFixed(5);
- info.longitude = location?.longitude.toFixed(5);
- info.time = new Date(location?.timeStamp).toLocaleString('zh-CN', { hour12: false });
- let address = await this.getAddressFromLocation(location.latitude, location.longitude);
- info.address = address;
- this.locationInfos.push(info);
- }
- };
-
- /**
- * 每隔2秒上报一次位置
- */
- getLocation() {
- let requestInfo: geoLocationManager.LocationRequest = {
- 'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX,
- 'scenario': geoLocationManager.LocationRequestScenario.UNSET,
- 'timeInterval': 2,
- 'distanceInterval': 0,
- 'maxAccuracy': 0
- };
-
- try {
- geoLocationManager.on('locationChange', requestInfo, this.locationChange);
- } catch (err) {
- console.error(`getLocation errCode: ${err?.code},errMessage: ${err?.message}`);
- }
- }
-
- /**
- * 坐标转化为地址
- */
- async getAddressFromLocation(lati: number, long: number): Promise<string> {
- let address: string | undefined = '';
- let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
- 'latitude': lati,
- 'longitude': long,
- 'maxItems': 1
- };
- let locationResult = await geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest);
- console.log(`locationResult => ${JSON.stringify(locationResult)}`);
- if (locationResult && locationResult.length > 0) {
- address = locationResult[0].placeName;
- }
- return address ? address : '';
- }
-
- /**
- * 关闭位置上报
- */
- offLocationListen() {
- try {
- // 需要取消订阅的回调函数。该回调函数需要与on接口传入的回调函数保持一致。若无此参数,则取消当前类型的所有订阅。
- geoLocationManager.off('locationChange', this.locationChange);
- this.workingState = false;
- this.locationInfos = [];
- } catch (err) {
- console.error(`offLocationListen errCode: ${err?.code},errMessage: ${err?.message}`);
- }
- }
-
- startContinuousTask() {
- let wantAgentInfo: wantAgent.WantAgentInfo = {
- // 点击通知后,将要执行的动作列表
- // 添加需要被拉起应用的bundleName和abilityName
- wants: [
- {
- bundleName: this.context.abilityInfo.bundleName,
- abilityName: 'EntryAbility'
- }
- ],
- // 指定点击通知栏消息后的动作是拉起ability
- actionType: wantAgent.OperationType.START_ABILITY,
- // 使用者自定义的一个私有值
- requestCode: 0,
- // 点击通知后,动作执行属性
- actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
- };
-
- // 通过wantAgent模块下getWantAgent方法获取WantAgent对象
- wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
- backgroundTaskManager.startBackgroundRunning(this.context,
- backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
- console.info(`Succeeded in operating startBackgroundRunning.`);
- }).catch((err: BusinessError) => {
- console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
- });
- });
- }
-
- stopContinuousTask() {
- backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
- console.info(`Succeeded in operating stopBackgroundRunning.`);
- }).catch((err: BusinessError) => {
- console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
- });
- }
- }
-
- class LocationInfo {
- public location: geoLocationManager.Location | undefined;
- public latitude: string = '';
- public longitude: string = '';
- public address: string = '';
- public time: string = '';
- }
Q:应用置于后台一段时间后会被冻结,报FROZEN_BY_DEVICE_STILL_ENTER。
A:FROZEN_BY_DEVICE_STILL_ENTER表示应用基于定位的长时任务,在应用退至后台进入静止状态超过3分钟或者应用退后台前已进入静止超过30秒,系统会主动进行冻结。
Q:应用在后台长时间进行定位采集时,进程被反复kill并重启,日志显示kill reason=GnssWorkTimeAbnormal。
A:应用长时间进行定位采集但未申请后台持续定位长时任务时,系统会对进程进行管控。需要申请后台持续定位长时任务,具体操作请参见【解决方案】章节。