欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在Android中使用WebSocket實現(xiàn)消息通信的方法詳解

 更新時間:2020年07月14日 16:10:22   作者:黃林晴  
這篇文章主要介紹了在Android中使用WebSocket實現(xiàn)消息通信的方法詳解,消息推送功能可以說移動APP不可缺少的功能之一,使用WebSocket實現(xiàn)消息推送功能。感興趣的可以了解一下

前言

消息推送功能可以說移動APP不可缺少的功能之一,一般簡單的推送我們可以使用第三方推送的SDK,比如極光推送、信鴿推送等,但是對于消息聊天這種及時性有要求的或者三方推送不滿足業(yè)務(wù)需求的,我們就需要使用WebSocket實現(xiàn)消息推送功能。

基本流程

WebSocket是什么,這里就不做介紹了,我們這里使用的開源框架是https://github.com/TakahikoKawasaki/nv-websocket-client

基于開源協(xié)議我們封裝實現(xiàn)WebSocket的連接、注冊、心跳、消息分發(fā)、超時任務(wù)功能,基本流程如下:

連接功能

首先我們新建一個項目,在build.grade中添加配置

compile 'com.neovisionaries:nv-websocket-client:2.2'

新建websocket管理類WsManger

public class WsManager {
 
 private volatile static WsManager wsManger;
 
 private WsManager() {
 }
 
 public static WsManager getWsManger() {
 if (wsManger == null) {
 synchronized (WsManager.class) {
 if (wsManger == null) {
  wsManger = new WsManager();
 }
 }
 }
 return wsManger;
 }
 
 
}

接下來添加連接方法,我們將webSocket的狀態(tài)分為三種,新建WsStatue枚舉類對應(yīng)起來

public enum WsStatus {
 
 /**
 * 連接成功
 */
 CONNECT_SUCCESS,
 /**
 * 連接失敗
 */
 CONNECT_FAIL,
 /**
 * 正在連接
 */
 CONNECTING;
}

連接方法如下所示:

/**
 * 連接方法 這里要判斷是否登錄 此處省略
 */
public void connect() {
 //WEB_SOCKET_API 是連接的url地址,
 // CONNECT_TIMEOUT是連接的超時時間 這里是 5秒
 try {
 ws = new WebSocketFactory().createSocket(WEB_SOCKET_API, CONNECT_TIMEOUT)
 //設(shè)置幀隊列最大值為5
 .setFrameQueueSize(5)
 //設(shè)置不允許服務(wù)端關(guān)閉連接卻未發(fā)送關(guān)閉幀
 .setMissingCloseFrameAllowed(false)
 //添加回調(diào)監(jiān)聽
 .addListener(new WsListener())
 //異步連接
 .connectAsynchronously();
 } catch (IOException e) {
 e.printStackTrace();
 }
 setStatus(WsStatus.CONNECTING);
}

調(diào)用連接方法后 我們來看連接的回調(diào) 也就是WsListener

/**
 * websocket回調(diào)事件
 */
private class WsListener extends WebSocketAdapter {
 
 
 @Override
 public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
 Log.d(TAG, "onConnected: 連接成功");
 }
 
 @Override
 public void onConnectError(WebSocket websocket, WebSocketException exception) throws Exception {
 Log.d(TAG, "onConnectError: 連接失敗");
 }
 
 @Override
 public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame,
  WebSocketFrame clientCloseFrame,
  boolean closedByServer) throws Exception {
 Log.d(TAG, "onDisconnected: 斷開連接");
 
 }
 
 @Override
 public void onTextMessage(WebSocket websocket, String text) throws Exception {
 Log.d(TAG, "onTextMessage: 收到消息:" + text);
 }
}

下面我們調(diào)用連接方法

WsManager.getWsManger().connect();

運行項目我們可以看到如下打?。?/p>

 

此處我們要做的處理是,如果收到連接失敗或者斷開連接的回調(diào) 需要重新連接,我們重新調(diào)用一次連接方法即可,并且如果超過三次重連失敗,我們在業(yè)務(wù)中可以通過調(diào)用接口來獲取數(shù)據(jù),避免數(shù)據(jù)丟失,此處細節(jié)省略。

協(xié)議封裝

此處協(xié)議如下所示:

{
 "action":"",
 "requestChild":{
 "clientType":"",
 "id":""
 }
}

心跳、發(fā)送請求都屬于客戶端主動發(fā)送請求,對于請求結(jié)果我們分為成功和失敗以及超時,發(fā)送超時我們是收不到服務(wù)器任何回復(fù)的,所以我們需要在發(fā)送之后將發(fā)送放在超時任務(wù)隊列中,如果請求成功將任務(wù)從超時隊列中移除,超時從超時隊列中獲取任務(wù)重新請求。

超時任務(wù)隊列中回調(diào)有成功、失敗、超時。

我們按照上述協(xié)議,新增對應(yīng)實體類,采用Builder設(shè)計模式

public class Request {
 
 /**
 * 行為
 */
 private String action;
 
 /**
 * 請求體
 */
 private RequestChild req;
 
 
 /**
 * 請求次數(shù)
 */
 private transient int reqCount;
 
