文章目录
- 简介
- 基本使用
- 后台运行
- 从Service中更新Activity的UI
- 总结流程
- 心跳检测和重连
- 资源下载
这篇文章是参考简书作者
ChaoYoung
的
Android WebSocket实现即时通讯功能 写的,不过侧重点是如何使用 Websocket,通过阅读本篇文章你可以学到目录中的内容,重点不放在即时通讯
简介
WebSocket,简而言之,就是一个可以建立长连接的全双工(full-duplex)通信协议,允许服务器端主动发送信息给客户端
对于使用websocket协议,Android端已经有些成熟的框架了,现在学习一下Java-WebSocket这个开源框架
GitHub地址
基本使用
1、引入
implementation "org.java-websocket:Java-WebSocket:1.4.0"
2、加入权限
<uses-permission android:name="android.permission.INTERNET" />
3、新建客户端类
新建一个客户端类并继承WebSocketClient,需要实现它的四个抽象方法和构造函数
public class JWebsocketClient extends WebSocketClient {
public JWebsocketClient(URI serverUri) {
//super(serverUri);
super(serverUri,new Draft_6455());
//构造方法中的new Draft_6455()代表使用的协议版本,这里可以不写或者写成这样即可
}
public void onOpen(ServerHandshake handshakedata) {
//websocket连接开启时调用
}
public void onMessage(String message) {
//接收到消息时调用
}
public void onClose(int code, String reason, boolean remote) {
//连接断开时调用
}
public void onError(Exception ex) {
//连接出错时调用
}
}
4、建立websocket连接
建立连接只需要初始化此客户端再调用连接方法,需要注意的是WebSocketClient对象是不能重复使用的,所以不能重复初始化,其他地方只能调用当前这个Client
初始化客户端时需要传入websocket地址(测试地址:ws://echo.websocket.org)
URI uri = URI.create("ws://echo.websocket.org");
JWebsocketClient client = new JWebsocketClient(uri);
连接时可以使用connect()
方法或connectBlocking()
方法,建议使用connectBlocking()
方法,它多出一个等待操作,会先连接再发送
运行之后,看到打印了onOpen,说明连接建立了
5、发送消息
if(client!=null && client.isOpen()){
client.send("hello");
}
6、关闭socket连接
关闭连接调用close()方法,最后为了避免重复实例化WebSocketClient对象,关闭时一定要将对象置空
try {
if (null != client) {
client.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
client = null;
}
后台运行
如果希望在后台保持运行,需要的是建一个Service,将websocket的逻辑放入服务中运行,让websocket保持连接
1、新建Service
新建一个 Service,在启动 Service 时实例化 WebSocketClient 对象并建立连接,将上面的代码搬到服务里即可
关于 Service 的教程可以查看【达内课程】Service(上)
所以我们创建 JWebSocketClientService
public class JWebSocketClientService extends Service {
JWebsocketClient client;
public void onCreate() {
super.onCreate();
}
public int onStartCommand(Intent intent, int flags, int startId) {
//初始化websocket
initSocketClient();
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
closeConnect();
super.onDestroy();
}
public IBinder onBind(Intent intent) {
return null;
}
private void initSocketClient() {
URI uri = URI.create("ws://echo.websocket.org");
client = new JWebsocketClient(uri);
connect();
}
private void connect() {
new Thread() {
public void run() {
try {
//connectBlocking多出一个等待操作,会先连接再发送,否则未连接发送会报错
client.connectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
private void closeConnect() {
try {
if (null != client) {
client.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
client = null;
}
}
}
在 manifest 中注册
<service android:name=".JWebSocketClientService"/>
在Activity中启动
private void startJWebSClientService() {
Intent intent = new Intent(mContext, JWebSocketClientService.class);
startService(intent);
}
2、Service和Activity之间通讯
从之前讲 Service 的教程:【达内课程】Service(下)可知,要进行服务和活动之间的通讯,需要用到Service中的onBind()
方法
public class JWebSocketClientService extends Service {
......
private InnerIBinder mBinder = new InnerIBinder();
......
public IBinder onBind(Intent intent) {
return mBinder;
}
private class InnerIBinder extends Binder {
public JWebSocketClientService getService() {
return JWebSocketClientService.this;
}
}
......
}
接下来就需要对应的Activity绑定Service,并获取Service的东西
public class SecondActivity extends AppCompatActivity {
private JWebsocketClient client;
private InnerServiceConnection serviceConnection = new InnerServiceConnection();
private JWebSocketClientService.InnerIBinder binder;
private JWebSocketClientService jWebSClientService;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//启动service
startJWebSClientService();
//绑定服务
bindService();
}
private void startJWebSClientService() {
Intent intent = new Intent(this, JWebSocketClientService.class);
startService(intent);
}
private void bindService() {
Intent bindIntent = new Intent(this, JWebSocketClientService.class);
bindService(bindIntent, serviceConnection, BIND_AUTO_CREATE);
}
private class InnerServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("Service","onServiceConnected()->当Activity和Service连接");
binder = (JWebSocketClientService.InnerIBinder) service;
jWebSClientService = binder.getService();
client = jWebSClientService.client;
}
public void onServiceDisconnected(ComponentName name) {
Log.d("Service","onServiceConnected()->当Activity和Service断开连接");
}
}
}
这里首先创建了一个 InnerServiceConnection 匿名类,在里面重写onServiceConnected()
和onServiceDisconnected()
方法,这两个方法会在活动与服务成功绑定以及连接断开时调用
在onServiceConnected()
首先得到 InnerIBinder 的实例,有了这个实例便可调用服务的任何public方法,这里调用getService()
方法得到 Service 实例,得到了 Service 实例也就得到了WebSocketClient 对象,也就可以在活动中发送消息了
从Service中更新Activity的UI
当 Service 中接收到消息时需要更新 Activity 中的界面,方法有很多,这里我们利用广播来实现,在对应 Activity 中定义广播接收者,Service 中收到消息发出广播即可
public class SecondActivity extends AppCompatActivity{
......
private ChatMessageReceiver chatMessageReceiver;
protected void onCreate(Bundle savedInstanceState) {
......
//注册广播
doRegisterReceiver();
}
......
private class ChatMessageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String message=intent.getStringExtra("message");
}
}
private void doRegisterReceiver() {
chatMessageReceiver = new ChatMessageReceiver();
IntentFilter filter = new IntentFilter("CCTV5");
registerReceiver(chatMessageReceiver, filter);
}
}
关于广播接收者的教程可以参考之前的:【达内课程】BroadcastReceiver(上),广播这里的代码,上边教程讲的很明白,这里不赘述
当 Service 中接收到消息时发出广播,就能在 ChatMessageReceiver 里接收广播了,我们修改 JWebSocketClientService 中代码,为了方便修改代码,重写 JWebsocketClient 的onMessage
方法
private void initSocketClient() {
URI uri = URI.create("ws://echo.websocket.org");
client = new JWebsocketClient(uri){
public void onMessage(String message) {
Intent intent = new Intent();
intent.setAction("CCTV5");
intent.putExtra("message", message);
sendBroadcast(intent);
}
};
connect();
}
获取广播传过来的消息后即可更新UI
总结流程
由于连接的 websocket 的地址是 "ws://echo.websocket.org"
,当我们给它发送一个消息“hello”时,它会返回我们一个相同的消息“hello”
所以利用这个我们可以测试发送和接收消息,流程如下
- 我们在Activity中增加一个EditText和一个Button,点击Button,向服务器发送消息
- 服务器接收到消息后,向我们返回一个相同的消息
- 我们这边接收到消息时,JWebSocketClientService 中
onMessage
会执行,我们会拿到接收的消息,同时发出广播 - Activity中的广播接收器,接收到广播时,就可以更新UI,由于这不是重点,我们简单显示下
Activity中修改的代码
//button添加点击事件,向服务器发送消息
btn_send.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if(client!=null && client.isOpen()){
client.send(et_message.getText().toString());
}
}
});
//接收到广播后,将服务器返回的消息显示出来
private class ChatMessageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String message=intent.getStringExtra("message");
tv_message.append(message+"\n");
}
}
心跳检测和重连
由于很多不确定因素会导致 websocket 连接断开,例如网络断开,所以需要保证 websocket 的连接稳定性,这就需要加入心跳检测和重连
心跳检测其实就是个定时器,每隔一段时间检测一次,如果连接断开则重连,Java-WebSocket 框架在目前最新版本中有两个重连的方法,分别是reconnect()
和reconnectBlocking()
,这里同样使用后者
在service中增加以下代码
// -------------------------------------websocket心跳检测------------------------------------------------
private static final long HEART_BEAT_RATE = 10 * 1000;//每隔10秒进行一次对长连接的心跳检测
private Handler mHandler = new Handler();
private Runnable heartBeatRunnable = new Runnable() {
public void run() {
Log.e("JWebSocketClientService", "心跳包检测websocket连接状态");
if (client != null) {
if (client.isClosed()) {
reconnectWs();
}
} else {
//如果client已为空,重新初始化连接
initSocketClient();
}
//每隔一定的时间,对长连接进行一次心跳检测
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
/**
* 开启重连
*/
private void reconnectWs() {
mHandler.removeCallbacks(heartBeatRunnable);
new Thread() {
public void run() {
try {
Log.e("JWebSocketClientService", "开启重连");
client.reconnectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
然后在onStartCommand
中开启心跳检测
mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测
运行程序后,每隔10s就会检测是否开启ws
资源下载