You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android与Java服务端WebSocket长连接实现及数据更新获取异常问题

问题解答: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:37:30