 /**
 * 超時的時間
 */
 private transient int timeOut;
 
 
 public Request() {
 }
 
 
 public Request(String action, int reqCount, int timeOut, RequestChild req) {
 this.action = action;
 this.req = req;
 this.reqCount = reqCount;
 this.timeOut = timeOut;
 }
 
 
 public static class Builder {
 //action 請求類型
 private String action;
 //請求子類數(shù)據(jù) 按照具體業(yè)務(wù)劃分
 private RequestChild req;
 //請求次數(shù) 便于重試
 private int reqCount;
 //超時時間
 private int timeOut;
 
 public Builder action(String action) {
 this.action = action;
 return this;
 }
 
 
 public Builder req(RequestChild req) {
 this.req = req;
 return this;
 }
 
 
 public Builder reqCount(int reqCount) {
 this.reqCount = reqCount;
 return this;
 }
 
 public Builder timeOut(int timeOut) {
 this.timeOut = timeOut;
 return this;
 }
 
 public Request build() {
 return new Request(action, reqCount, timeOut, req);
 }
 
 }
}
 

public class RequestChild {
 
 /**
 * 設(shè)備類型
 */
 private String clientType;
 
 
 /**
 * 用于用戶注冊的id
 */
 private String id;
 
 public RequestChild(String clientType, String id) {
 this.clientType = clientType;
 this.id = id;
 }
 
 public RequestChild() {
 }
 
 
 public static class Builder {
 private String clientType;
 private String id;
 
 public RequestChild.Builder setClientType(String clientType) {
 this.clientType = clientType;
 return this;
 }
 
 
 public RequestChild.Builder setId(String id) {
 this.id = id;
 return this;
 }
 
 
 public RequestChild build() {
 return new RequestChild(clientType, id);
 }
 
 }
 
 
}

我們添加一個發(fā)送請求的方法如下:

/**
 * 發(fā)送請求
 *
 * @param request 請求體
 * @param reqCount 請求次數(shù)
 * @param requestListern 請求回調(diào)
 */
private void senRequest(Request request, final int reqCount, final RequestListern requestListern) {
 if (!isNetConnect()) {
 requestListern.requestFailed("網(wǎng)絡(luò)未連接");
 return;
 }
 
}

請求回調(diào)如下所示

public interface RequestListern {
 
 /**
 * 請求成功
 */
 void requestSuccess();
 
 /**
 * 請求失敗
 *
 * @param message 請求失敗消息提示
 */
 void requestFailed(String message);
}

接著我們要把請求放在超時隊列中,新建超時任務(wù)類,對應(yīng)的分別是請求參數(shù)、請求回調(diào)、任務(wù)調(diào)度

public class TimeOutTask {
 
 
 /**
 * 請求主體
 */
 private Request request;
 
 /**
 * 通用返回
 */
 private RequestCallBack requestCallBack;
 
 /**
 * r任務(wù)
 */
 private ScheduledFuture scheduledFuture;
 
 
 public TimeOutTask(Request request,
  RequestCallBack requestCallBack,
  ScheduledFuture scheduledFuture) {
 this.request = request;
 this.requestCallBack = requestCallBack;
 this.scheduledFuture = scheduledFuture;
 }
 
 public ScheduledFuture getScheduledFuture() {
 return scheduledFuture;
 }
 
 public void setScheduledFuture(ScheduledFuture scheduledFuture) {
 this.scheduledFuture = scheduledFuture;
 }
 
 public Request getRequest() {
 return request;
 }
 
 public void setRequest(Request request) {
 this.request = request;
 }
 
 public RequestCallBack getRequestCallBack() {
 return requestCallBack;
 }
 
 public void setRequestCallBack(RequestCallBack requestCallBack) {
 this.requestCallBack = requestCallBack;
 }
 
}

RequestCallBack是超時任務(wù)的回調(diào),只是比請求回調(diào)多了個超時,因為超時的處理機制是一樣的,所以這里我們沒必要將超時回調(diào)到請求中

public interface RequestCallBack {
 
 /**
 * 請求成功
 */
 void requestSuccess();
 
 /**
 * 請求失敗
 *
 * @param request 請求體
 * @param message 請求失敗的消息
 */
 void requestFailed(String message, Request request);
 
 /**
 * 請求超時
 *
 * @param request 請求體
 */
 void timeOut(Request request);
}
/**
 * 添加超時任務(wù)
 */
private ScheduledFuture enqueueTimeout(final Request request, final long timeout) {
 Log.d(TAG, " " + "enqueueTimeout: 添加超時任務(wù)類型為:" + request.getAction());
 return executor.schedule(new Runnable() {
 @Override
 public void run() {
 TimeOutTask timeoutTask = callbacks.remove(request.getAction());
 if (timeoutTask != null) {
 timeoutTask.getRequestCallBack().timeOut(timeoutTask.getRequest());
 }
 }
 }, timeout, TimeUnit.MILLISECONDS);
}

