最近开发一款微信餐饮外卖配送小程序,通过微信公众号和微信小程序实现菜单展示、线上订餐、预约点餐、外卖配送等功能。在开发过程中遇到一个需求,需要小程序持续定位,然后向后台发送实时位置,实时更新配送行程轨迹。
能力体验之"后台持续定位"接口的使用与踩坑之旅开始了。
一、申请开通相关接口
进入小程序后台管理页面找到开发-开发管理-接口设置,进行申请一般小程序都会申请开通 wx.getLocation 这个接口,然后我们会在第一次进入小程序时弹出让我们授权位置信息获取。但是为了实现微信出在后台时我们还需要额外申请其他几个接口,wx.onLocationChange用来监听实时地理位置变化事件;wx.startLocationUpdate 开启小程序进入前台时接受位置信息(如果开通前后台的话,一般会用的前后台的,建议开通,服务其他页面需求);wx.startLocationUpdateBackground 开启小程序进入前后台时均接受位置信息。
二、代码实现
权限配置:
json
"permission" : { //小程序接口权限相关设置
"scope.userLocation" : { //获取用户位置信息的权限,需要配置
"desc" : "获取用户位置,用于配送轨迹实时更新"
}
},
"requiredBackgroundModes" : [ "location" ], //申明需要后台运行的能力,类型为数组,目前支持audio后台音乐播放,location 后台定位
"requiredPrivateInfos" : [
"getLocation",
"startLocationUpdate",
"onLocationChange",
"startLocationUpdateBackground"
], //在代码中使用申请的地理位置接口 需要在此配置,未申请接口需先申请开通
交互逻辑:
javascript
// 获取微信授权弹框
getWxLocation() {
return new Promise((resolve, reject) => {
console.log("定位中...");
// 开启定位追踪
// wx.startLocationUpdateBackground 后台获取位置接口
wx.startLocationUpdateBackground({
success: (res) => {
console.log("开启定位追踪", res);
// 监听位置变化
wx.onLocationChange((data) => {
console.log(data, '位置变化上传');
//获取当前时间
var currentTime = new Date().getTime();
//获取上次执行的时间
var oldTime = wx.getStorageSync("oldTime");
//判断并且间隔时间
// 五分钟执行一次
if (currentTime - oldTime > 1000 * 60 * 5) {
//缓存当前执行的时间
wx.setStorageSync("oldTime", currentTime);
// wx.showToast({title:'上传位置信息',icon:'none'})
//将位置信息上传后台的自己的代码
this.uploadLocation(data);
}
});
resolve();
},
fail: (err) => {
console.log("获取当前位置失败", err);
reject();
},
});
});
},
// 上传位置信息
uploadLocation(data) {
console.log("地址", data);
locationUtils
.getLocationRegeo(`${data.longitude},${data.latitude}`)
.then((addressInfo) => {
const addressComponent = addressInfo.addressComponent;//经纬度转码后信息
let requestData = {} //参数需要上传后台的参数具体根据自己的业务写
methodName(requestData).then((res) => {}); //上传接口
});
},
// 停止获取位置信息
stopUploadLocation() {
wx.stopLocationUpdate({ success: (res) => { }, fail: (error) => { } });
wx.offLocationChange();
},
三、遇到的问题
wx.onLocationChange能否加个精度选项和上报频率选项?
用小程序测试了下wx.onLocationChange,坐那没动,console.log显示每隔一秒钟就会回调一次,相当于一秒钟回调一次。
建议:
可以加个位置变化精度选项,比如移动超过1米,再回调;
可以加个回调频率,比如可以选择1ms、1min、5min等;
如果有以上两个选项感觉是不是可以能更省电?客户也更愿意使用,否则客户会以费电抵触使用,导致该API实际用不起来;
四、思考&解决方案
微信团队在公众号里说"满足线路导航、路线记录等服务场景下,小程序需要长时间持续定位来提供服务",但在实际情况中是,小程序很难实现所谓的"长时间"(有时甚至不到5分钟)。这样就会导致我们记录的数据还没来得及上传就丢失了。当我发现这个问题,第一时间是抱怨为什么小程序销毁前不能给我们返回一个提醒。但思考后才理解:微信APP自身都不能确保"长时间"在后台运行,还怎么样保证小程序的"长时间"运行呢?(如被一些软件清理了)既然无法避免突发性关闭,又不宜频繁上传地点数据。那么,我们就只能用到缓存了!以实现类似断线重连的功能。
交互逻辑:
javascript
var points_map = [ ] // 实时绘制地图
var points_yun = [ ] // 云开发需要的格式
var point
Page({
data: {
polyline: [{
points: points_map,
color: '#FFA500',
width: 3
}],
},
onLoad: function (options) {
var that = this
wx.getStorage({
key: 'TimeStamp',
success(res) {
console.log('有缓存')
wx.getStorage({
key: 'points_yun',
success(res) { points_yun = res.data }
})
wx.showModal({
content: '检测到您有一个未完成的配送记录!',
cancelText: '不保存',
cancelColor: '#DC143C',
confirmText: '继续配送',
confirmColor: '#228B22',
success(k) {
if (k.confirm) {
console.log('用户点击确定-继续配送')
that.setData({ TimeStamp: res.data })
that.GoContinue()
} else if (k.cancel) {
console.log('用户点击取消-不保存')
wx.removeStorage({ key: 'TimeStamp' }) //删缓存
wx.removeStorage({ key: 'points_yun' })
points_yun = []
}
}
})
}, fail(res) {
console.log('无缓存')
points_yun = []
}
})
},
Go: function () { // 开始配送(首次)
var that = this
wx.startLocationUpdateBackground({
success(res) {
var TimeStamp = (new Date()).valueOf()
that.GetNowGeo()
wx.setStorage({ //设置缓存(TimeStamp)
key: "TimeStamp",
data: TimeStamp
})
},
fail() {
// 这里弹窗引导用户授权使用地理位置
}
})
},
GoContinue: function () { // 开始配送(再续)
var that = this
wx.startLocationUpdateBackground({
success(res) {
that.GetNowGeo()
},
fail() {
// 这里弹窗引导用户授权使用地理位置
}
})
},
End: function () {
var that = this
wx.stopLocationUpdate()
clearInterval(this.data.setInter)
wx.showModal({
title: '',
content: '是否要上传数据?',
success(res) {
if (res.confirm) {
that.updateGeo()
}
}
})
},
GetNowGeo: function () {
var that = this
wx.onLocationChange(function (res) {
point = { latitude: res.latitude, longitude: res.longitude }
})
this.data.setInter = setInterval(function () {
if (points_map.length == 0) {
points_yun.push([])
}
points_map.push(point); //画地图
that.setData({
'polyline[0].points': points_map
})
var n = [point.longitude, point.latitude] // 云开发数据库需要的格式
var r = points_yun.length - 1
points_yun[r].push(n)
wx.setStorage({ // 设置缓存(路程数据)
key: "points_yun",
data: points_yun
})
}, 6000);
},
updateGeo: function () {
var that = this
wx.showLoading({
title: '数据上传中',
})
db.collection('patrol_geo').add({ // 云开发上传
data: {
location: {
type: 'MultiLineString',
coordinates: points_yun
}
},
success: res => {
wx.showToast({
title: '上传成功'
})
wx.removeStorage({ key: 'TimeStamp' }) // 清理缓存
wx.removeStorage({ key: 'points_yun' })
},
fail: res => {
wx.showToast({
title: '上传失败,请重新操作!'
})
console.log(res)
}
})
},
})