我使用uniapp开发支付宝+微信小程序,websocket中用了stomp.js做订阅,用支付宝真机测试websocket时发现消息监听不到了,导致接口唤起了,但是收不到后续的回调。微信IDEA与支付宝IDEA没问题,微信真机也没问题,只有支付宝真机有问题。
后续调试发现,因为页面跳转导致uni.onSocketMessage监听不到消息。
于是还原场景:
在App.vue中写了全局的websocket,小程序启动页为login登录页面,登录成功后跳转首页,使用了switchTab跳转,支付宝小程序说switchTab会关闭其他页面,我发现用reLaunch或者redirectTo页会出现这样的问题
应该是支付宝真机上页面栈与全局的websocket有冲突,具体哪里冲突我也没有深入了解。
如果你也和我一样的问题的话,可以在switchTab、reLaunch、redirectTo后的页面手动关闭一次websocket,但是我发现用uni.closeSocket无法重启websocket。于是我用了stomp的订阅关闭。别的页面需要调用到App.vue中的属性,我只能在App.vue中定义一个全局变量globleData,在别的页面调用。
上代码:
App.vue
<script>
export default {
onLaunch: function() {
this.initSocket()
},
onShow: function() {},
onHide: function() {},
globalData: {
stompClient: '', //后续页面需要用到
restart: '' //因为回到登录页重启会出现2个监听,回调会重复,所以需要手动连接
},
methods: {
//获取Stmop消息推送
initSocket() {
var that = this;
// socket是否连接
var socketConnected = false;
// 待发送的消息队列
var messageQueue = [];
// 是否断线重连
var reconnect = true;
var openid = uni.getStorageSync('token');
// // 发送消息
function sendSocketMessage(msg) {
console.log(msg);
//如果socket已连接则发送消息
if (socketConnected) {
uni.sendSocketMessage({
data: msg
})
} else {
// socket没有连接将消息放入队列中
messageQueue.push(msg);
}
}
// 关闭连接
function close() {
if (socketConnected) {
uni.closeSocket()
}
}
// 符合WebSocket定义的对象
var ws = {
send: sendSocketMessage,
close: close
}
// 创建一个 WebSocket 连接
function connect() {
console.log(uni.getStorageSync('token'))
openid = uni.getStorageSync('token');
uni.connectSocket({
url: '你的websocket地址',
header: {
'content-type': 'application/json'
},
success(res) {
console.log(res)
}
})
}
connect()
// 监听 WebSocket 连接打开事件
uni.onSocketOpen(function(res) {
console.log("WebSocket 连接成功")
socketConnected = true;
ws.onopen();
//连接成功后,将队列中的消息发送出去
})
// 监听 WebSocket 接受到服务器的消息事件
uni.onSocketMessage(function(res) {
ws.onmessage(res);
})
// 监听 WebSocket 错误事件
uni.onSocketError(function(res) {
console.log("WebSocket 错误事件")
})
// 监听 WebSocket 连接关闭事件
uni.onSocketClose(function(res) {
console.log("WebSocket 连接关闭")
socketConnected = false;
// 断线重连
if (reconnect) {
connect();
}
})
var Stomp = require('common/SDK/stomp.min.js').Stomp;
/**
* 定期发送心跳或检测服务器心跳
* The heart-beating is using window.setInterval() to regularly send heart-beats and/or check server heart-beats.
* 可看stomp.js的源码(195,207,489行),由于小程序没有window对象,所以我们要调用小程序的定时器api实现
*/
Stomp.setInterval = function(interval, f) {
return setInterval(f, interval);
};
// 结束定时器的循环调用
Stomp.clearInterval = function(id) {
return clearInterval(id);
};
var stompClient = Stomp.over(ws);
that.$scope.globalData.stompClient = stompClient
stompClient.connect({}, function(callback) {
console.log(uni.getStorageSync('token'))
stompClient.subscribe('/user/' + openid + '/equipment', function(body, headers) {
console.log('收到webSocket回调', body);
var res = JSON.parse(body.body);
});
})
},
},
}
</script>
home.vue SwichTab的首页
<script>
export default{
onLoad() {
getApp().globalData.stompClient.disconnect( function(){
console.log('订阅连接关闭') //此处关闭订阅,websocket会重新连接,disconnect是stomp的关闭连接
})
getApp().globalData.restart = true //因为回到登录页不会重新连接,得手动,所以给个状态通知登录页
},
}
</script>
Login.vue 项目的启动页
<script>
export default{
onLoad(){
getApp().globalData.stompClient.disconnect( function(){
console.log('订阅连接关闭') //此处关闭连接,websocket不会再打开,因为是首页redirectTo过来的。所以需要手动再开一次,不需要再调onSocketMessage,会出现2次重复监听,所以只需要手动打开Socket连接就可以了
if(getApp().globalData.restart){
uni.connectSocket({
url: '你的websocket地址',
header: {
'content-type': 'application/json'
},
success(res){
console.log(res)
}
})
}
})
}
}
</script>