收藏
回答

安卓系统微信后台运行时wx.onLocationChange不执行

框架类型 问题类型 操作系统 操作系统版本 手机型号 微信版本
小程序 Bug Android HarmonyOS(4.2.0.132) HUAWEI Mate 40 Pro Version8.0.50

问题详细描述:

目前公司业务需要可以通过小程序定位到员工的位置,但是员工微信小程序不可能一直前台运行着,大部分情况下还是后台运行,为此申请开通了wx.onLocationChange、wx.startLocationUpdate、wx.startLocationUpdateBackground接口,但是发现ios系统没问题,可以后台运行,安卓手机超过一定时间后,小程序就会重置,以下是代码片段

<template>
	<view>
		<custom-tab-bar>
			<view class="container">
				<map id="map" :longitude="longitude" :latitude="latitude" :scale="17"
					:style="{ width: '100%', height: mapHeight + 'px' }" show-location :polyline="polyline"
					:markers="markers" @regionchange="regionChange"></map>
				<view class="toggle-info" @click="toggleInfo">
					<image v-if="showInfo" src="@/static/collapse.png" />
					<image v-else src="@/static/expand.png" />
				</view>
				<view class="banner">
					<view class="info">
						<text>轨迹距离: {{ (distance / 1000).toFixed(2) }} 公里</text>
						<text>海拔高度: {{ altitude }} 米</text>
					</view>
					<view class="controls" v-show="showInfo">
						<image v-if="!tracking && !paused" src="@/static/start.png" @click="startTracking" />
						<image v-if="paused" src="@/static/continue.png" @click="resumeTracking" />
						<image v-if="tracking" src="@/static/stop.png" @click="stopTracking" />
						<image v-if="paused" src="@/static/pause.png" @click="confirmTerminateTracking" />
					</view>
					<!-- <text v-if="elapsedTime">经过时间: {{ Math.floor(elapsedTime / 60) }} 分 {{ elapsedTime % 60 }} 秒</text> -->
				</view>
			</view>
		</custom-tab-bar>
	</view>
</template>




