优雅地使用OkHhttp3实现WebSocket长连接

前言

个人认为websocket其实本质上是对websocket协议的强调,这也是websocket建立长连接的灵魂,关于websocket协议不清楚的同学可以参看我的另外一篇博客:WebSocket协议的由来以及与Http协议的异同

最近项目中需要实现服务器实时更新数据到客户端的推送功能,打算使用websocket来完成,而刚好OkHttp从3.5版本开始新增了对于websocket的支持,以前都是提供了扩展库okhttp-ws,建议使用3.5以上版本,OkHttp作为Square公司的产品他的质量也是毋庸置疑的,因此我们打算借助OkHttp这个强大的第三方库来实现websocket的长连接功能。

开始使用

1,添加OkHttp依赖和网络权限

compile 'com.squareup.okhttp3:okhttp:3.8.1'
// 选择性添加依赖
compile 'com.squareup.okhttp3:mockwebserver:3.8.1'

MockWebServer是square出品的跟随okhttp一起发布,用来Mock测试服务器行为的库。MockWebServer使用在单元测试中,专门用来测试http请求。
关于更多更详细的MockWebServer库的使用介绍参见博客:https://www.cnblogs.com/ceshi2016/p/7884309.html

<uses-permission  android:name="android.permission.INTERNET" />

2,布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.yinzhendong.websocket.MainActivity">
 
 
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
 
        <LinearLayout
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="vertical">
 
 
            <Button
                android:id="@+id/start"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="连接"
                android:textSize="17sp"/>
 
            <TextView
                android:id="@+id/output"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:textSize="16sp"/>
        </LinearLayout>
 
    </ScrollView>
 
</RelativeLayout>

点击这个Button就建立连接,然后服务器返回的消息都显示在下面的TextView上。