超時任務(wù)的方法是通過任務(wù)調(diào)度定時調(diào)用,請求成功后我們會把超時任務(wù)移除,當?shù)搅顺瑫r時間時,任務(wù)還存在就說明任務(wù)超時了。

每次的任務(wù)我們以action為鍵值存在hashMap中

private Map<String, CallbackWrapper> callbacks = new HashMap<>();

將任務(wù)放入超時任務(wù)代碼如下所示:

final ScheduledFuture timeoutTask = enqueueTimeout(request, request.getTimeOut());
 
final RequestCallBack requestCallBack = new RequestCallBack() {
 @Override
 public void requestSuccess() {
 requestListern.requestSuccess();
 }
 
 @Override
 public void requestFailed(String message, Request request) {
 requestListern.requestFailed(message);
 }
 
 @Override
 public void timeOut(Request request) {
 timeOutHanlder(request);
 }
};
callbacks.put(request.getAction(),
 new CallbackWrapper(request, requestCallBack, timeoutTask));

一般而言,任務(wù)超時都是由于連接原因?qū)е拢晕覀冞@里可以嘗試重試一次,如果還是超時,通過 timeOutHanlder(request);方法 進行重新連接,重連代碼和連接代碼一樣,這里就省略了,做好這步操作,我們就可以發(fā)送消息了。

/**
 * 超時任務(wù)
 */
private void timeOutHanlder(Request requset) {
 setStatus(WsStatus.CONNECT_FAIL);
 //這里假裝有重連
 Log.d(TAG, "timeOutHanlder: 請求超時 準備重連");
}

到這里我們的流程基本可以走通了。

心跳

首先我們要了解下心跳的作用是什么,心跳是在連接成功后,通過固定的間隔時間向服務(wù)器發(fā)送詢問,當前是否還在線,有很多人說心跳失敗我們就重連,成功就繼續(xù)心跳,但是這里要注意的是,我們一般是收不到心跳失敗回調(diào)的,心跳也是向服務(wù)器發(fā)送數(shù)據(jù),所以我們要將所有的主動請求都放在超時任務(wù)隊列中,

所以對websocket來說 請求結(jié)果有三種:成功、失敗、超時,對于用戶 只有成功、失敗即可。

至于心跳、注冊等請求發(fā)送的數(shù)據(jù)是什么,這就得看我們與服務(wù)端定的協(xié)議是什么樣了,通常來說 分為action 和 requestBody,協(xié)議格式我們再第二步已經(jīng)封裝好了,這里我們以心跳任務(wù)為例驗證上面的封裝。

/**
 * 心跳
 */
void keepAlive() {
 
 Request request = new Request.Builder()
 .reqCount(0)
 .timeOut(REQUEST_TIMEOUT)
 .action(ACTION_KEEPALIVE).build();
 
 WsManager.getWsManger().senRequest(request, request.getReqCount() + 1, new RequestListern() {
 @Override
 public void requestSuccess() {
 Log.d(TAG, "requestSuccess: 心跳發(fā)送成功了");
 }
 
 @Override
 public void requestFailed(String message) {
 }
 });
}

我們每間隔10s中開啟一次心跳任務(wù)

/**
 * 開始心跳
 */
public void startKeepAlive() {
 mHandler.postDelayed(mKeepAliveTask, HEART_BEAT_RATE);
}
/**
 * 心跳任務(wù)
 */
private Runnable mKeepAliveTask = new Runnable() {
 
 @Override
 public void run() {
 keepAlive();
 mHandler.removeCallbacks(mKeepAliveTask);
 mHandler.postDelayed(mKeepAliveTask, HEART_BEAT_RATE);
 }
};

為了便于操作演示,在主頁面上加個按鈕 ,點擊按鈕調(diào)用startKeepAlive方法,運行如下所示: 

我們可以看到心跳返回的statue是300 不成功,5秒之后走到了請求超時的方法中,所以如果狀態(tài)返回成功的話,我們需要回調(diào)給調(diào)用者

/**
 * 處理 任務(wù)回調(diào)
 *
 * @param action 請求類型
 */
void disPatchCallbackWarp(String action, boolean isSuccess) {
 CallbackWrapper callBackWarp = callbacks.remove(action);
 if (callBackWarp == null) {
 Logger.d(TAG+" "+ "disPatchCallbackWarp: 任務(wù)隊列為空");
 } else {
 callBackWarp.getScheduledFuture().cancel(true);
 if (isSuccess) {
 callBackWarp.getRequestCallBack().requestSuccess();
 } else {
 callBackWarp.getRequestCallBack().requestFailed("", new Request());
 }
 
 }
}

這樣調(diào)用者才知道成功或失敗。

發(fā)送其他消息與心跳一樣,只是請求參數(shù)不同而已,修改Request參數(shù)即可。這樣我們根據(jù)協(xié)議和業(yè)務(wù)就實現(xiàn)一個比較規(guī)范的webSocket消息推送流程了。

 到此這篇關(guān)于在Android中使用WebSocket實現(xiàn)消息通信的方法詳解的文章就介紹到這了,更多相關(guān)Android使用WebSocket實現(xiàn)消息通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論