WebSocket 安卓客戶端實現及程式碼封裝
WebSocketDemo
WebSocket 安卓客戶端的實現方式。
介紹
如果不想了解其中的原理可以直接拉到最後面的使用方式章節,按照教程使用即可,或者直接開啟 demo 檢視程式碼,程式碼地址:。
https://github.com/0xZhangKe/WebSocketDemo
本文使用一個後臺 Service 來建立 WebSocket 連線,Activity 與 Fragment 需要使用 WebSocket 介面時只需要繫結該服務既可。
WebSocketService 接收到資料之後會通知每一個綁定了 WebSocketService 服務的 Activity 及 Fragment,其自身也可以對返回的資料進行判斷等等,具體如何操作根據業務需求定。
下圖是 WebSocketService 的工作流程圖
WebSocketService 負責建立 WebSocket 連線,接收返回的資料,接收到的資料通過 EventBus 傳送出去,連線失敗之後可以自動重連。
下圖是 Activity 的工作流程圖
Activity/Fragment 繫結 WebSocket 服務,繫結成功後可以直接呼叫 WebSocketService 物件傳送資料。
WebSocketService
新增必要的依賴
首先新增 WebSocket 框架依賴:
compile 'com.neovisionaries:nv-websocket-client:2.3'
這個框架也是我在 Github 上找了一圈之後選中的一個,使用的人很多,文件齊全,還在繼續維護。
另外還要新增一個 EventBus 及阿里的 JSON 框架:
compile 'com.alibaba:fastjson:1.2.33'
compile 'org.greenrobot:eventbus:3.0.0'
好了完事大吉,現在開始吧。
定義 WebSocket 提供的介面
先建立一個 WebSocket 的介面,其中定義了 WebSocket 必須提供的幾個公開方法:
public interface IWebSocket {
/**
* 傳送資料
*
* @param text 需要傳送的資料
*/
void sendText(String text);
/**
* 0-未連線
* 1-正在連線
* 2-已連線
*/
int getConnectStatus();
/**
* 重新連線
*/
void reconnect();
/**
* 關閉連線
*/
void stop();
}
WebSocketService 需要實現這個介面,後面繫結 WebSocketService 時直接通過 IWebSocket 建立物件既可。
AbsWebSocketService
我這裡為了降低程式碼的耦合度,將與業務邏輯相關的程式碼(介面地址、資料處理及分發等)與 WebSocket 的連線、傳送資料等操作剝離開來,所以這裡建立的時一個抽象類 AbsWebSocketService 來實現與業務邏輯無關的程式碼。
在實際使用中只需要建立一個 WebSocketService 並繼承該 AbsWebSocketService 既可,不需要改動其中的程式碼。
首先看一下 AbsWebSocketService 的程式碼:
public abstract class AbsBaseWebSocketService extends Service implements IWebSocket {
private static final String TAG = "AbsBaseWebSocketService";
private static final int TIME_OUT = 15000;
private static WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(TIME_OUT);
private AbsBaseWebSocketService.WebSocketThread webSocketThread;
private WebSocket webSocket;
private AbsBaseWebSocketService.ServiceBinder serviceBinder = new AbsBaseWebSocketService.ServiceBinder();
public class ServiceBinder extends Binder {
public AbsBaseWebSocketService getService() {
return AbsBaseWebSocketService.this;
}
}
private boolean stop = false;
/**
* 0-未連線
* 1-正在連線
* 2-已連線
*/
private int connectStatus = 0;//是否已連線
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
ProxySettings settings = factory.getProxySettings();
settings.addHeader("Content-Type", "text/json");
connectStatus = 0;
webSocketThread = new AbsBaseWebSocketService.WebSocketThread();
webSocketThread.start();
Log.i(TAG, "onCreated");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
if (serviceBinder == null) {
serviceBinder = new AbsBaseWebSocketService.ServiceBinder();
}
Log.i(TAG, "onBind");
return serviceBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
stop = true;
webSocket.disconnect();
webSocket.flush();
webSocket = null;
connectStatus = 0;
Log.i(TAG, "onDestroy");
}
/**
* 獲取伺服器地址
*/
protected abstract String getConnectUrl();
/**
* 分發響應資料
*/
protected abstract void dispatchResponse(String textResponse);
/**
* 連線成功傳送 WebSocketConnectedEvent 事件,
* 請求成功傳送 CommonResponse 事件,
* 請求失敗傳送 WebSocketSendDataErrorEvent 事件。
*/
private class WebSocketThread extends Thread {
@Override
public void run() {
Log.i(TAG, "WebSocketThread->run()");
setupWebSocket();
}
}
private void setupWebSocket() {
if (connectStatus != 0) return;
connectStatus = 1;
try {
webSocket = factory.createSocket(getConnectUrl());
webSocket.addListener(new WebSocketAdapter() {
@Override
public void onTextMessage(WebSocket websocket, String text) throws Exception {
super.onTextMessage(websocket, text);
if (debug()) {
Log.i(TAG, String.format("onTextMessage->%s", text));
}
dispatchResponse(text);
}
@Override
public void onTextMessageError(WebSocket websocket, WebSocketException cause, byte[] data) throws Exception {
super.onTextMessageError(websocket, cause, data);
Log.e(TAG, "onTextMessageError()", cause);
EventBus.getDefault().post(new WebSocketSendDataErrorEvent("", "", "onTextMessageError():" + cause.toString()));
}
@Override
public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception {
super.onDisconnected(websocket, serverCloseFrame, clientCloseFrame, closedByServer);
EventBus.getDefault().post(new DisconnectedEvent());
Log.e(TAG, "onDisconnected()");
connectStatus = 0;
if (!stop) {
//斷開之後自動重連
setupWebSocket();
}
}
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
super.onConnected(websocket, headers);
Log.i(TAG, "onConnected()");
connectStatus = 2;
EventBus.getDefault().post(new WebSocketConnectedEvent());
}
@Override
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
super.onError(websocket, cause);
Log.e(TAG, "onError()", cause);
EventBus.getDefault().post(new WebSocketConnectionErrorEvent("onError:" + cause.getMessage()));
}
});
try {
webSocket.connect();
} catch (NullPointerException e) {
connectStatus = 0;
Log.i(TAG, String.format("NullPointerException()->%s", e.getMessage()));
Log.e(TAG, "NullPointerException()", e);
EventBus.getDefault().post(new WebSocketConnectionErrorEvent("NullPointerException:" + e.getMessage()));
} catch (OpeningHandshakeException e) {
connectStatus = 0;
Log.i(TAG, String.format("OpeningHandshakeException()->%s", e.getMessage()));
Log.e(TAG, "OpeningHandshakeException()", e);
StatusLine sl = e.getStatusLine();
Log.i(TAG, "=== Status Line ===");
Log.e(TAG, "=== Status Line ===");
Log.i(TAG, String.format("HTTP Version = %s\n", sl.getHttpVersion()));
Log.e(TAG, String.format("HTTP Version = %s\n", sl.getHttpVersion()));
Log.i(TAG, String.format("Status Code = %s\n", sl.getStatusCode()));
Log.e(TAG, String.format("Status Code = %s\n", sl.getStatusCode()));
Log.i(TAG, String.format("Reason Phrase = %s\n", sl.getReasonPhrase()));
Log.e(TAG, String.format("Reason Phrase = %s\n", sl.getReasonPhrase()));
Map<String, List<String>> headers = e.getHeaders();
Log.i(TAG, "=== HTTP Headers ===");
Log.e(TAG, "=== HTTP Headers ===");
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
// Header name.
String name = entry.getKey();
// Values of the header.
List<String> values = entry.getValue();
if (values == null || values.size() == 0) {
// Print the name only.
System.out.println(name);
continue;
}
for (String value : values) {
// Print the name and the value.
Log.e(TAG, String.format("%s: %s\n", name, value));
Log.i(TAG, String.format("%s: %s\n", name, value));
}
}
EventBus.getDefault().post(new WebSocketConnectionErrorEvent("OpeningHandshakeException:" + e.getMessage()));
} catch (HostnameUnverifiedException e) {
connectStatus = 0;
// The certificate of the peer does not match the expected hostname.
Log.i(TAG, String.format("HostnameUnverifiedException()->%s", e.getMessage()));
Log.e(TAG, "HostnameUnverifiedException()", e);
EventBus.getDefault().post(new WebSocketConnectionErrorEvent("HostnameUnverifiedException:" + e.getMessage()));
} catch (WebSocketException e) {
connectStatus = 0;
// Failed to establish a WebSocket connection.
Log.i(TAG, String.format("WebSocketException()->%s", e.getMessage()));
Log.e(TAG, "WebSocketException()", e);
EventBus.getDefault().post(new WebSocketConnectionErrorEvent("WebSocketException:" + e.getMessage()));
}
} catch (IOException e) {
connectStatus = 0;
Log.i(TAG, String.format("IOException()->%s", e.getMessage()));
Log.e(TAG, "IOException()", e);
EventBus.getDefault().post(new WebSocketConnectionErrorEvent("IOException:" + e.getMessage()));
}
}
@Override
public void sendText(String text) {
if (TextUtils.isEmpty(text)) return;
if (debug()) {
Log.i(TAG, String.format("sendText()->%s", text));
}
if (webSocket != null && connectStatus == 2) {
webSocket.sendText(text);
}
}
@Override
public int getConnectStatus() {
return connectStatus;
}
@Override
public void reconnect() {
Log.i(TAG, "reconnect()");
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "reconnect()->begin restart...");
try {
Thread.sleep(200);
}catch(Exception e){
Log.e(TAG, "reconnect()->run: ", e);
}
if (webSocketThread != null && !webSocketThread.isAlive()) {
connectStatus = 0;
webSocketThread = new WebSocketThread();
webSocketThread.start();
Log.i(TAG, "reconnect()->start success");
} else {
Log.i(TAG, "reconnect()->start failed: webSocketThread==null || webSocketThread.isAlive()");
}
}
}).start();
}
@Override
public void stop() {
Log.i(TAG, "stop()");
webSocket.disconnect();
stop = true;
Log.i(TAG, "stop()->success");
}
public boolean debug() {
try {
ApplicationInfo info = getApplication().getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}
其中有兩個抽象方法:
String getConnectUrl()//獲取伺服器連線地址
void dispatchResponse(String textResponse)//接收到資料後回撥此方法,在此方法中分發資料
建立好上面的 AbsWebSocketService 服務之後,還需要根據業務需求建立一個 WebSocketService 實現該類。
WebSocketService 服務
這個程式碼就很簡單了,如下:
public class WebSocketService extends AbsBaseWebSocketService {
@Override
protected String getConnectUrl() {
return "伺服器對應的url";
}
@Override
protected void dispatchResponse(String textResponse) {
//處理資料
try {
CommonResponse<String> response = JSON.parseObject(textResponse, new TypeReference<CommonResponse<String>>() {
});
if (response == null) {
EventBus.getDefault().post(new WebSocketSendDataErrorEvent("", textResponse, "響應資料為空"));
return;
}
//此處可根據伺服器介面文件進行調整,判斷 code 值是否合法,如下:
// if (response.getCode() >= 1000 && response.getCode() < 2000) {
// EventBus.getDefault().post(response);
// }else{
// EventBus.getDefault().post(new WebSocketSendDataErrorEvent(response.getCommand().getPath(), textResponse, response.getMsg()));
// }
EventBus.getDefault().post(response);
}catch(Exception e){
//一般由於 JSON 解析時出現異常
EventBus.getDefault().post(new WebSocketSendDataErrorEvent("", textResponse, "資料異常:" + e.getMessage()));
}
}
}
dispatchResponse(String) 方法中就是將資料轉換成對應的實體,然後使用 EventBus 將其傳送出去,可以再其中做一些資料正確的判斷,比如上面註釋的地方。
其中的 CommonResponse 是我們後臺介面的一個標準模板,所有格介面返回的資料都應該按照這個格式來,這個類就按照自家的介面寫就行了,不用按照我的。看一下其中的程式碼:
public class CommonResponse<T> {
private String msg;
private T data;
private int code;
private String path;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
其中 path 表示介面地址,其本質就是個字串,我們通過這個字串當做一個識別符號,標識返回的資料屬於哪個介面,然後我們才能做出對應的操作。
泛型 T 表示資料的實體,一般來說我們會按照不同的介面寫出不同的實體方便使用,當然了,這些都不重要,也只是我的個人習慣,這裡也不涉及核心程式碼,所以可以根據個人愛好隨意改動。
別忘了在 AndroidManifest 中註冊該服務,然後在合適的時候啟動該服務,我的是在 Application 中的 onCreate 方法啟動的:
public class GateApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, WebSocketService.class);
startService(intent);
}
}
到了這裡 WebSocket 服務就已經介紹完了,但是我們如果直接這麼用肯定很麻煩。
比如當呼叫 WebSocketService.sendText(String) 方法時發現 WebSocket 連線已經斷了、繫結 WebSocket 服務、判斷其連線狀態等等,還有很多事情要做,總不能每個 Activity/Fragment 都要寫這麼多程式碼去判斷吧。
為此我又寫了 AbsBaseWebSocketActivity 與 AbsBaseWebSocketFragment 兩個抽象類,其中遮蔽掉了大部分的連線狀態判斷等等操作。
比如我們呼叫 AbsBaseWebSocketFragment.sendText(String) 方法時,可以直接判斷出當前時候是連線狀態,如果未連線則重新連線,連線完成後再去傳送資料。
先來看一下 ABSBaseWebSocketActivity 的程式碼:
AbsBaseWebSocketActivity
其中主要包括繫結服務,判斷連線狀態,傳送資料等操作,另外暴露出了幾個方法以供使用:
public abstract class AbsBaseWebSocketActivity extends BaseAppCompatActivity {
/**
* 服務重連次數,
* 這裡指的是繫結 WebSocket 服務失敗時使用的重連次數,一般來說不會出現繫結失敗的情況
*/
private final int RECONNECT_TIME = 5;
private IWebSocket mWebSocketService;
protected String networkErrorTips;
/**
* 連線時機:</br>
* 0 - 剛進入介面時,如果 WebSocket 還未連線,會繼續連線,或者由於某些原因 WebSocket 斷開,會自動重連,從而會觸發連線成功/失敗事件;</br>
* 1 - onResume() 方法回撥時判斷 WebSocket 是否連線,如果未連線,則進行連線,從而觸發連線成功/失敗事件;</br>
* 2 - sendText() 方法會判斷 WebSocket 是否已經連線,如果未連線,則進行連線,從而觸發連線成功/失敗事件,此時連線成功後應繼續呼叫 sendText() 方法傳送資料。</br>
* <p>
* 另外,當 connectType != 0 時,每次使用完之後應該設定為 0。因為 0 的狀態是無法預知的,隨時可能呼叫。
*/
private int connectType = 0;
/**
* 需要傳送的資料,當 connectType == 2 時會使用。
*/
private String needSendText;
private boolean isConnected = false;
private boolean networkReceiverIsRegister = false;
private int connectTime = 0;
protected ServiceConnection mWebSocketServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected()");
mWebSocketService = (IWebSocket) ((AbsBaseWebSocketService.ServiceBinder) service).getService();
//此處假設要不就已經連線,要不就未連線,未連線就等著接收連線成功/失敗的廣播即可
if (mWebSocketService.getConnectStatus() == 2) {
Log.i(TAG, "onServiceConnected()->mWebSocketService.getConnectStatus() == 2; BindSuccess");
onServiceBindSuccess();
} else {
Log.i(TAG, String.format("onServiceConnected()->mWebSocketService.getConnectStatus() == %s", mWebSocketService.getConnectStatus()));
if (mWebSocketService.getConnectStatus() == 0) {
Log.i(TAG, "onServiceConnected()->mWebSocketService.getConnectStatus() == 0; mWebSocketService.restartThread()");
mWebSocketService.reconnect();
}
showRoundProgressDialog();
}
}
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected()");
mWebSocketService = null;
if (connectTime <= RECONNECT_TIME) {
Log.i(TAG, "onServiceDisconnected()->retry bindWebSocketService()");
bindWebSocketService();
}
}
};
@Override
protected void initBind() {
super.initBind();
networkErrorTips = "網路錯誤";
EventBus.getDefault().register(this);
bindWebSocketService();
}
/**
* 從後臺返回時,判斷服務是否已斷開,
* 斷開則呼叫 reconnect 方法重連。
*/
@Override
protected void onResume() {
super.onResume();
if (mWebSocketService != null)
Log.e(TAG, "-----------------ConnectStatus" + mWebSocketService.getConnectStatus());
if (mWebSocketService != null && mWebSocketService.getConnectStatus() != 2) {
Log.i(TAG, "onResume()->WebSocket 未連線");
showRoundProgressDialog();
if (mWebSocketService.getConnectStatus() == 0) {
Log.i(TAG, "onResume()->WebSocket 嘗試重新連線 restartThread()");
mWebSocketService.reconnect();
}else{
Log.i(TAG, "onResume()->WebSocket 正在連線");
}
connectType = 1;
}
}
protected abstract Class<? extends AbsBaseWebSocketService> getWebSocketClass();
/**
* 繫結服務,
* 進入該介面時繫結服務,
* 繫結失敗則繼續繫結,知道超過設定的次數為止。
*/
protected void bindWebSocketService() {
Intent intent = new Intent(this, getWebSocketClass());
bindService(intent, mWebSocketServiceConnection, Context.BIND_AUTO_CREATE);
connectTime++;
Log.i(TAG, "bindWebSocketService() success");
}
protected abstract void onCommonResponse(CommonResponse<String> response);
protected abstract void onErrorResponse(WebSocketSendDataErrorEvent response);
/**
* 連線失敗
*/
protected void onConnectFailed() {
Log.i(TAG, "onConnectFailed()");
}
protected IWebSocket getWebSocketService() {
return mWebSocketService;
}
/**
* 服務繫結成功後回撥改方法,可以在此方法中載入一些初始化資料
*/
protected void onServiceBindSuccess() {
Log.i(TAG, "onServiceBindSuccess()");
}
/**
* 傳送資料
*/
protected void sendText(String text) {
if (mWebSocketService.getConnectStatus() == 2) {
Log.i(TAG, "sendText()->已連線,直接傳送資料");
//已連線,直接傳送資料
mWebSocketService.sendText(text);
} else {
//未連線,先連線,再發送資料
Log.i(TAG, "sendText()->未連線");
connectType = 2;
needSendText = text;
if (mWebSocketService.getConnectStatus() == 0) {
Log.i(TAG, "sendText()->建立連線");
mWebSocketService.reconnect();
}
}
}
/**
* 傳送資料失敗或者資料返回不合規
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(CommonResponse<String> event) {
onCommonResponse(event);
}
/**
* 傳送資料失敗或者資料返回不合規(code >=2000等)
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(WebSocketSendDataErrorEvent event) {
Log.e(TAG, String.format("onEventMainThread(WebSocketSendDataErrorEvent)->%s", event.toString()));
onErrorResponse(event);
}
/**
* 連線成功
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(WebSocketConnectedEvent event) {
isConnected = true;
if (connectType == 2 && !TextUtils.isEmpty(needSendText)) {
Log.i(TAG, "onEventMainThread(WebSocketConnectedEvent) -> sendText()");
sendText(needSendText);
} else if (connectType == 0) {
Log.i(TAG, "onEventMainThread(WebSocketConnectedEvent) -> onServiceBindSuccess()");
closeRoundProgressDialog();
onServiceBindSuccess();
}
connectType = 0;
}
/**
* 連線失敗
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(WebSocketConnectionErrorEvent event) {
Log.e(TAG, String.format("onEventMainThread(WebSocketConnectionErrorEvent)->onConnectFailed:%s", event.toString()));
closeRoundProgressDialog();
showToastMessage(networkErrorTips);
connectType = 0;
onConnectFailed();
}
@Override
protected void onDestroy() {
unbindService(mWebSocketServiceConnection);
EventBus.getDefault().unregister(this);
super.onDestroy();
}
}
看一下其中的抽象方法:
Class<? extends AbsBaseWebSocketService> getWebSocketClass();//獲取 WebSocketService 類,這裡傳入 WebSocketService.class 既可
void onCommonResponse(CommonResponse<String> response);//當有接收到資料時會回撥此方法
void onErrorResponse(WebSocketSendDataErrorEvent response);//當有傳送資料失敗時會回撥此方法
這樣一個 WebSocket 的功能就已經實現了,現在來說一下怎麼使用。
使用方式
直接使需要的 Activity 繼承 ABSBaseWebSocketActivity,呼叫 sendText(String) 方法既可傳送資料,接收到資料後會回撥 onCommonResponse(CommonResponse) 方法或 onErrorResponse(WebSocketSendDataErrorEvent) 方法。
下面用一個使用案例更直觀一點:
假設現在要在 LoginActivity 中實現登陸功能,首先建立 LoginActivity,並初始化控制元件:
public class LoginActivity extends AbsBaseWebSocketActivity {
private EditText etAccount;
private EditText etPassword;
private Button btnLogin;
@Override
protected int getLayoutResId() {
return R.layout.activity_login;
}
@Override
protected void initView() {
etAccount = (EditText) findViewById(R.id.et_account);
etPassword = (EditText) findViewById(R.id.et_password);
btnLogin = (Button) findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account = etAccount.getText().toString();
String password = etPassword.getText().toString();
if(TextUtils.isEmpty(account) || TextUtils.isEmpty(password)){
showToastMessage("輸入不能為空");
return;
}
login(account, password);
}
});
}
private void login(String account, String password){
}
@Override
protected Class<? extends AbsBaseWebSocketService> getWebSocketClass() {
return WebSocketService.class;
}
@Override
protected void onCommonResponse(CommonResponse<String> response) {
}
@Override
protected void onErrorResponse(WebSocketSendDataErrorEvent response) {
}
}
上面的程式碼就是很簡單的初始化控制元件,監聽按鍵輸入。
其中的 login(String, String) 方法是空的,現在我們來完成 login 方法:
private void login(String account, String password){
JSONObject param = new JSONObject();
param.put("account", account);
param.put("password", password);
param.put("path", LOGIN_PATH);
sendText(param.toString());//呼叫 WebSocket 傳送資料
showRoundProgressDialog();//顯示載入對話方塊
}
以及獲取返回資料:
/**
* 登陸成功
*/
@Override
protected void onCommonResponse(CommonResponse<String> response) {
closeRoundProgressDialog();//關閉載入對話方塊
showToastMessage("登陸成功");
}
/**
* 呼叫接口出錯或介面提示錯誤
*/
@Override
protected void onErrorResponse(WebSocketSendDataErrorEvent response) {
closeRoundProgressDialog();//關閉載入對話方塊
showToastMessage(String.format("登陸失敗:%s", response));
}
下面來看一下完整的 LoginActivity 程式碼:
public class LoginActivity extends AbsBaseWebSocketActivity {
/**
* 假設這是登陸的介面Path
*/
private static final String LOGIN_PATH = "path_login";
private EditText etAccount;
private EditText etPassword;
private Button btnLogin;
@Override
protected int getLayoutResId() {
return R.layout.activity_login;
}
@Override
protected void initView() {
etAccount = (EditText) findViewById(R.id.et_account);
etPassword = (EditText) findViewById(R.id.et_password);
btnLogin = (Button) findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account = etAccount.getText().toString();
String password = etPassword.getText().toString();
if(TextUtils.isEmpty(account) || TextUtils.isEmpty(password)){
showToastMessage("輸入不能為空");
return;
}
login(account, password);
}
});
}
private void login(String account, String password){
JSONObject param = new JSONObject();
param.put("account", account);
param.put("password", password);
param.put("path", LOGIN_PATH);
sendText(param.toString());//呼叫 WebSocket 傳送資料
showRoundProgressDialog();//顯示載入對話方塊
}
/**
* 登陸成功
*/
@Override
protected void onCommonResponse(CommonResponse<String> response) {
if (response != null && !TextUtils.isEmpty(response.getPath()) && TextUtils.equals(LOGIN_PATH, response.getPath())) {
//我們需要通過 path 判斷是不是登陸介面返回的資料,因為也有可能是其他介面返回的
closeRoundProgressDialog();//關閉載入對話方塊
showToastMessage("登陸成功");
}
}
/**
* 呼叫接口出錯或介面提示錯誤
*/
@Override
protected void onErrorResponse(WebSocketSendDataErrorEvent response) {
closeRoundProgressDialog();//關閉載入對話方塊
showToastMessage(String.format("登陸失敗:%s", response));
}
@Override
protected Class<? extends AbsBaseWebSocketService> getWebSocketClass() {
return WebSocketService.class;//這裡傳入 WebSocketService 既可
}
}
按照上面所示就可以完成一次 WebSocket 的介面呼叫。
另外還有一點需要注意的,考慮這樣的一種情況,比如我們在開啟登陸介面時需要初始化一些資料,如果是 HTTP 介面我們可以直接在 onCreate 方法中獲取資料就行了,但是使用 WebSocket 就沒辦法在 onCreate 去呼叫,因為開啟一個新的 Activity 時我們需要先繫結 WebSocketService 服務,我們得在繫結完成後才能呼叫 WebSocket 介面。
ABSBaseWebSocketActivity 中提供了一個 onServiceBindSuccess() 方法,這個方法就是繫結成功後的回撥方法,我們可以再這個方法中初始化一些資料。
PS:我們可以在建立一個 BaseWebSocketServiceActivity 抽象類,實現其中的 Class