<script>
	import {
		startInspection,
		endInspection
	} from '@/api/track.js'
	import {
		getFormattedDate,
	} from '@/utils/common.js'
	import CustomTabBar from '@/pages/components/customTabbar.vue';
	export default {
		components: {
			CustomTabBar
		},
		data() {
			return {
				longitude: 0,
				latitude: 0,
				altitude: 0,
				distance: 0,
				tracking: false,
				paused: false,
				path: [], // 存储移动轨迹
				polyline: [], // 生成移动轨迹
				lastLocation: null, // 存储上一个有效位置
				isStart: false, // 是否满足调用开始接口
				isEnd: false, // 是否满足调用结束接口
				startId: null, // ID获取
				startTime: 0, // 记录开始时间
				// elapsedTime: 0, // 添加经过时间字段,以秒为单位
				// timerId: null // 定时器ID
				locationChangeHandler: null,
				inspectionSortKey: 0,
				showInfo: true,
				mapHeight: 520,
				systemInfo: null,
				markers: [],
			};
		},
		mounted() {
			this.systemInfo = uni.getSystemInfoSync();
			this.mapHeight = this.systemInfo.platform === 'ios' ? 520 : 480; // 根据需要调整高度
		},
		onLoad() {
			this.restoreState();
			this.requestLocationPermission();
		},
		onUnload() {
			// this.saveState();
		},
		onHide() {
			this.saveState();
		},
		methods: {
			requestLocationPermission() {
				uni.authorize({
					scope: 'scope.userLocationBackground',
					success: () => {
						this.getCurrentLocation();
					},
					fail: () => {
						uni.showModal({
							title: '提示',
							content: '请授权获取位置信息,否则无法使用该功能',
							showCancel: false
						});
					}
				});
			},
			getCurrentLocation() {
				uni.getLocation({
					type: 'gcj02',
					altitude: true,
					success: (res) => {
						if (res && res.longitude !== undefined && res.latitude !== undefined) {
							this.longitude = res.longitude;
							this.latitude = res.latitude;
							this.altitude = (res.altitude || 0).toFixed(2);
							this.lastLocation = {
								longitude: res.longitude,
								latitude: res.latitude
							};
						} else {
							console.error("获取位置返回值不正确", res);
						}
					},
					fail: (err) => {
						console.error("获取位置失败", err);
					}
				});
			},
			startTracking() {
				wx.startLocationUpdateBackground({
					type: 'wgs84',
					success: (res) => {
						// console.log(res, 'startLocationUpdateBackgroundstartLocationUpdateBackground')
						if (this.tracking) return;
						this.isStart = true;
						this.tracking = true;
						this.paused = false;
						this.path = [];
						this.polyline = [];




						this.getLocationAndUpdate();








						// this.timerId = setInterval(() => {
						// 	this.elapsedTime++;
						// }, 1000);
					},
					fail: (err) => {
						console.error('开始后台定位失败', err);
					}
				});
			},
			getLocationAndUpdate() {
				this.locationChangeHandler = async (res) => {
					const {
						longitude,
						latitude,
						altitude,
						accuracy,
					} = res;
					console.log(res, 'resresresres')
					this.altitude = (altitude || 0).toFixed(2);
					if (this.isSignificantChange(longitude, latitude) && this.startId !== null && accuracy < 40) {
						this.inspectionSortKey++
						this.path.push({
							longitude,
							latitude,
							height: (altitude || 0).toFixed(2),
							inspectionSortKey: this.inspectionSortKey
						});
						this.updatePolyline();
						this.calculateDistance();
						this.longitude = longitude;
						this.latitude = latitude;
						this.lastLocation = {
							longitude,
							latitude
						}; // 更新上一个位置
					} else if (this.startId === null) {
						if (this.isStart) {
							await this.reverseGeocode(longitude, latitude, res);
						}
					}
				};
				wx.onLocationChange(this.locationChangeHandler);
			},
			async stopTracking() {
				if (!this.tracking) return;
				this.tracking = false;
				this.paused = true;




				// if (this.timerId !== null) {
				// 	clearInterval(this.timerId);
				// 	this.timerId = null;
				// }
				await this.getLocationAndUpdate();




				if (this.locationChangeHandler) {
					wx.offLocationChange(this.locationChangeHandler);
					this.locationChangeHandler = null; // 清空引用
				}
			},
			resumeTracking() {
				wx.startLocationUpdateBackground({
					type: 'wgs84',
					success: (res) => {
						if (this.tracking) return;
						this.tracking = true;
						this.paused = false;
						this.getLocationAndUpdate();
					},
					fail: (err) => {
						console.error('开始后台定位失败', err);
					}
				});








				// this.timerId = setInterval(() => {
				// 	this.elapsedTime++;
				// }, 1000);
			},
			confirmTerminateTracking() {
				uni.showModal({
					title: '确认终止',
					content: '请确认本次轨迹定位是否结束',
					success: (res) => {
						if (res.confirm) {
							this.isEnd = true;
							this.isStart = false;
							uni.getLocation({
								type: 'gcj02',
								altitude: true,
								success: async (res) => {
									const {
										longitude,
										latitude,
										altitude
									} = res;
									await this.reverseGeocode(longitude, latitude, res);
									this.markers.push({
										id: 2,
										latitude,
										longitude,
										iconPath: '/static/road-stop.png', // 开始图标路径
										width: 60,
										height: 60,
									});




									setTimeout(() => {
										this.markers = [];
									}, 2000)
								},
								fail: (err) => {
									console.error("获取位置失败", err)
								}
							});
						}
					}
				});
			},
			updatePolyline() {
				this.polyline = [{
					points: this.path,
					color: "#FF0000DD",
					width: 5,
					dottedLine: false
				}];
			},
			calculateDistance() {
				if (this.path.length < 2) return;
				const lastPoint = this.path[this.path.length - 2];
				const currentPoint = this.path[this.path.length - 1];
				const distance = this.getDistance(lastPoint, currentPoint);
				this.distance += distance;
			},
			getDistance(point1, point2) {
				const radLat1 = (point1.latitude * Math.PI) / 180.0;
				const radLat2 = (point2.latitude * Math.PI) / 180.0;
				const a = radLat1 - radLat2;
				const b = (point1.longitude * Math.PI) / 180.0 - (point2.longitude * Math.PI) / 180.0;
				let s = 2 * Math.asin(
					Math.sqrt(
						Math.pow(Math.sin(a / 2), 2) +
						Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
					)
				);
				s = s * 6378137.0; // 地球半径
				s = Math.round(s * 10000) / 10000.0;
				return s;
			},
			isSignificantChange(longitude, latitude) {
				if (!this.lastLocation) return true;
				const deltaLongitude = Math.abs(longitude - this.lastLocation.longitude);
				const deltaLatitude = Math.abs(latitude - this.lastLocation.latitude);
				return deltaLongitude > 0 || deltaLatitude > 0;
			},
			regionChange(e) {
				// console.log('region change', e);
			},
			async reverseGeocode(longitude, latitude, data) {
				// const key = '6b7fd0d3e900c4be1279cb705e103953';
				const key = '2f2f22f5109d0b452c754da47fd45f6d';
				const url = `https://restapi.amap.com/v3/geocode/regeo?location=${longitude},${latitude}&key=${key}`;
				// const url = `https://restapi.amap.com/v3/geocode/regeo?location=119.146617,33.618107&key=${key}`;
				try {
					let params;
					const res = await new Promise((resolve, reject) => {
						uni.request({
							url: url,
							success: (res) => {
								resolve(res);
							},
							fail: (err) => {
								reject(err);
							}
						});
					});
					if (this.isStart) {
						const formattedDate = getFormattedDate();
						if (!this.startId) this.startTime = formattedDate;
						const {
							city,
							province,
							district,
							township
						} = res.data.regeocode.addressComponent;
						params = {
							id: this.startId,
							longitude,
							latitude,
							city: Array.isArray(city) ? null : city,
							province,
							district,
							township,
							address: res.data.regeocode.formatted_address,
							height: (data.altitude || 0).toFixed(2),
							inspectionSortKey: this.inspectionSortKey,
							inspectionStartTime: formattedDate
						};
						const startRes = await startInspection(params);
						this.startId = startRes.data;
						this.markers.push({
							id: 1,
							latitude,
							longitude,
							iconPath: '/static/road-start.png', // 开始图标路径
							width: 60,
							height: 60,
						});
						if (this.path.length === 0) {
							this.inspectionSortKey++;
							this.path.push({
								longitude,
								latitude,
								height: (data.altitude || 0).toFixed(2),
								inspectionSortKey: this.inspectionSortKey
							});
						}
						this.isStart = false;
					}




					if (this.isEnd) {
						params = {
							id: this.startId,
							userInspectionDtoList: this.path,
							totalDistance: this.distance.toFixed(2),
						}
						await endInspection(params);
						this.paused = false;
						this.tracking = false;
						this.isEnd = false;
						this.distance = 0;
						this.altitude = 0;
						this.startId = null;
						this.path = [];
						this.polyline = [];
						this.inspectionSortKey = 0;
						uni.removeStorageSync('trackingState');
						wx.stopLocationUpdate({
							success: () => {
								console.log('成功停止后台定位');
								if (this.locationChangeHandler) {
									wx.offLocationChange(this.locationChangeHandler);
									this.locationChangeHandler = null; // 清空引用
								}
							},
							fail: (err) => {
								console.error('停止后台定位失败', err);
							}
						});
					}
				} catch (e) {
					console.error("反向地理编码请求失败", e);
				}
			},
			saveState() {
				const state = {
					startId: this.startId, // 开始运动ID
					startTime: this.startTime, // 开始日期
					isStart: this.isStart, // 开始状态
					tracking: this.tracking, // 按钮显示状态
					paused: this.paused, // 按钮显示状态
					isEnd: this.isEnd, // 是否满足结束状态
					lastLocation: this.lastLocation, // 存储上一个有效位置
					path: this.path,
					hideTime: getFormattedDate(),
					inspectionSortKey: this.inspectionSortKey,




					// distance: this.distance,
					// polyline: this.polyline,
				};
				uni.setStorageSync('trackingState', state);
			},
			restoreState() {
				this.constructor();
				const state = uni.getStorageSync('trackingState');
				if (state) {
					if (state.startId) {
						const currentDate = new Date();
						const pastDateObj = new Date(state.hideTime);
						const timeDifference = currentDate - pastDateObj;
						if (timeDifference > 240000) {
							uni.showModal({
								title: '提示',
								content: '检测到您上次的跑步还没有结束,是否继续?',
								success: (res) => {
									if (res.confirm) {
										const {
											startId,
											startTime,
											isStart,
											tracking,
											paused,
											isEnd,
											lastLocation,
											path,
											inspectionSortKey
										} = state;
										this.startId = startId; // 开始运动ID
										this.startTime = startTime; // 开始日期
										this.isStart = isStart; // 开始状态
										this.tracking = tracking; // 按钮显示状态
										this.paused = paused; // 按钮显示状态
										this.isEnd = isEnd; // 是否满足结束状态
										this.lastLocation = lastLocation; // 存储上一个有效位置
										this.path = path;
										this.inspectionSortKey = inspectionSortKey;
										// if (tracking) {
										// 	const pastDate = new Date(startTime); // 过去的日期
										// 	const currentDate = new Date(); // 当前日期
										// 	// 计算时间差(以秒为单位)
										// 	this.elapsedTime = Math.floor((currentDate - pastDate) /
										// 		1000); // 转换为秒
										// }




										// this.timerId = setInterval(() => {
										// 	this.elapsedTime++;
										// }, 1000);
									} else if (res.cancel) {
										this.confirmTerminateTracking()
									}
								}
							});
						}
					}
				}
			},
			constructor() {
				this.longitude = 0;
				this.latitude = 0;
				this.altitude = 0;
				this.distance = 0;
				this.tracking = false;
				this.paused = false;
				this.path = []; // 存储移动轨迹
				this.polyline = []; // 生成移动轨迹
				this.lastLocation = null; // 存储上一个有效位置
				this.isStart = false; // 是否满足调用开始接口
				this.isEnd = false; // 是否满足调用结束接口
				this.startId = null; // ID获取
				this.startTime = 0; // 记录开始时间
				this.inspectionSortKey = 0;
				// this.elapsedTime = 0; // 添加经过时间字段,以秒为单位
				// this.timerId = null; // 定时器ID
			},
			toggleInfo() {
				this.showInfo = !this.showInfo; // 切换信息的显示状态
				if (this.showInfo) {
					this.mapHeight = this.systemInfo.platform === 'ios' ? 520 : 480; // 根据需要调整高度
				} else {
					this.mapHeight = this.systemInfo.platform === 'ios' ? 600 : 550; // 根据需要调整高度
				}
			},
		}
	};
</script>




<style>
	.container {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}




	.controls {
		margin-top: 20px;
		display: flex;
		flex-direction: row;
		justify-content: center;
		gap: 15px;
	}




	.controls image {
		width: 50px;
		height: 50px;
		cursor: pointer;
	}




	.toggle-info {
		width: 100%;
		padding-bottom: 10px;
		text-align: center;
	}




	.toggle-info image {
		width: 25px;
		height: 25px;
	}




	.banner {
		width: 100%;
	}




	.info {
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 0 30px;
	}




	.info text {
		flex: 1
	}




	.info text:last-child {
		text-align: right;
	}
</style>
回答关注问题邀请回答
收藏
登录 后发表内容