在WebSocket概念出来之前,数据刷新、列表刷新、通讯、聊天等功能的实现都需要通过轮询的方式,每隔一段时候,就发出一个询问,来更新界面,了解服务器有没有新的信息,轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
而使用WebSocket技术之后,当服务器有了新的数据,会主动通知进行数据处理
这样就实现了实时通讯功能,双向通信,与服务器保持长连接
客服通信、ui数据刷新等都可简单高效实现
具体实现
1.依赖
implementation "org.java-websocket:Java-WebSocket:1.4.0"
2.创建一个消息接收和推送的实体类
public class WebSocketEvent {
private String message;
public WebSocketEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3.在AndroidManifest.xml中注册一个service,
<service android:name=".utils.WebSocketService"
/>
websocket保活需要service中开启,并且适配保活方法(白色保活、灰色保活),同时创建心跳检测重连的方法,维持websocket与后台建立的长连接
public class WebSocketService extends Service {
private final static String TAG = "kkkkkkkkkkkkkkkkkkk";
public JWebSocketClient client;
private JWebSocketClientBinder mBinder = new JWebSocketClientBinder();
private final static int GRAY_SERVICE_ID = 1001;
private static final long CLOSE_RECON_TIME = 15000;//连接断开或者连接错误立即重连
private Notification notification;
//用于Activity和service通讯
public class JWebSocketClientBinder extends Binder {
public WebSocketService getService() {
return WebSocketService.this;
}
}
//灰色保活
public static class GrayInnerService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(GRAY_SERVICE_ID, new Notification());
stopForeground(true);
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//初始化WebSocket
initSocketClient();
mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测
//设置service为前台服务,提高优先级
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
//Android4.3以下 ,隐藏Notification上的图标
startForeground(GRAY_SERVICE_ID, new Notification());
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
//Android4.3 - Android8.0,隐藏Notification上的图标
Intent innerIntent = new Intent(this, GrayInnerService.class);
startService(innerIntent);
startForeground(GRAY_SERVICE_ID, new Notification());
} else {
Intent nfIntent = new Intent(this, MainActivity.class);
Notification.Builder builder = new Notification.Builder(this.getApplicationContext())
.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setSmallIcon(R.mipmap.logou) // 设置状态栏内的小图标
.setPriority(Notification.PRIORITY_MIN)
.setContentTitle(getResources().getString(R.string.app_name))
.setContentText("正在前台运行") // 设置上下文内容
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
//修改安卓8.1以上系统报错
NotificationChannel notificationChannel = new NotificationChannel("1", "CHANNEL_ONE_NAME", NotificationManager.IMPORTANCE_MIN);
notificationChannel.enableLights(false);//如果使用中的设备支持通知灯,则说明此通知通道是否应显示灯
notificationChannel.setShowBadge(false);//是否显示角标
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(notificationChannel);
builder.setChannelId("1");
}
// 获取构建好的Notification
notification = builder.build();
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
startForeground(GRAY_SERVICE_ID, notification);
}
return START_STICKY;
}
//这里是处理webscoket
private void initSocketClient() {
String url = "ws://端口号:ip地址"; //协议标识符是ws
//(如果加密,则为wss),服务器网址就是 URL。
URI uri = URI.create(url);
client = new JWebSocketClient(uri) {
@Override
public void onMessage(String message) {
//message就是接收到的消息
Log.d(TAG, "WebSocketService收到的消息:" + message);
//这里我通过EvenBus获取接收的消息并更新ui
EventBus.getDefault().post(new WebSocketEvent(message));
}
@Override
public void onOpen(ServerHandshake handShakeData) {//在webSocket连接开启时调用
Log.d(TAG, "WebSocket 连接成功" );
}
@Override
public void onClose(int code, String reason, boolean remote) {//在连接断开时调用
Log.d(TAG, "onClose() 连接断开_reason:" + reason );
mHandler.removeCallbacks(heartBeatRunnable);
mHandler.postDelayed(heartBeatRunnable, CLOSE_RECON_TIME);//开启心跳检测
}
@Override
public void onError(Exception ex) {//在连接出错时调用
Log.d(TAG, "onError() 连接出错:" + ex.getMessage() );
mHandler.removeCallbacks(heartBeatRunnable);
mHandler.postDelayed(heartBeatRunnable, CLOSE_RECON_TIME);//开启心跳检测
}
};
connect();
}
/**
* 连接WebSocket
*/
private void connect() {
new Thread() {
@Override
public void run() {
try {
//connectBlocking多出一个等待操作,会先连接再发送,否则未连接发送会报错
client.connectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
/**
* 发送消息
*/
public void sendMsg(String msg) {
if (null != client) {
try {
client.send(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
closeConnect();
super.onDestroy();
}
/**
* 断开连接
*/
public void closeConnect() {
mHandler.removeCallbacks(heartBeatRunnable);
try {
if (null != client) {
client.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
client = null;
}
}
// -------------------------------------WebSocket心跳检测------------------------------------------------
private static final long HEART_BEAT_RATE = 10 * 1000;
//每隔10秒进行一次对长连接的心跳检测
private Handler mHandler = new Handler();
private Runnable heartBeatRunnable = new Runnable() {
@Override
public void run() {
if (client != null) {
if (client.isClosed()) {
reconnectWs();
Log.d(TAG, "心跳包检测WebSocket连接状态:已关闭" );
} else if (client.isOpen()) {
Log.d(TAG, "心跳包检测WebSocket连接状态:已连接" );
} else {
Log.d(TAG, "心跳包检测WebSocket连接状态:已断开" );
}
} else {
//如果client已为空,重新初始化连接
initSocketClient();
Log.d(TAG, "心跳包检测WebSocket连接状态:client已为空,重新初始化连接" );
}
//每隔一定的时间,对长连接进行一次心跳检测
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
/**
* 开启重连
*/
private void reconnectWs() {
mHandler.removeCallbacks(heartBeatRunnable);
new Thread() {
@Override
public void run() {
try {
Log.d(TAG, "开启重连" );
client.reconnectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
public class AuxiliaryService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startNotification();
return super.onStartCommand(intent, flags, startId);
}
/** 启动通知*/
private void startNotification(){
Notification notification = new Notification();
this.startForeground(GRAY_SERVICE_ID, notification);
stopSelf(); //关键 如果AuxiliaryService 没有与什么组件绑定 系统就会回收
stopForeground(true);
}
}
}
4.服务中的初始化WebSocket写完以后,在自定义的application中开启webSocketService,application是程序的生命周期,在application可进行心跳检测操作,维持webSocket
public class MyApp extends Application {
public static MyApp app;
public static MyApp instance;
public WebSocketService mWebSocketService;
private final static String TAG = "hahahhahhahahhahahha";
private static final String DEVICE_TOKEN = "device_token";//设备token
@RequiresApi(api = Build.VERSION_CODES.P)
@Override
public void onCreate() {
super.onCreate();
app = this;
//开启
startWebSocketService(DEVICE_TOKEN);
}
private static final long HEART_BEAT_RATE = 60 * 1000;//每隔1分钟发送空消息保持WebSocket长连接
public static MyApp getInstance() {
if (instance == null) {
instance = new MyApp();
}
return instance;
}
private Handler mHandler = new Handler();
private Runnable webSocketRunnable = new Runnable() {
@Override
public void run() {
if (mWebSocketService != null &&
mWebSocketService.client != null && mWebSocketService.client.isOpen()) {
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("from","");
jsonObject.put("to", "");
mWebSocketService.sendMsg(jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
//每隔一定的时间,对长连接进行一次心跳检测
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
public WebSocketService getWebSocketService() {
return mWebSocketService;
}
/**
* 开启并绑定WebSocket服务
*/
public void startWebSocketService(String deviceToken) {
Intent bindIntent = new Intent(this, WebSocketService.class);
bindIntent.putExtra(DEVICE_TOKEN, deviceToken);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//android8.0以上通过startForegroundService启动service
startForegroundService(bindIntent);
} else {
startService(bindIntent);
}
bindService(bindIntent, serviceConnection, BIND_AUTO_CREATE);
mHandler.removeCallbacks(webSocketRunnable);
mHandler.postDelayed(webSocketRunnable, HEART_BEAT_RATE);//开启心跳检测
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//服务与活动成功绑定
mWebSocketService = ((WebSocketService.JWebSocketClientBinder) iBinder).getService();
Log.d(TAG, "WebSocket服务与Application成功绑定");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
//服务与活动断开
mWebSocketService = null;
Log.d(TAG, "WebSocket服务与Application成功断开: ");
}
};
}
最后不要忘记自定义application在清单文件中注册