对WebSocket的认识
WebSocket一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。WebSocket API也被W3C定为标准。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
来自维基百科
需求(为什么要使用WebSocket)
我们公司做的一个WEB项目,而这个WEB项目中有POS机支付的功能。当WEB上点击使用POS支付时,在POS机端要调起支付功能,完成支付。起初第一个版本是这样的,当WEB 页面点击POS支付会弹出一个二维码进行扫码支付。而这个版本在现场发现使用起来过于繁琐,就决定使用去掉扫码操作,当WEB页面点击使用POS支付时直接调起POS机的支付功能进行支付,这种让我想到了推送,而第三方推送考虑到 安全就放弃了。最终结合我们后台服务为tomcat8.0,决定使用WebSocket。
开发
好吧,明白了需求,那就开始开发吧,然后我就开始疯狂似的编码。首先我为了满足需求,让这个应用保活的状态,我写了一个前台服务(只是为了保护进程不被杀死和在没有打开程序也能接收到推送)。写一个类继承Service然后在onCreate中调用:
注意: 在写前台服务的时候不要onStartCommand()在这个方法里做任何事情,因为这个方法在Service中多次调用很容易导致内存溢出(当然我看过一本书上是这么说的,如果抛出OutOfMemoryException这个异常时,我们可以在AndroidMainfest.xml中的application largelHeap属性设置为true, 这样就可以增加系统为当前App分配内存,甚至到100M。然而在这个方法中有执行操作的话并不会减少崩溃),如果是使用绑定服务的话,就更不要在这个方法写你的逻辑代码,因为使用绑定服务这个方法是不会被调用的。然后要在onDestrou要停止掉前台服务
前面的都是开胃菜,真正的该上主菜了,就是如何去使用WebSocket。网上有使用Okhttp进行封装,在封装的,还有使用了java-websocket这个包的。而我选择的是后者。因为这个包有maven包,好像还是麻省理工实验室出的,使用起来也算比较简单。
第一步:去gitHub上导包gitHub地址Java-WebSocket。
第二步:
webSocketUrl是后台的URL包括参数。
第三步:
好了,经过这三步成功的使用了WebSocket连上后台,在onDestroy()方法中记得调用client.close()这个方法进行断开链接。是不是很简单。然而并不是这么简单,如果由于网络的原因或者后台发生故障了,断开了怎么办呢?你肯定会想,这还不好办,这个包肯定有断开重连的方法吧!你写个广播监听网络状态。如果网络异常了就去重连。呵呵,这个包是没有重连的方法的,在github上还有人提过这个Bug,但是博主并没有解决,目前还是open状态bug号#392。这个问题让后思索了很久,后来我我就想,既然断开了,我就不如重新开给连接,重新走上面的方法。然而,在测试的时候,我自己将网络断开,发现无限的走上面的方法,因为我在onClose和onError是有重新走上面的方法,最后抛异常了,异常是OutOfMemoryException没错就是我上面所指的内存溢出。这很明显吧,断开或者异常断开就走上面的方法,就会去重新创建一个WebSocketClient对象,而原来的也没有回收掉,还无限的去创建新对象,不抛异常才怪。既然问题找到了,那就找解决办法呗。我的解决方案是这样的,在调用这个方法之前,我判断这个对象是否为空,如果不为空,我会想调用client.close()这个方法。为什么要调用这个方法呢?其实很简单,因为这个方法会将里面的一些变量变为空。然后将client变为空。经过改良后是这样的
这样Android移动端出现问题,自己可以检测到,而且可以重连的问题解决了,但是如果是后台发生问题呢怎么办呢。最好的办法就是进行心跳检测。那什么是心跳检测呢?知道的可以跳过。心跳检测是这样的,客户端:”你还活着吗?”,后台服务:”嗯,还活着”,然后过一段时间客户端又问一句,后台回复一句,如果超过一定时间没有回答,就视为客户端和后台断开了,然后进行重连。这样包括客户端自己出问题了重连,和后台出问题了重连,可以保证客户端一直连着的状态了。
最后完整代码放在github上。代码上还有点问题,比如重连的次数,我的重连的次数是在onCreate方法中进行请求,其实大可以通过心跳进行获取,而后台可以将这个值放到缓存中,比如放到redis中,不需要每次去数据库中获取。而Android客户端不需要重启应用进行获取。这点不足是我的好朋友看了代码给的提醒。还有写不足的地方还希望大家提醒,点拨一下 ^^ ^^!!