> 技术文档 > 微信小程序中使用WebSocket通信_微信小程序websocket用法

微信小程序中使用WebSocket通信_微信小程序websocket用法

一、在utils文件夹下新建websocket.js文件,用来封装websocket的连接以及生命周期非法:

const app = getApp()// 域名地址(项目实地址)const Host = \'wss://fczd.hkbtwx.com/websocket/ws/\'; // Socket连接成功var socketOpen = false;// Socket关闭var socketClose = false;// 消息队列var socketMsgQueue = []; // 判断心跳变量var heart = null;// 心跳失败次数var heartBeatFailCount = 0;// 终止心跳var heartBeatTimeout = null;// 终止重连var connectSocketTimeout = null; var webSocket = { // 连接Socket connectSocket:function(options) { if (socketOpen) return // wx.showLoading({ // title: \'Socket连接中...\', // mask: true // }); socketOpen = false; socketClose = false; socketMsgQueue = []; let url = Host + app.globalData.userInfo.code wx.connectSocket({ url: url, header:{ \'content-type\': \'application/json\' }, success:function(res) { console.log(\'链接成功\') if (options) { options.success && options.success(res); } }, fail:function(res) { if (options) { options.fail && options.fail(res); } } }) }, // 发送消息 sendSocketMessage:function(options) { if (socketOpen) { wx.sendSocketMessage({ data: options.msg, success: function(res) { if (options) { options.success && options.success(res); } }, fail: function(res) { if (options) { options.fail && options.fail(res); } } }) } else { socketMsgQueue.push(options.msg) } }, // 关闭Socket closeSocket: function(options) { if (connectSocketTimeout) { clearTimeout(connectSocketTimeout); connectSocketTimeout = null; }; socketClose = true; this.stopHeartBeat(); wx.closeSocket({ success: function(res) { if (options) { options.success && options.success(res); } }, fail: function(res) { if (options) { options.fail && options.fail(res); } } }) }, // 收到消息 onSocketMessageCallback: function(msg) {}, // 开始心跳 startHeartBeat: function() { heart = true; this.heartBeat(); }, // 正在心跳 heartBeat: function() { var that = this; if (!heart) { return; }; that.sendSocketMessage({ msg: JSON.stringify({ // 与后端约定,传点消息,保持链接 \'message\': \'ping\', \'toUserId\': app.globalData.userInfo.code }), success: function(res) { if (heart) { heartBeatTimeout = setTimeout(() => { that.heartBeat(); }, 8000); } }, fail: function(res) { if (heartBeatFailCount > 2) { that.connectSocket(); }; if (heart) { heartBeatTimeout = setTimeout(() => { that.heartBeat(); }, 8000); }; heartBeatFailCount++; } }); }, // 结束心跳 stopHeartBeat: function() { heart = false; if (heartBeatTimeout) { clearTimeout(heartBeatTimeout); heartBeatTimeout = null; }; if (connectSocketTimeout) { clearTimeout(connectSocketTimeout); connectSocketTimeout = null; } }}; // 监听WebSocket打开连接wx.onSocketOpen(function(res) { wx.hideLoading(); // 如果已经关闭socket if (socketClose) { webSocket.closeSocket(); } else { socketOpen = true for (var i = 0; i  { webSocket.connectSocket(); }, 10000); }}); module.exports = webSocket;

二、在需要接收websocket消息的页面引入:

const app = getApp()const $api = require(\"../../utils/api.js\")const myRequest = require(\'../../utils/request.js\')let navBarTitleText = \'\'const WebSocket = require(\'../../utils/websocket.js\')Page({ data: { isBindWx: false, showLoginPop: false, userInfo: {}, statisticsData: {}, //统计数据 avatarUrl: \'\', // 登录页面相关参数 avHeight: \'\', navTop: \'\', navHeight: \'\', loading: false, isOut: false, isLogin: false, message: \'\', messageCode: \'\' }, onLoad: function(options) { if (wx.getStorageSync(\'isOut\')) { this.setData({ isOut: true }) wx.setStorageSync(\'isOut\', \'\') } }, onShow: function() { if (typeof this.getTabBar === \'function\' && this.getTabBar()) { this.getTabBar().setData({ selected: 0, show: app.globalData.isLogin }) } // 打开调试 wx.setEnableDebug({ enableDebug: false }) wx.setNavigationBarTitle({ title: navBarTitleText }) this.setData({ isBindWx: app.globalData.isBindWx, userInfo: app.globalData.userInfo, avatarUrl: app.globalData.avatarUrl, navHeight: app.globalData.navHeight, navTop: app.globalData.navTop, loading: false, isLogin: app.globalData.isLogin, navBarTitleText: app.globalData.isLogin ? \'首页\' : \'登录\' }) if (app.globalData.isLogin) { this.getStatistics() } // 设置接收消息回调 WebSocket.onSocketMessageCallback = this.onSocketMessageCallback }, // 用户信息 onUserInfo() { let that = this if (app.globalData.avatarUrl) { wx.navigateTo({ url: \'/pages/userInfo/index\' }) return } wx.getUserProfile({ desc: \'用于展示用户信息\', success: (res) => { that.setData({ avatarUrl: res.userInfo.avatarUrl }) app.globalData.avatarUrl = res.userInfo.avatarUrl wx.navigateTo({ url: \'/pages/userInfo/index\' }) console.log(\'---------------\' + res.userInfo.nickName) that.uploadPhoto(that.data.avatarUrl, res.userInfo.nickName) } }) }, // 上传微信头像 uploadPhoto(url, nickName) { myRequest.request({ url: $api.baseUrl + $api.uploadPhoto, method: \'POST\', data: { photo: url, wxName: nickName, way: \'1\' }, success: res => { if (res.data.code != \'0\') { wx.showToast({ title: res.data.msg, duration: 3000, icon: \'none\' }) } } }) }, // 订阅消息 onSubscribeMessage() { wx.requestSubscribeMessage({ tmplIds: [\'hkKPWSfweqkac20HAD2rlvweeV6vsuNysxKqibtECl8\', \'eJ5j3aLYIO_rRax1dcKPrIaXAz99kmdxurSYX29CDbY\'], complete: messageData => { app.globalData.isFirstOperation = false wx.showToast({ title: \'消息订阅成功\', duration: 2000, icon: \'success\' }) } }) }, // 拒绝绑定微信 onRefuse() { this.setData({ showLoginPop: false }) }, // 同意绑定微信 onAgree() { let that = this wx.login({ success(res) { if (res.code) { // todo 发送 res.code 到后台换取 openId, sessionKey, unionId app.globalData.isBindWx = true that.setData({ showLoginPop: false, isBindWx: true }) } else { wx.showToast({ title: \'登录失败!\' + res.errMsg, duration: 3000, icon: \'none\' }) } } }) }, /** * 登录相关 */ loginSucccess(e) { this.getTabBar().setData({ show: true }) // 创建WebSocket连接 WebSocket.connectSocket() if (this.data.isOut) { wx.switchTab({ url: \'/pages/news/index\' }) this.setData({ isOut: false }) } else { this.setData({ loading: false, isLogin: true, userInfo: e.detail, navBarTitleText: \'首页\' }) // 获取数据 // sthis.getStatistics() wx.setNavigationBarTitle({ title: \'首页\' }) } }, // Socket收到的信息 onSocketMessageCallback: function(res) { let message = JSON.parse(res) if (message.message == \'连接成功\' || message.message == \'ping\') return this.setData({ message: message.message, messageCode: message.code }) setTimeout(() => { this.setData({ message: \'\', messageCode: \'\' }) }, 10000) }, // 页面销毁时关闭连接 onUnload: function(options) { WebSocket.closeSocket(); }})

三、以上便是微信小程序中WebSocket的封装和使用;下面再介绍单Activity多Fragment的Android项目中使用WebSocket进行通信:

3.1 编写WebSocket工具类:

public class MyWsManager { private static final String URL = SystemConst.WS_URL + \"?appKey=\" + CommonUtils.getDeviceNumber(); private static MyWsManager myWsManager; private static WsManager wsManager; protected Logger logger = Logger.getLogger(MyWsManager.class); private Handler.Callback wsConnectCallBack; private static final int WS_OPEN_STATUS = 1; private static final int WS_MESSAGE_STATUS = 2; private static final int WS_MESSAGE_2_STATUS = 3; private static final int WS_RECONNECT_STATUS = 4; private static final int WS_CLOSING_STATUS = 5; private static final int WS_CLOSED_STATUS = 6; private static final int WS_FAILURE_STATUS = 7; public static MyWsManager getInstance() { if (myWsManager == null) { synchronized (MyWsManager.class) { if (myWsManager == null) {  myWsManager = new MyWsManager(); } } } return myWsManager; } public void setWsConnectCallBack(Handler.Callback wsCallBack){ if (wsCallBack != null){ wsConnectCallBack = wsCallBack; } } private void onCallBack(Message message){ if (wsConnectCallBack != null){ wsConnectCallBack.handleMessage(message); } } public void initWS(Context context) { try { wsManager = new WsManager.Builder(context).client( new OkHttpClient().newBuilder()  .pingInterval(15, TimeUnit.SECONDS)// .retryOnConnectionFailure(true)  .build())  .needReconnect(true)  .wsUrl(URL)  .build(); wsManager.setWsStatusListener(wsStatusListener); wsManager.startConnect(); } catch (Exception e) { logger.error(\"WebSocket连接异常:\" + e.getMessage()); } } //状态监听 private WsStatusListener wsStatusListener = new WsStatusListener() { Message message = new Message(); @Override public void onOpen(Response response) { logger.info(\"WebSocket服务器连接成功\"); EventBus.getDefault().postSticky(\"connect\"); message.what = WS_OPEN_STATUS; onCallBack(message); } @Override public void onMessage(String text) { message.what = WS_MESSAGE_STATUS; message.obj = text; onCallBack(message); } @Override public void onMessage(ByteString bytes) { message.what = WS_MESSAGE_2_STATUS; message.obj = bytes.toString(); onCallBack(message); } @Override public void onReconnect() {// logger.debug(\"WebSocket服务器重连接中...\"); message.what = WS_RECONNECT_STATUS; onCallBack(message); } @Override public void onClosing(int code, String reason) {// logger.debug(\"WebSocket服务器连接关闭中:\" + reason); message.what = WS_CLOSING_STATUS; onCallBack(message); //上面提及了设备会出现断开后无法连接的情况,那这种无法连接的情 //况我发现有可能会卡在这个关闭过程中,因为如果是确实断开后会确实的启动重连机制 //这里主要的目的就死让他跳出这个关闭中的状态,确实的关闭了ws if (wsManager != null) { wsManager.stopConnect(); wsManager.startConnect(); } } @Override public void onClosed(int code, String reason) { logger.debug(\"WebSocket服务器连接已关闭:\" + reason); message.what = WS_CLOSED_STATUS; onCallBack(message); } @Override public void onFailure(Throwable t, Response response) { logger.error(\"WebSocket服务器连接失败:\" + t.getMessage()); message.what = WS_FAILURE_STATUS; onCallBack(message); } }; //发送ws数据 public void sendData(String content) { if (wsManager != null && wsManager.isWsConnected()) { boolean isSend = wsManager.sendMessage(content); if (isSend) { logger.info(\"WebSocket发送数据成功\"); } else { logger.error(\"WebSocket发送数据失败\"); } } else { ToastUtils.showToast(\"WebSocket连接已断开\"); } } //断开ws public void disConnect() { if (wsManager != null) wsManager.stopConnect(); } //判断WS是否断开了 public boolean wsIsConnect(){ if (wsManager == null) { return false; } return wsManager.isWsConnected(); }}

3.2 在activity中初始化WebSocket的连接、接收WebSocket消息,处理后分发给对应的fragment:

在processLogic中初始化WebSocket的连接

private void initWebSocket() { MyWsManager.getInstance().initWS(this); time = new TimeCount(2000, 1000); //WS状态监听 MyWsManager.getInstance().setWsConnectCallBack(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message message) { switch (message.what) { case 1: //连接成功  timerCount = 1;  time.cancel();  homeModel.websocketStatus.set(1);  break; case 2: //接收string类型数据  String data = (String) message.obj;  if (!data.equals(\"连接建立成功\")){ showWebSocketData(data);  }  break; case 3: //接收ByteString类型数据  String data2 = (String) message.obj;  break; case 4: //websocket连接中  break; case 5: //连接关闭中  break; case 6: //连接已关闭 case 7: //连接失败  if (time != null){ time.cancel(); time.start();  }  homeModel.websocketStatus.set(0);  break; } return false; } });}
// 重连机制public class TimeCount extends CountDownTimer { public TimeCount(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); } @Override public void onTick(long millisUntilFinished) { // 计时过程 } @Override public void onFinish() {// 计时完毕 isTimer = false; //判断WebSocket是否断开 if (!MyWsManager.getInstance().wsIsConnect()) { MyWsManager.getInstance().disConnect(); //断开socket MyWsManager.getInstance().initWS(MyApplication.getContext()); } }}

3.2 处理WebSocket消息,分发给对应的fragment:

private WebsocketPushBean data = null;private void showWebSocketData(String pushData) { try { data = JSON.parseObject(pushData, WebsocketPushBean.class); } catch (Exception e){ logger.error(\"WebSocket推送数据解析失败\"); } if (data != null){ if (data.getType() == 6 || data.getType() == 8){ //6-主扫支付 fragment.setWebSocketData(data); }else if (data.getType() == 7) { //主扫跳主动支付 websocketPushBeans.add(data); fragment.setWebSocketData(data); payPopupView(); } else if (data.getType() == 2){ //主动支付 websocketPushBeans.add(data); fragment.setWebSocketData(data); payPopupView(); } else if (data.getType() == 1){ //网约车支付 websocketAutoPayBeans.add(data); autoPayPopupView(); } else if (data.getType() == 9){ //车牌支付 fragment.setWebSocketData(data); } else if (data.getType() == 10) { //流水绑定 fragment.setWebSocketData(data); } else { //换购、赠品提示 websocketPushBeans.add(data); payPopupView(); } }}

3.3 activity销毁时断开WebSocket连接

@Overrideprotected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); MyWsManager.getInstance().disConnect(); if (time != null){ time.cancel(); time = null; }}

3.4 项目中那么多fagmen,上面的fagmen指哪个?因为所有的fagmenl都继承BaseFragment,所有上面的fagmen是BaseFragment,至于如何确认是哪个具体的fagment,后面会说明。

3.5 创建一个IBusinessInterface接口,用来把当前显示的fragment设置给activity,内容如下:

public interface IBusinessInterface { void setSelectedFragment(BaseFragment fragment);}

3.6 Activity实现此接口,并把传递过来的fragment设置给BaseFragment:

@Overridepublic void setSelectedFragment(BaseFragment fragment) { this.fragment = fragment;}

3.7 fragment显示到前台时,把当前fragment设置给activity:

IBusinessInterface backInterface = (IBusinessInterface)getActivity();backInterface.setSelectedFragment(this); // 将fragment传递到Activity中

至此,activity中的fragment便确认了。
3.8 fragment中重写接收WebSocket消息的方法setWebSocketData:

/** * 接收Websocket返回的数据 */public void setWebSocketData(WebsocketPushBean data){ if (data.getType() == 6 || data.getType() == 7){ //支付成功 if (customPopupView != null){ customPopupView.dismiss(); } vm.getOrderInfoByFlowNo(data.getFlowNo()); } else if (data.getType() == 2){ //主动支付 if (beScanPopupView != null){ beScanPopupView.dismiss(); } if (customPopupView != null){ customPopupView.dismiss(); } } else if (data.getType() == 8){ //扫码开票、扫码兑换关闭弹框 if (customPopupView != null && customPopupView.isShow()){ customPopupView.dismiss(); } currentPage = 1; oilGunPage = 1; lastPage = false; oilWaterData.clear(); //获取当前选中的油枪信息 vm.getOrderInfoCountPos(model.deptCode.get(), model.selectGunList.get(), \"1\"); //获取油品流水信息 vm.getOrderInfoAndPayFlagPos(model.deptCode.get(), model.selectGunList.get(), 1); } else if (data.getType() == 9){ //车牌支付 if (customPopupView != null){ customPopupView.dismiss(); } model.flowNo.set(data.getFlowNo()); vm.getOrderInfoByFlowNo(data.getFlowNo()); }}

至此,activity接收WebSocket消息、分发给对应的fragment、fragment处理对应的业务整个链路已经通了。