3,MainActivity代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
 
    private Button btnStart;
    private TextView tvOutput;
    private WebSocket mSocket;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnStart = (Button) findViewById(R.id.start);
        tvOutput = (TextView) findViewById(R.id.output);
        btnStart.setOnClickListener(this);
    }
 
    private void start() {
        OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
                .readTimeout(3, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(3, TimeUnit.SECONDS)//设置写的超时时间
                .connectTimeout(3, TimeUnit.SECONDS)//设置连接超时时间
                .build();
 
        Request request = new Request.Builder().url("ws://echo.websocket.org").build();
        EchoWebSocketListener socketListener = new EchoWebSocketListener();
        mOkHttpClient.newWebSocket(request, socketListener);
        mOkHttpClient.dispatcher().executorService().shutdown();
    }
 
    /**
     * @param v The view that was clicked.
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start://开始连接
                start();
                break;
        }
    }
 
 
    private final class EchoWebSocketListener extends WebSocketListener {
 
        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            super.onOpen(webSocket, response);
            mSocket = webSocket;
            String openid = "1";
            //连接成功后,发送登录信息
            String message = "{\"type\":\"login\",\"user_id\":\""+openid+"\"}";
            mSocket.send(message);
            mSocket.send("welcome");
            mSocket.send(ByteString.decodeHex("adef"));
            mSocket.close(1000, "再见");
            output("连接成功!");
        }
 
        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            super.onMessage(webSocket, bytes);
            output("receive bytes:" + bytes.hex());
        }
 
        @Override
        public void onMessage(WebSocket webSocket, String text) {
            super.onMessage(webSocket, text);
            output("receive text:" + text);
            //收到服务器端发送来的信息后,每隔25秒发送一次心跳包
            final String message = "{\"type\":\"heartbeat\",\"user_id\":\"heartbeat\"}";
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    mSocket.send(message);
                }
            },25000);
        }
 
        @Override
        public void onClosed(WebSocket webSocket, int code, String reason) {
            super.onClosed(webSocket, code, reason);
            output("closed:" + reason);
        }
 
        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {
            super.onClosing(webSocket, code, reason);
            output("closing:" + reason);
        }
 
        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            output("failure:" + t.getMessage());
        }
    }
 
    private void output(final String text) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tvOutput.setText(tvOutput.getText().toString() + "\n\n" + text);
            }
        });
    }
}

监听类中重写了WebSocketListener中的几个方法,这几个方法很好理解,是用来异步回调的,这里简单说一下:onOpen当WebSocket和远程建立连接时回调;两个onMessage就是接收到消息时回调,只是消息内容的类型不同;onClosing是当远程端暗示没有数据交互时回调(即此时准备关闭,但连接还没有关闭);onClosed就是当连接已经释放的时候被回调;onFailure当然是失败时被回调(包括连接失败,发送失败等)。
注意:这些方法执行均在后台线程中,如需要更新UI,需要注意线程间通信,可借助Handler或者eventBus来实现。

WebSocket的2个方法:
1,send用来发送消息,此方法能接收两种类型的参数:String和ByteString;因为OkHttp将使用它自己的后台线程发送数据,所以send能够在任何线程中调用而不用担心阻塞当前线程(也不会有抛出NetworkOnMainThreadException的风险)。 这里唯一的警告就是正的返回结果(即返回true)仅仅表明消息被插入队列,但是它并不会反应出传送的结果。
2,close用来关闭连接。
OkHttp提供两个方法来关闭连接:
1,webSocket.close(0, “bye”);
请求服务器优雅地关闭连接然后等待确认。在关闭之前,所有已经在队列中的消息将被传送完毕。 既然涉及到交互,那么socket可能不会立即关闭。如果初始化和关闭连接是和Activity的生命周期绑定的(比如onPause/onResume),有一些消息可能是在close被调用之后接收到,所以这需要小心去处理。
2,cncel()
cancel更加残忍:它会丢弃所有已经在队列中的消息然后残忍地关闭socket。这样也有优点:不必等待家政(housekeeping)和已在队列中消息的传送。然而,选择cancel还是close取决于使用场景。

上面的ws://echo.websocket.org,是WebSocket官网提供的测试url,在我们自己项目的服务器还没搭建好的情况下可以使用该url来进行测试。

在这里插入图片描述
2,扩展案例(在上面的基础上添加心跳保持,断线重连等):
https://blog.csdn.net/lhy349/article/details/79699394
https://blog.csdn.net/qaz520929/article/details/80496281
综合以上两个即可。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Android实现WebSocket长连接,可以通过以下步骤来实现: 1. 导入WebSocket库:首先,需要在Android项目的build.gradle文件添加WebSocket库的依赖项。例如,在dependencies块添加以下代码: ```java implementation 'org.java-websocket:Java-WebSocket:1.5.1' ``` 2. 创建WebSocket客户端:在需要使用WebSocket长连接的地方,创建一个WebSocket客户端对象。例如,可以在Activity或Fragment创建WebSocketClient对象。 ```java WebSocketClient mWebSocketClient = new WebSocketClient(URI.create("ws://your_server_url"), new Draft_17()) { // WebSocket事件回调方法 @Override public void onOpen(ServerHandshake serverHandshake) { // 连接成功后的处理逻辑 } @Override public void onMessage(String message) { // 接收到服务器发送的消息时的处理逻辑 } @Override public void onClose(int code, String reason, boolean remote) { // 连接关闭时的处理逻辑 } @Override public void onError(Exception ex) { // 连接发生错误时的处理逻辑 } }; ``` 3. 连接WebSocket服务器:调用WebSocketClient对象的connect()方法来连接WebSocket服务器。 ```java mWebSocketClient.connect(); ``` 4. 发送和接收消息:可以使用WebSocketClient对象的send()方法发送消息,并在onMessage()方法接收服务器发送的消息。 ```java mWebSocketClient.send("Hello, Server!"); ``` 5. 关闭WebSocket连接:当不再需要长连接时,可以调用WebSocketClient对象的close()方法来关闭WebSocket连接。 ```java mWebSocketClient.close(); ``` 注意:以上代码只是简单示例,实际使用时可能需要根据具体需求进行适当的改进和处理,例如添加重连机制、处理心跳包等。 ### 回答2: Android实现WebSocket长连接可以使用Java开发,具体步骤如下: 1. 引入相应的WebSocket依赖库,如Java-WebSocket库。 2. 创建一个WebSocket连接类,继承自WebSocketClient类,并实现的各个回调方法。 3. 在连接类,重写onOpen()方法,在该方法进行WebSocket连接的初始化和握手操作。 4. 重写onMessage()方法,用于接收服务器发送的消息,可以在此方法处理收到的消息。 5. 重写onClose()方法,处理WebSocket连接关闭的逻辑。 6. 重写onError()方法,处理WebSocket连接发生错误的逻辑。 7. 在需要使用WebSocket长连接的地方,创建WebSocket连接对象,传入服务器的WebSocket地址和端口号等参数。 8. 调用WebSocket连接对象的connect()方法,发起WebSocket连接。 9. 可以通过WebSocket连接对象的send()方法发送消息给服务器。 10. 在连接不需要时,调用WebSocket连接对象的close()方法关闭WebSocket连接。 总结:通过引入相应的WebSocket依赖库,并创建WebSocket连接类来实现WebSocket长连接,可以实现Android应用与服务端的实时通讯。在连接的各个回调方法,可以处理接收到的消息和连接关闭等事件。使用WebSocket连接对象的connect()方法发起连接,send()方法发送消息,close()方法关闭连接。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值