Android 使用WebSocket通信

本文介绍了如何在Android应用中使用Java-WebSocket库创建WebSocket客户端,与SpringBoot服务器进行实时通信。步骤包括添加依赖、设置权限、创建WebSocketClient子类、建立连接、关闭连接以及处理各种事件。同时展示了服务器端的配置和实现,包括添加依赖、编写配置类和具体实现WebSocket服务。最后,提供了Android服务端的心跳检测和重连机制以及Fragment中的消息接收和广播处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

APP端实现

1.添加依赖
implementation "org.java-websocket:Java-WebSocket:1.5.1"
2.加入网络请求权限
<uses-permission android:name="android.permission.INTERNET" />
3.创建客户端类并继承WebSocketClient,需要实现它的四个抽象方法和构造函数
import android.util.Log;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

public class JWebSocketClient extends WebSocketClient {
    public JWebSocketClient(URI serverUri) {
        super(serverUri, new Draft_6455());
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        Log.e("JWebSocketClient", "onOpen()");
    }

    @Override
    public void onMessage(String message) {
        Log.e("JWebSocketClient", "onMessage()");
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        Log.e("JWebSocketClient", "onClose()");
    }

    @Override
    public void onError(Exception ex) {
        Log.e("JWebSocketClient", "onError()");
    }
}

4.建立websocket连接
private void createWebSocketClient(String userId){
        URI uri = URI.create("ws://172.25.204.62:8082/websocket/"+userId);
        client = new JWebSocketClient(uri){
            @Override
            public void onOpen(ServerHandshake handshakedata) {
                super.onOpen(handshakedata);
                sb.append("onOpen at time:");
                sb.append(new Date());
                sb.append("服务器状态:");
                sb.append(handshakedata.getHttpStatusMessage());
                sb.append("\n");
                tv_message.setText(sb.toString());
            }

            @Override
            public void onMessage(String message) {
                super.onMessage(message);
                Message handlerMessage = Message.obtain();
                handlerMessage.obj = message;
                handler.sendMessage(handlerMessage);
            }

            @Override
            public void onClose(int code, String reason, boolean remote) {
                super.onClose(code, reason, remote);
                sb.append("onClose at time:");
                sb.append(new Date());
                sb.append("\n");
                sb.append("onClose info:");
                sb.append(code);
                sb.append(reason);
                sb.append(remote);
                sb.append("\n");
                tv_message.setText(sb.toString());
            }

            @Override
            public void onError(Exception ex) {
                super.onError(ex);
                sb.append("onError at time:");
                sb.append(new Date());
                sb.append("\n");
                sb.append(ex);
                sb.append("\n");
                tv_message.setText(sb.toString());
            }
        };
        try {
            client.connectBlocking();
        } catch (InterruptedException e) {
            e.printStackTrace();
            XToastUtils.error(e+"");
        }

    }


    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            sb.append("服务器返回数据:");
            sb.append(msg.obj.toString());
            sb.append("\n");
            tv_message.setText(sb.toString());
            return true;
        }
    });
5.关闭连接
 @Override
    public void onDestroy() {
        super.onDestroy();
        client .close();
    }

服务端:

springboot集成websocket。

1.添加依赖

Maven依赖:

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
            <!-- <scope>test</scope> -->
        </dependency>
2.编写配置类:

首先要注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的WebsocketEndpoint。


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

具体实现类:


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ConcurrentHashMap;
//注册成组件
@Component
//定义websocket服务器端,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址
@ServerEndpoint("/websocket/{userId}")
//如果不想每次都写private  final Logger logger = LoggerFactory.getLogger(当前类名.class); 可以用注解@Slf4j;可以直接调用log.info
@Slf4j
public class WebSocket {

    //实例一个session,这个session是websocket的session
    private Session session;

    /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
    private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();

    /**接收userId*/
    private String userId="";

    private static int onlineCount = 0;

