前言
最近一段时间在研究微信小程序的开发,相比于原生app来说,确实上手要容易不少,也能够提升项目的开发速度。但与此同时,小程序开发中也存在一些“坑”需要我们注意,因此我想记录一下自己在微信小程序开发中遇到的一些问题,方便日后查阅。
微信小程序中的权限介绍
大家如果接触过移动开发应该都知道,涉及到一些用户敏感信息的操作(比如定位、访问本地文件等)都是需要用户授权的,在Android开发中,我们需要提前将这些权限配置到项目中,Android 6.0及以上版本还需要通过代码来动态申请部分权限。微信小程序也是如此,我们平时使用小程序时可能也见到过进行某个操作之前弹出授权框的场景,需要用户手动授权后才能进行下一步操作。接下来我简单介绍一下微信小程序中的授权机制,基本上都是参考自官方文档,大家也可以直接阅读官方文档,了解更详细的介绍。
微信小程序中所有需要授权的操作和对应的权限关系如下表:
权限说明 | 对应接口 |
---|---|
scope.userInfo(获取用户信息) | wx.getUserInfo |
scope.userLocation(获取地理位置信息) | wx.getLocation, wx.chooseLocation |
scope.userLocationBackground(后台定位) | wx.startLocationUpdateBackground |
scope.record(录音) | wx.startRecord |
scope.writePhotosAlbum(保存文件到相册) | wx.saveImageToPhotosAlbum, wx.saveVideoToPhotosAlbum |
scope.camera(调用系统摄像头) | camera组件 |
scope.werun(获取微信运动步数) | wx.getWeRunData |
scope.address(获取收货地址) | wx.chooseAddress |
scope.invoiceTitle(获取发票抬头) | wx.chooseInvoiceTitle |
scope.invoice(获取发票信息) | wx.chooseInvoice |
和Android开发不同的是,微信小程序不需要通过代码来申请权限,在调用上述接口时会自动弹出授权框让用户进行授权。但是需要注意的是,一旦用户选择了拒绝或者允许该权限,那么授权关系就会被记录在后台,直到用户主动删除小程序,之后再次调用该API时都不会再弹出权限申请框。如果用户拒绝了授权,之后调用该API都会失败(执行对应的fail回调),因此我们要根据实际的开发场景处理好用户拒绝授权的情况,如果API的返回值是必需的,那么我们就要在用户拒绝授权后引导用户手动开启该权限从而保证API的成功调用。下面我就以获取位置信息API(wx.getLocation
)为例,介绍一下如何处理用户拒绝授权的情况。
如何处理用户拒绝授权的情况
下面就以调用wx.getLocation()
获取用户位置信息为例介绍一下用户拒绝授权的处理方式。scope.userLocation和scope.userLocationBackground权限比较特殊,需要在小程序根目录下的app.json文件中配置位置信息的用途说明,如下所示:
"permission": {
"scope.userLocation": {
"desc": "您的位置信息将用于定位"
}
}
其中的"desc"就是展示给用户的位置信息用途说明,之后在调用wx.getLocation()
时就会弹出如下授权提示框。
当然具体的弹窗样式会和手机系统以及微信版本有关,但是弹窗展示的内容是不变的。根据此前的介绍,当用户点击了“拒绝”后,此后调用wx.getLocation()
时都会执行fail失败回调,无法获取用户位置信息,因此我们可以在fail回调中判断用户是否拒绝了授权,对于用户拒绝授权的情况调用wx.openSetting跳转小程序设置页面,引导用户手动开启该权限。判断用户是否拒绝授权的方式有以下两种:
- 通过
wx.getSetting
获取用户当前的授权状态来判断
我们可以在wx.getLocation
的fail回调中调用wx.getSetting
来获取用户当前授权状态,从而判断用户是否拒绝了定位权限,示例代码如下:
wx.getLocation({
success: res => {
console.log(res);
},
fail: e => {
console.log(e);
// 判断用户是否拒绝了授权
wx.getSetting({
success: res => {
if (typeof(res.authSetting['scope.userLocation']) != 'undefined' && !res.authSetting['scope.userLocation']) {
// 用户拒绝了授权,跳转设置页面
}
}
});
}
});
大家可能会有疑问,为什么不能直接根据!res.authSetting['scope.userLocation']
来判断呢?这是因为wx.getSetting
的返回值中只会出现小程序已经向用户申请过的权限,换句话说,只有此前弹出过权限申请框,并且用户点击了“拒绝”或者“允许”,之后在wx.getSetting
的success回调中才能获取到res.authSetting['scope.userLocation']
的值。我们可以考虑这样一种情况,当调用wx.getLocation
弹出权限申请框后,用户没有点击“拒绝”或“允许”按钮,而是通过系统返回键关闭了权限申请框,之后也会执行fail回调,但是此时res.authSetting
中是没有scope.userLocation
属性的,同样满足!res.authSetting['scope.userLocation']
条件,这种情况跳转设置页面是不太符合逻辑的。还有一个重要的原因,wx.openSetting
同样只会显示小程序已经向用户申请过的权限,因此这种情况即使跳转了设置页面,页面中也不会显示出该权限,当然也无法引导用户手动开启了。
- 通过返回的错误信息来判断
我们也可以通过wx.getLocation
的fail回调返回的错误信息来判断用户是否拒绝了定位权限。拒绝授权返回的错误信息官方文档中并没有写,我们只能通过测试来得到,经过我个人的测试,拒绝定位权限返回的错误信息如下:
微信开发者工具:getLocation:fail auth deny
Android手机用户点击授权框中的“拒绝”按钮:getLocation:fail:auth denied
Android手机用户已拒绝授权,之后每一次调用定位API:getLocation:fail auth deny
苹果手机用户点击授权框中的“拒绝”按钮:getLocation:fail auth deny
苹果手机用户已拒绝授权,之后每一次调用定位API:getLocation:fail authorize no response
这里就不得不吐槽一下了,其实一开始我只在开发工具上进行了测试,错误信息只考虑了一种,后来通过测试人员反馈我才知道原来这也和手机的系统有关,之后我测试了录音的API,也是同样的情况。我确实不能理解为什么同样的情况,不同手机系统返回的错误信息还要有所区别,不过既然小程序是这样设计的,可能也有它的道理吧,如果大家有什么见解或者是我的测试有什么不对的地方欢迎提出。
好了,回到正题,对于用户拒绝定位权限的情况,我们需要同时考虑这三种错误信息,示例代码如下:
wx.getLocation({
success: res => {
console.log(res);
},
fail: e => {
console.log(e);
// 判断用户是否拒绝了授权
if (e.errMsg == 'getLocation:fail:auth denied' || e.errMsg == 'getLocation:fail auth deny' || e.errMsg == 'getLocation:fail authorize no response') {
// 用户拒绝了授权,跳转设置页面
}
}
});
由于授权失败对应的错误信息是不唯一的,我也不清楚官方在之后是否会修改,因此我不是很推荐使用这种方式来判断。
之后对于用户拒绝授权的情况就可以调用wx.openSetting
打开设置页面,引导用户手动开启权限了。
// 用户拒绝了授权,跳转设置页面
wx.openSetting({
success: res => {
if (res.authSetting['scope.userLocation']) {
// 授权成功,重新定位
wx.getLocation({
success: res => {}
});
} else {
// 没有允许定位权限
wx.showToast({
title: '您拒绝了定位权限,将无法使用XX功能',
icon: 'none'
});
}
}
});
在wx.openSetting
的success回调中可以判断用户是否已授权,并根据实际开发场景执行相关逻辑,这里为了简单演示,对于用户已授权的情况重新调用wx.getLocation
进行定位;对于用户依然没有授权的情况,弹出一个Toast提示。
需要注意,官方文档中明确指出从2.3.0版本开始,当用户发生点击行为后才可以跳转打开设置页,不允许在用户无任何操作的情况下进行跳转,对于该限制,我们可以使用弹出一个模态框,当用户点击确定按钮时再跳转设置页面。最后附上定位的完整代码如下:
wx.getLocation({
success: res => {
console.log(res);
},
fail: e => {
console.log(e);
// 判断用户是否拒绝了授权
wx.getSetting({
success: res => {
if (typeof(res.authSetting['scope.userLocation']) != 'undefined' && !res.authSetting['scope.userLocation']) {
// 用户拒绝了授权
wx.showModal({
title: '提示',
content: '您拒绝了定位权限,将无法使用XX功能',
success: res => {
if (res.confirm) {
// 跳转设置页面
wx.openSetting({
success: res => {
if (res.authSetting['scope.userLocation']) {
// 授权成功,重新定位
wx.getLocation({
success: res => {}
});
} else {
// 没有允许定位权限
wx.showToast({
title: '您拒绝了定位权限,将无法使用XX功能',
icon: 'none'
});
}
}
});
}
}
});
}
}
});
});
这里使用的是第一种方式判断用户是否拒绝了授权,使用错误信息来判断也是一样的,就不展示了。
最后展示一下运行效果:
总结
由于微信小程序只会向用户申请一次授权,之后的授权记录会一直保留,并且用户拒绝授权会导致相关API调用失败,因此我们需要考虑用户拒绝授权的情况。本文以获取位置信息的API为例,介绍了微信小程序中如何处理这种情况,对于其他权限来说逻辑也是类似的,主要包括两个步骤:
1.判断用户是否拒绝了授权。
2.对于用户拒绝授权的情况,跳转小程序设置页面引导用户手动授权。
具体的处理方式还是要考虑实际项目的功能,如果该权限涉及到的功能不是必需的(比如定位获取到的位置信息可以为空),那么当然可以不用做这些处理,我这里只是提供了一种思路,大体的判断逻辑是不会变的。
此外如果我有什么分析得不对的地方欢迎大家提出指正。