最近项目中引入了实时接收服务器数据的功能,考量后通过WebSocket长链接来实现。

接下来了解一下webSocket 的特点:
1、建立在 TCP 协议之上,服务器端的实现比较容易。
2、与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3、支持双向通信,实时性更强。
4、数据格式比较轻量,性能开销小,通信高效。
5、可以发送文本,也可以发送二进制数据。
6、没有同源限制,客户端可以与任意服务器通信。
7、协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

在我的项目计划中是通过Okhttp3实现socket长连接,先看一下效果:
依次展示的效果图为开启长链接------关闭长链接-------重连长链接

接下来看一下具体的代码
1、长链接初始化,创建Service服务,通过bindService启动Service服务

/*** 获取单例,非appContext,要先init*/public static ImWebSocketBackgroundService getInstance(Context context) {if (instance == null) {synchronized (ImWebSocketBackgroundService.class) {if (instance == null) {instance = new ImWebSocketBackgroundService(context);}}}return instance;}private ImWebSocketBackgroundService(Context context) {this.context = context;}private void onCreate() {if(webSocketServiceManager == null){webSocketServiceManager = new WebSocketServiceManager(context, this);webSocketServiceManager.bindService();sendGeneration = 0;}}
public class MyWebSocketService extends Service implements WebSocketResultListener {private WebSocketThread webSocketThread;private IWebsocketResponseDispatcher responseDispatcher;private SocketResultListenerStorage socketResultListenerStorage = new SocketResultListenerStorage();@Overridepublic void onCreate() {super.onCreate();webSocketThread = new WebSocketThread();webSocketThread.setWebSocketResultListener(this);webSocketThread.start();responseDispatcher = new WebsocketResponseDispatcher();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return new WebSocketBinder();}@Overridepublic void onDestroy() {super.onDestroy();if (webSocketThread.getHandler() != null)webSocketThread.getHandler().sendEmptyMessage(MessageType.QUIT);}public boolean sendText(String text) {if (webSocketThread.getHandler() != null) {Message message = Message.obtain();message.obj = text;message.what = MessageType.SEND;return webSocketThread.getHandler().sendMessage(message);}return false;}
}

在WebSocketServiceManager中创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
当执行到onServiceConnected回调时通过IBinder实例得到Service,实现client与Service的连接
onServiceDisconnected回调被执行时,表示client与Service断开连接

public WebSocketServiceManager(Context context, WebSocketResultListener socketResultListener) {this.context = context;this.socketResultListener = socketResultListener;}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.e(WebSocketThread.TAG, "WebSocketService 已经连接");serviceBindSucc = true;binding = false;bindTime = 0;   //连接成功后归零myWebSocketService = ((MyWebSocketService.WebSocketBinder)service).getWebSocketService();myWebSocketService.addSocketListener(socketResultListener);if(myWebSocketService.isSendConnected()) {if (socketResultListener != null) {socketResultListener.connection();}}}@Overridepublic void onServiceDisconnected(ComponentName name) {     //service异常被关闭 回调该方法binding = false;serviceBindSucc = false;if (bindTime<5 && !binding){Log.e(WebSocketThread.TAG, String.format("WebSocketService 连接断开,开始第%s次重连", bindTime));bindService();}}};//绑定服务public void bindService(){serviceBindSucc = false;binding = true;Intent intent = new Intent(context, MyWebSocketService.class);context.bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);bindTime++;}

2、在service创建时初始化Thread线程,通过Handler发送消息建立连接
   2.1 配置OkHttpClient
其中head中需要的参数依照大家项目中需要参数随时改变

private void initHeader() {headerMap.put("sourse", "android");headerMap.put("appVersion", Constants.VERSION_CODE);headerMap.put("token", Constants.TOKEN);headerMap.put("devCode",Constants.DevCode);}
OkHttpClient mClient = new OkHttpClient.Builder().readTimeout(3, TimeUnit.SECONDS)//设置读取超时时间.writeTimeout(3, TimeUnit.SECONDS)//设置写的超时时间.connectTimeout(3, TimeUnit.SECONDS)//设置连接超时时间.build();

2.2 使用Url,构建WebSocket请求
WEB_SOCKET_URL = "ws://47.74.235.101:8190/ws-community-im-websocket";
WEB_SOCKET_URL 依照大家项目中具体路径改变

private void initRequest() {Request.Builder requestBuilder = new Request.Builder().url(WEB_SOCKET_URL);if(headerMap == null){initHeader();}if (headerMap != null && !headerMap.isEmpty()) {Set<String> strings = headerMap.keySet();StringBuffer stringBuffer = new StringBuffer();for (String string : strings) {stringBuffer.append(string).append(" ").append(headerMap.get(string)).append("\t");}Log.e("web", stringBuffer.toString());requestBuilder.headers(Headers.of(headerMap));}request = requestBuilder.build();}

