Android与Java服务端WebSocket长连接实现及数据更新获取异常问题
嘿,我来帮你搞定这两个问题!先聊聊Android和Java服务端怎么用WebSocket搭长连接发数据包,再解决那个变量更新后读不到的坑。
一、Android与Java服务端通过WebSocket建立长连接并发送数据包
1. Java服务端实现(以Tomcat WebSocket为例)
服务端咱们用Tomcat自带的WebSocket支持来做,代码很直观,用@ServerEndpoint注解定义端点就行:
import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; @ServerEndpoint("/websocket") public class WebSocketServer { // 用线程安全的集合存所有连接会话 private static CopyOnWriteArraySet<WebSocketServer> connections = new CopyOnWriteArraySet<>(); private Session session; @OnOpen public void onOpen(Session session) { this.session = session; connections.add(this); System.out.println("新客户端已连接"); } @OnMessage public void onMessage(String message, Session session) throws IOException { // 处理客户端发来的消息 System.out.println("收到客户端消息: " + message); // 给客户端回发消息 session.getBasicRemote().sendText("服务端已收到: " + message); } @OnClose public void onClose() { connections.remove(this); System.out.println("客户端断开连接"); } // 主动给所有在线客户端发消息的方法 public static void broadcastMessage(String message) throws IOException { for (WebSocketServer connection : connections) { connection.session.getBasicRemote().sendText(message); } } }
注意:要确保你的Tomcat版本在7.0.47以上,不然可能不支持WebSocket哦。
2. Android客户端实现(以OkHttp WebSocket为例)
Android这边推荐用OkHttp来做WebSocket,它的封装省心还稳定。先加依赖到你的build.gradle:
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
然后写客户端连接逻辑:
import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.WebSocket; import okhttp3.WebSocketListener; import okio.ByteString; public class AndroidWebSocketClient { private WebSocket webSocket; public void connectToServer() { OkHttpClient client = new OkHttpClient.Builder() .build(); Request request = new Request.Builder() .url("ws://你的服务端IP:端口/websocket") .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, okhttp3.Response response) { super.onOpen(webSocket, response); // 连接成功后可以发个初始消息 webSocket.send("Android客户端上线啦!"); } @Override public void onMessage(WebSocket webSocket, String text) { super.onMessage(webSocket, text); // 处理服务端发来的文本消息 System.out.println("收到服务端消息: " + text); } @Override public void onMessage(WebSocket webSocket, ByteString bytes) { super.onMessage(webSocket, bytes); // 处理二进制数据包 } @Override public void onFailure(WebSocket webSocket, Throwable t, okhttp3.Response response) { super.onFailure(webSocket, t, response); // 连接失败可以加个重连逻辑 System.out.println("连接失败,原因: " + t.getMessage()); } }; webSocket = client.newWebSocket(request, listener); } // 发送文本消息 public void sendTextMessage(String message) { if (webSocket != null) { webSocket.send(message); } } // 发送二进制数据包 public void sendBinaryData(byte[] data) { if (webSocket != null) { webSocket.send(ByteString.of(data)); } } }
注意:Android里网络请求不能跑在主线程,所以connectToServer()要放在子线程或者用协程、AsyncTask来调用哦。
二、解决TestDatabean中data变量无法获取更新值的问题
先看你贴的TestDatabean代码:
public class TestDatabean { public static int count = 0; public int id; public volatile static int data; }
结合你说的TimerTask里拿不到更新值的情况,我帮你捋几个大概率的坑:
1. 先检查getTestDatabean()的单例实现
你用到了TestDatabean.getTestDatabean(),说明这是个单例类,但你没贴这个方法的代码。如果单例实现错了(比如每次调用都new一个新实例),虽然data是static变量,但也可能因为其他逻辑出问题。正确的双重检查锁单例应该是这样的:
public class TestDatabean { public static int count = 0; public int id; public volatile static int data; // 单例实例,volatile保证初始化可见性 private static volatile TestDatabean instance; // 私有构造,防止外部new private TestDatabean() {} public static TestDatabean getTestDatabean() { if (instance == null) { synchronized (TestDatabean.class) { if (instance == null) { instance = new TestDatabean(); } } } return instance; } // 假设你的getData()是返回static data public int getData() { return data; } // 对应的更新方法 public void setData(int newData) { data = newData; } }
2. 确认data的更新逻辑是否正确
你说data每5秒被覆盖一次,那更新的地方是不是真的修改了TestDatabean.data这个static变量?比如更新代码应该是:
// 正确的更新方式 TestDatabean.data = 新值; // 或者通过实例方法 TestDatabean.getTestDatabean().setData(新值);
如果更新的是某个局部变量或者非static的data,那肯定读不到变化。
3. 检查TimerTask是否真的在执行
有没有可能TimerTask没被正确调度?比如Timer被取消了,或者没调用schedule()?你可以在run()里加个日志,看看是不是每5秒真的跑了:
task = new TimerTask() { @Override public void run() { int currentData = TestDatabean.getTestDatabean().getData(); System.out.println("当前data值: " + currentData); } }; // 别忘了调度任务,延迟0秒开始,每5秒执行一次 new Timer().schedule(task, 0, 5000);
如果日志里的data一直不变,那再回头检查更新逻辑,确保真的修改了static的data变量。
4. 关于volatile的小提醒
你给data加了volatile static,这个是对的,它能保证一个线程修改后,其他线程能立即看到最新值。但如果你的getData()方法里有缓存逻辑(比如把data赋值给一个局部变量后返回,但这个其实不影响),那也要检查有没有问题。
内容的提问来源于stack exchange,提问作者Xour

