1. 程式人生 > >WebSocket 安卓客戶端實現及程式碼封裝

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