    //前端请求时一个websocket时
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) {
        this.session = session;
        this.userId=userId;
        if(webSocketMap.containsKey(userId)){

            webSocketMap.remove(userId);
            webSocketMap.put(userId,this);
            //加入set中
        }else{

            webSocketMap.put(userId,this);
            //加入set中
            addOnlineCount();
            //在线数加1
        }
        log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
        sendMessage("连接成功");

    }

    //前端关闭时一个websocket时
    @OnClose
    public void onClose() {
        if(webSocketMap.containsKey(userId)){

            webSocketMap.remove(userId);
            //从set中删除
            subOnlineCount();
        }
        log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
    }

    @OnError
    public void onError(Session session, Throwable t) {

        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.error("WebSocket连接发生异常,message:"+t.getMessage());
    }

    StringBuilder stringBuilder;
    //前端向后端发送消息
    @OnMessage
    public void onMessage(String message) {
        log.info("用户消息:"+userId+",报文:"+message);
        //可以群发消息
        //消息保存到数据库、redis
        if(!message.isEmpty()){

            try {

                //解析发送的报文
                JSONObject jsonObject = JSON.parseObject(message);
                //追加发送人(防止串改)
                jsonObject.put("fromUserId",this.userId);
                String toUserId=jsonObject.getString("toUserId");
                //传送给对应toUserId用户的websocket
                if(!toUserId.isEmpty()&&webSocketMap.containsKey(toUserId)){
                    sendMessage(jsonObject.toJSONString());
                }else{

                    log.error("请求的userId:"+toUserId+"不在该服务器上");
                    //否则不在这个服务器上,发送到mysql或者redis
                }
            }catch (Exception e){

                e.printStackTrace();
            }
        }



    }



    //新增一个方法用于主动向客户端发送消息
    public void sendMessage(String message) {
        log.info("【websocket消息】广播消息, message={}", message);
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /** * 发送自定义消息 * */
    public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {

        log.info("发送消息到:"+userId+",报文:"+message);
        if(!userId.isEmpty()&&webSocketMap.containsKey(userId)){

            webSocketMap.get(userId).sendMessage(message);
        }else{

            log.error("用户"+userId+",不在线!");
        }
    }
    public synchronized int getOnlineCount() {

        return onlineCount;
    }
    public static synchronized void addOnlineCount() {

        WebSocket.onlineCount++;
    }
    public static synchronized void subOnlineCount() {
c
        WebSocket.onlineCount--;
    }
    public ConcurrentHashMap<String, WebSocket>  getWebSocketMap() {
        return webSocketMap;
    }
}

测试结果:

在这里插入图片描述
在这里插入图片描述

心跳检测和重连

1.service

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;

import com.jqwl.myapp.utils.websocket.JWebSocketClient;

import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

/**
 * @author yangyuan
 * @description:
 * @date :2022-05-23 15:33
 */
public class JWebSocketClientService1 extends Service {
    private String TAG="JWebSocketClientService";
    private URI uri;
    public JWebSocketClient client;
    private JWebSocketClientBinder mBinder = new JWebSocketClientBinder();

    //用于Activity和service通讯
    public class JWebSocketClientBinder extends Binder {
        public JWebSocketClientService1 getService() {
            return JWebSocketClientService1.this;
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化websocket
        initSocketClient();
        mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }


    /**
     * 初始化websocket连接
     */
    private void initSocketClient() {
        URI uri = URI.create( "ws://172.25.204.62:8082/websocket/00001");//测试使用
        client = new JWebSocketClient(uri) {
            @Override
            public void onMessage(String message) {
                Log.e("JWebSocketClientService", "收到的消息:" + message);

                Intent intent = new Intent();//广播接收到的消息,在Activity接收
                intent.setAction("com.xxx.servicecallback.content");
                intent.putExtra("message", message);
                sendBroadcast(intent);

            }

            @Override
            public void onOpen(ServerHandshake handshakedata) {
                super.onOpen(handshakedata);
                Log.e("JWebSocketClientService", "websocket连接成功");
            }
        };
        connect();
    }
    /**
     * 连接websocket
     */
    private void connect() {
        new Thread() {
            @Override
            public void run() {
                try {
                    //connectBlocking多出一个等待操作,会先连接再发送,否则未连接发送会报错
                    client.connectBlocking();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

    }
    /**
     * 发送消息
     *
     * @param msg
     */
    public void sendMsg(String msg) {
        if (null != client) {
            Log.e("JWebSocketClientService", "发送的消息:" + msg);
            client.send(msg);
        }
    }

    //    -------------------------------------websocket心跳检测------------------------------------------------
    private static final long HEART_BEAT_RATE = 30 * 1000;//每隔30秒进行一次对长连接的心跳检测
    private Handler mHandler = new Handler();
    private Runnable heartBeatRunnable = new Runnable() {
        @Override
        public void run() {
            Log.e("JWebSocketClientService", "心跳包检测websocket连接状态");
            if (client != null) {
                if (client.isClosed()) {
                    reconnectWs();
                }else {
                    //业务逻辑 这里如果服务端需要心跳包为了防止断开 需要不断发送消息给服务端
                    // client.send("");
                }
            } else {
                //如果client已为空,重新初始化连接
                client = null;
                initSocketClient();
            }
            //每隔一定的时间,对长连接进行一次心跳检测
            mHandler.postDelayed(this, HEART_BEAT_RATE);
        }
    };
    /**
     * 开启重连
     */
    private void reconnectWs() {
        mHandler.removeCallbacks(heartBeatRunnable);
        new Thread() {
            @Override
            public void run() {
                try {
                    Log.e("JWebSocketClientService", "开启重连");
                    client.reconnectBlocking();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

在 AndroidManifest.xml 添加

<service
            android:name=".utils.service.JWebSocketClientService1"
            android:enabled="true"
            android:exported="true" />
2.fragment

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.NonNull;

import com.alibaba.fastjson.JSONObject;
import com.jqwl.myapp.core.BaseFragment;
import com.jqwl.myapp.databinding.FragmentWebsocketByServiceBinding;
import com.jqwl.myapp.models.ChatMessage;
import com.jqwl.myapp.utils.XToastUtils;
import com.jqwl.myapp.utils.service.JWebSocketClientService1;
import com.jqwl.myapp.utils.websocket.JWebSocketClient;
import com.xuexiang.xpage.annotation.Page;
import com.xuexiang.xpage.enums.CoreAnim;

import java.util.ArrayList;
import java.util.List;

@Page(name = "WebSocket+service心跳检测1", anim = CoreAnim.slide)
public class WebSocketServiceFragment1 extends BaseFragment<FragmentWebsocketByServiceBinding> {

    FragmentWebsocketByServiceBinding binding;
    TextView tv_message;
    EditText et_send_message;
    EditText et_user_id;
    Button btn_send;
    Button btn_clear;
    Button btn_create;
    Button btn_close;
    StringBuffer stringBuffer = new StringBuffer();
    private JWebSocketClientService1.JWebSocketClientBinder binder;
    private JWebSocketClientService1 jWebSClientService;
    private JWebSocketClient client;
    private ChatMessageReceiver chatMessageReceiver;

    private List<ChatMessage> chatMessageList = new ArrayList<>();//消息列表

    @NonNull
    @Override
    protected FragmentWebsocketByServiceBinding viewBindingInflate(LayoutInflater inflater, ViewGroup container) {
        binding = FragmentWebsocketByServiceBinding.inflate(inflater, container, false);
        tv_message = binding.tvMessage;
        et_send_message = binding.etSendMessage;
        btn_send = binding.btnSend;
        btn_clear = binding.btnClear;
        et_user_id = binding.etUserId;
        btn_create = binding.btnCreate;
        btn_close = binding.btnClose;
        return binding;
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e("this", "服务与活动成功绑定");
            binder = (JWebSocketClientService1.JWebSocketClientBinder) iBinder;
            jWebSClientService = binder.getService();
            client = jWebSClientService.client;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e("this", "服务与活动成功断开");
        }
    };

    private class ChatMessageReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String message = intent.getStringExtra("message");
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setContent(message);
            chatMessage.setIsMeSend(0);
            chatMessage.setIsRead(1);
            chatMessage.setTime(System.currentTimeMillis() + "");
            chatMessageList.add(chatMessage);
            for (ChatMessage chatMessage1 : chatMessageList){
                stringBuffer.append(chatMessage1.getContent());


            }
            tv_message.setText(stringBuffer);
        }
    }


        @Override
        protected void initViews() {

            //启动服务
            startJWebSClientService();
            //绑定服务
            bindService();
            //注册广播
            doRegisterReceiver();
            //检测通知是否开启
//            checkNotification(getContext());
        }

        /**
         * 启动服务(websocket客户端服务)
         */
        private void startJWebSClientService() {
            Intent intent = new Intent(getActivity(), JWebSocketClientService1.class);
            getActivity().startService(intent);
        }

        /**
         * 绑定服务
         */
        private void bindService() {
            Intent bindIntent = new Intent(getContext(), JWebSocketClientService1.class);
            getActivity().bindService(bindIntent, serviceConnection, 0000);
        }

        /**
         * 动态注册广播
         */
        private void doRegisterReceiver() {
            chatMessageReceiver = new ChatMessageReceiver();
            IntentFilter filter = new IntentFilter("com.xxx.servicecallback.content");
            getActivity().registerReceiver(chatMessageReceiver, filter);
        }


        @Override
        protected void initListeners() {
            super.initListeners();
            btn_send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String content = et_send_message.getText().toString();
                    if (content.length() <= 0) {
                        XToastUtils.error("消息不能为空哟");
                        return;
                    }

                    if (client != null && client.isOpen()) {
                        JSONObject json = new JSONObject();
                        json.put("message",content);
                        json.put("toUserId","00001");
                        jWebSClientService.sendMsg(json.toString());

                        //暂时将发送的消息加入消息列表,实际以发送成功为准(也就是服务器返回你发的消息时)
                        ChatMessage chatMessage = new ChatMessage();
                        chatMessage.setContent(content);
                        chatMessage.setIsMeSend(1);
                        chatMessage.setIsRead(1);
                        chatMessage.setTime(System.currentTimeMillis() + "");
                        tv_message.setText(chatMessage.getContent());

                    } else {
                        XToastUtils.error("连接已断开,请稍等或重启App哟");
                    }

                }
            });
            btn_clear.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    tv_message.setText("");
                }
            });
            btn_create.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                }
            });
        }
    /**
     * 注销广播
     */
    private void unRegisterReceiver() {
        getActivity().unregisterReceiver(chatMessageReceiver);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        unRegisterReceiver();
        getActivity().unbindService(serviceConnection);
    }
}

效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值