2.3 发起连接,配置回调。

  • onOpen(),连接成功
  • onMessage(String text),收到字符串类型的消息,一般我们都是使用这个
  • onMessage(ByteString bytes),收到字节数组类型消息,我这里没有用到
  • onClosed(),连接关闭
  • onFailure(),连接失败,一般都是在这里发起重连操作
//开始连接
WebSocket websocket = mClient.newWebSocket(request, new WebSocketListener() {@Overridepublic void onOpen(WebSocket webSocket, Response response) {super.onOpen(webSocket, response);//连接成功...}@Overridepublic void onMessage(WebSocket webSocket, String text) {super.onMessage(webSocket, text);//收到消息...(一般是这里处理json)}@Overridepublic void onMessage(WebSocket webSocket, ByteString bytes) {super.onMessage(webSocket, bytes);//收到消息...(一般很少这种消息)}@Overridepublic void onClosed(WebSocket webSocket, int code, String reason) {super.onClosed(webSocket, code, reason);//连接关闭...}@Overridepublic void onFailure(WebSocket webSocket, Throwable throwable, Response response) {super.onFailure(webSocket, throwable, response);//连接失败...}
});

2.4 使用WebSocket对象发送消息,msg为消息内容,send方法会马上返回发送结果

//发送消息
webSocket.send(msg);

3、通过第2步连接成功后启动心跳,每隔10秒钟发起一次心跳,在onMessageReceive中接收到后台返回的json信息,在本项目中通过后台返回的code值判断此时时在线收到消息还是断开连接,大家依照各自项目改变

SEND_MESSAGE = 601;  //在线接收消息
private static final int DISCONNECT_MESSAGE = 602; //断开连接
   //开始心跳public void startHeartBeat() {Log.e(WebSocketThread.TAG, "启动心跳");runHeartBeat = true;if (scheduler == null) {scheduler = Executors.newScheduledThreadPool(1);scheduler.schedule(this, getDelay(), TimeUnit.SECONDS);}}@Overridepublic void onMessageReceive(String jsonText) {try {JSONObject jsonObject = new JSONObject(jsonText);Log.i(WebSocketThread.TAG,jsonObject+"");if(jsonObject.has("msgType") && Integer.valueOf(jsonObject.getInt("msgType")) != null){int msgType = jsonObject.getInt("msgType");if (msgType == SEND_MESSAGE) {     //发送消息String msg = jsonObject.getString("msg");Log.d(WebSocketThread.TAG, msg);} else if (msgType == DISCONNECT_MESSAGE) {     //断开连接stopHeartBeat();}}} catch (JSONException e) {e.printStackTrace();}}@Overridepublic void connection() {Log.e(WebSocketThread.TAG, "连接成功");startHeartBeat();}@Overridepublic void run() {if (runHeartBeat) {if (sendText(defineText())) {sendGeneration++;Log.d(WebSocketThread.TAG,"发送心跳成功"+sendGeneration);}scheduler.schedule(this, getDelay(), TimeUnit.SECONDS);}}private int getDelay() {return 10;}

4、重连机制----处理切换网络,被挤掉线等情况
在第3步中提到的onFailure()方法中进行重连,连接成功后重新接收到后台返回的json信息

public class ReconnectWebSocketManager {private WebSocketThread webSocketThread;private volatile boolean retrying;      //正在重新连接private volatile boolean destroyed ;private final ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();       //单线程public ReconnectWebSocketManager(WebSocketThread webSocketThread) {this.webSocketThread = webSocketThread;this.retrying = false;this.destroyed = false;}synchronized void performReconnect(){retrying = false;retry();}private synchronized void retry() {if (!retrying){retrying = true;synchronized (singleThreadPool){singleThreadPool.execute(new ReconnectRunnable());}}}//销毁public void destroy(){destroyed = true;if (singleThreadPool !=null)singleThreadPool.shutdownNow();webSocketThread = null;}public class ReconnectRunnable implements Runnable {public void run() {retrying = true;for (int i = 0;i <20 ; i++){if (destroyed){retrying = false;return;}Handler handler = webSocketThread.getHandler();if (handler !=null){if (webSocketThread.getConnectStatus() ==2) //已连接break;if (webSocketThread.getConnectStatus() ==1) //正在连接continue;if (webSocketThread.getConnectStatus() ==0)handler.sendEmptyMessage(MessageType.CONNECT);}else {break;}try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}retrying = false;}}}
}

到此长链接主要代码已经讲完了,别忘了在清单文件中注册service,添加网络权限

<service android:name="com.example.msg.imwebsocket.MyWebSocketService"/><uses-permission android:name="android.permission.INTERNET" />

在build.gradle中添加okhttp3依赖

implementation 'com.squareup.okhttp3:okhttp:3.10.0'

希望能对于长链接需求的同学帮助,欢迎讨论,附上项目的代码地址demo

Android 实现WebSocket长连接相关推荐

  1. Android websocket长连接+点对点订阅

    项目中使用到了websocket长连接+点对点订阅,博客记录下. 长连接通常使用的是名称叫做STOMP的协议,具体跟服务器端的开发人员确认即可. 直接贴干货: module build.gradle添 ...

  2. 实现单台测试机6万websocket长连接

    本文由作者郑银燕授权网易云社区发布. 本文是我在测试过程中的记录,实现了单台测试机发起最大的websocket长连接数.在一台测试机上,连接到一个远程服务时的本地端口是有限的.根据TCP/IP协议,由 ...

  3. java nio socket长连接_netty学习实战—实现websocket长连接和socket之间进程通信

    netty学习-实现websocket长连接和socket之间通信 最近正在学习netty,跟着教程写了一个基于WebSocket的网页聊天室,对netty有了一定的了解,现在正好项目使用到长连接,选 ...

  4. websocket 获取连接id_Swoole学习笔记七:搭建WebSocket长连接 之 使用 USER_ID 作为身份凭证...

    Swoole学习笔记七:搭建WebSocket长连接 之 使用 USER_ID 作为身份凭证 2年前 阅读 3678 评论 0 喜欢 0 ### 0.前言 前面基本的WebSocket操作,我们基本都 ...

  5. java与微信小程序通讯_java与微信小程序实现websocket长连接

    本文实例为大家分享了java与微信小程序实现websocket长连接的具体代码,供大家参考,具体内容如下 背景: 需要在小程序实现地图固定坐标下实时查看消息 java环境 :tomcat7 jdk1. ...

  6. WebSocket长连接因为网络波动而导致客户端的“假离线”---问题发现、分析到解决

    文章目录 简介 问题的现象.场景和解决方案 基本的部署架构 问题是什么呢? 假离线到底是怎么来的? 验证猜想 解决问题 如何发现问题的呢? 客户端离线预警 奇怪的现象来了 该怎么去发现呢 到底是谁改的 ...

  7. 微信是与服务器长连接,java与微信小程序实现websocket长连接.pdf

    java与与微微信信小小程程序序实实现现websocket长长连连接接 本文实例为大家分享了j ava与微信小程序实现websocket长连接的具体代码,供大家参考,具体内容 下 背背景景:: 需要在 ...

  8. WebSocket长连接

    WebSocket长连接 1.概述 1.1 定义 1.2 原理 2.Django中配置WebSocket 2.1安装第三方法包 pip install channels 2.2 Django 中的配置 ...

  9. 安卓中socket长连接和websocket长连接的实现

    现在一款成熟的app一般都会具备长连接推送功能,那么我们要想项目具备长连接的功能现在又两种选择的方案,一种基于原生tcp协议的socket长连接,另外一种基于ws协议的websocket的长连接,今天 ...

最新文章

  1. 手机php文件怎么改后辍,php修改文件后缀名的方法
  2. 关于服务器返回信息的Unicode转码的方法
  3. AI修行三十篇文章到不惑,已经掌握了什么,接下来还要说什么
  4. RabbitMQ通配符模式以及与Routing模式的区别
  5. Eclipse中快速为类提供构造器、get()、set()方法、重写toString()、hasCode()、equals()等
  6. ABAP UDO generation report
  7. 妈妈花3万给9岁儿子报编程课 网友:全是忽悠!
  8. 信息学奥赛一本通C++语言——1070:人口增长
  9. 禁用部分radio(实践)
  10. 爬虫基本功之学点JS(一)
  11. html 说明文档样式,通用模板说明文档
  12. 吴裕雄--天生自然 PHP开发学习:MySQL 插入数据
  13. EXTJS 5 开发环境搭建
  14. 有一种VR电影比爱情动作片更“爽”
  15. 中国联通开放号码标记一键查询与清除服务
  16. 读《编码:隐匿在计算机软硬件背后的语言》有感
  17. 高中计算机教学心得,高中教学心得随笔
  18. 2018年福建省计算机二级acess试题,2018年计算机二级Access试题及答案(一)
  19. Charles打开macOS proxy无法上网
  20. oracle reorg的意义,Oracle Reorg 的形式与相关的script - 2016-02-26

热门文章

  1. 鼠标停止移动后指针消失
  2. 干货分享 | 尿液cfDNA的研究现状及样本准备方法总结
  3. 万字长文概述单目3D目标检测算法
  4. 高端羽绒服价值重构 SKYPEOPLE天空人的新答案
  5. Lootcode 201~220
  6. 2021CCPC女生赛总结
  7. 渠道归因(一)传统渠道归因
  8. 为什么KEYNOTE导出HTML很大,给keynote文件瘦身
  9. 从 keynote 大神到语雀画图大神,她是怎么做的?
  10. layui-dtree 重设根节点parentId的值